Merge branch 'PaperMC:main' into add-effectiveName

This commit is contained in:
masmc05 2024-12-23 00:23:37 +02:00 committed by GitHub
commit 791b26c200
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
1522 changed files with 58178 additions and 70227 deletions

View file

@ -32,10 +32,7 @@ ij_java_generate_final_parameters = true
ij_java_method_parameters_new_line_after_left_paren = true
ij_java_method_parameters_right_paren_on_new_line = true
[test-plugin/**/*.java]
ij_java_use_fq_class_names = false
[Paper-Server/src/main/resources/data/**/*.json]
[paper-server/src/minecraft/resources/data/**/*.json]
indent_size = 2
[paper-api-generator/generated/**/*.java]

View file

@ -102,7 +102,7 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: paper-${{ fromJSON(steps.determine.outputs.result).pr }}
path: build/libs/paper-paperclip-*-mojmap.jar
path: paper-server/build/libs/paper-paperclip-*-mojmap.jar
event_file:
name: "Event File"
# Only run on PRs if the source branch is on someone else's repo

View file

@ -9,19 +9,19 @@ jobs:
if: |
github.repository != github.event.pull_request.head.repo.full_name &&
(
github.head_ref == 'master' ||
github.head_ref == 'main' ||
github.event.pull_request.head.repo.owner.type != 'User'
)
runs-on: ubuntu-latest
steps:
- uses: superbrothers/close-pull-request@v3
id: "master_branch"
if: github.head_ref == 'master'
id: "main_branch"
if: github.head_ref == 'main'
with:
comment: "Please do not open pull requests from the `master` branch, create a new branch instead."
comment: "Please do not open pull requests from the `main` branch, create a new branch instead."
- uses: superbrothers/close-pull-request@v3
id: "org_account"
if: github.event.pull_request.head.repo.owner.type != 'User' && steps.master_branch.outcome == 'skipped'
if: github.event.pull_request.head.repo.owner.type != 'User' && steps.main_branch.outcome == 'skipped'
with:
comment: "Please do not open pull requests from non-user accounts like organizations. Create a fork on a user account instead."

2
.gitignore vendored
View file

@ -48,4 +48,4 @@ test-plugin.settings.gradle.kts
paper-api-generator.settings.gradle.kts
# Don't track patched vanilla submodules
paper-server/src/vanilla/
paper-server/src/minecraft/

View file

@ -66,16 +66,16 @@ Assuming you have already forked the repository:
2. Type `./gradlew applyPatches` in a terminal to apply the changes from upstream.
On Windows, replace the `./` with `.\` at the beginning for all `gradlew` commands;
3. cd into `paper-server` for server changes, and `paper-api` for API changes.
**Only changes made in `paper-server/src/vanilla` have to deal with the patch system.**
**Only changes made in `paper-server/src/minecraft` have to deal with the patch system.**
`paper-server/src/vanilla` is not a git repositories in the traditional sense. Its
initial commits are the decompiled and deobfuscated Vanilla source files. The per-file
`paper-server/src/minecraft` is not a git repositories in the traditional sense. Its
initial commits are the decompiled and deobfuscated Minecraft source files. The per-file
patches are applied on top of these files as a single, large commit, which is then followed
by the individual feature-patch commits.
### Modifying (per-file) Vanilla patches
### Modifying (per-file) Minecraft patches
This is generally what you need to do when editing Vanilla files. Updating our
This is generally what you need to do when editing Minecraft files. Updating our
per-file patches is as easy as making your changes and then running
# TODO
in the root directory. If nothing went wrong, you can rebuild patches with
@ -169,13 +169,14 @@ move it under the line of the patch you wish to modify;
1. Make your change while at HEAD;
1. Make a fixup commit. `git commit -a --fixup <hashOfPatchToFix>`;
- If you want to modify a per-file patch, use `git commit -a --fixup file`
- You can also use `--squash` instead of `--fixup` if you want the commit
message to also be changed.
- You can get the hash by looking at `git log` or `git blame`; your IDE can
assist you too.
- Alternatively, if you only know the name of the patch, you can do
`git commit -a --fixup "Subject of Patch name"`.
1. Rebase with autosquash: `git rebase -i --autosquash base`.
1. Rebase with autosquash: `git rebase -i --autosquash mache/main`.
This will automatically move your fixup commit to the right place, and you just
need to "save" the changes.
1. Type `./gradlew rebuildPatches` in the root directory;
@ -184,11 +185,11 @@ need to "save" the changes.
## Rebasing PRs
Steps to rebase a PR to include the latest changes from `master`.
Steps to rebase a PR to include the latest changes from `main`.
These steps assume the `origin` remote is your fork of this repository and `upstream` is the official PaperMC repository.
1. Pull the latest changes from upstreams master: `git switch main && git pull upstream main`.
1. Checkout feature/fix branch and rebase on master: `git checkout patch-branch && git rebase main`.
1. Pull the latest changes from upstreams main: `git switch main && git pull upstream main`.
1. Checkout feature/fix branch and rebase on main: `git checkout patch-branch && git rebase main`.
1. Apply updated patches: `./gradlew applyPatches`.
1. If there are conflicts, fix them.
1. If your PR creates new feature patches instead of modifying existing ones, ensure your newly-created patch is the last commit by either:

View file

@ -1,4 +1,4 @@
Paper [![Paper Build Status](https://img.shields.io/github/actions/workflow/status/PaperMC/Paper/build.yml?branch=master)](https://github.com/PaperMC/Paper/actions)
Paper [![Paper Build Status](https://img.shields.io/github/actions/workflow/status/PaperMC/Paper/build.yml?branch=main)](https://github.com/PaperMC/Paper/actions)
[![Discord](https://img.shields.io/discord/289587909051416579.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/papermc)
[![GitHub Sponsors](https://img.shields.io/github/sponsors/papermc?label=GitHub%20Sponsors)](https://github.com/sponsors/PaperMC)
[![Open Collective](https://img.shields.io/opencollective/all/papermc?label=OpenCollective%20Sponsors)](https://opencollective.com/papermc)
@ -65,7 +65,7 @@ How To (Compiling Jar From Source)
------
To compile Paper, you need JDK 21 and an internet connection.
Clone this repo, run `./gradlew applyPatches`, then `./gradlew createMojmapBundlerJar` from your terminal. You can find the compiled jar in the project root's `build/libs` directory.
Clone this repo, run `./gradlew applyPatches`, then `./gradlew createMojmapBundlerJar` from your terminal. You can find the compiled jar in the `paper-server/build/libs` directory.
To get a full list of tasks, run `./gradlew tasks`.
@ -73,6 +73,10 @@ How To (Pull Request)
------
See [Contributing](CONTRIBUTING.md)
Old Versions (1.21.3 and below)
------
For branches of versions 1.8-1.21.3, please see our [archive repository](https://github.com/PaperMC/Paper-archive).
Support Us
------
First of all, thank you for considering helping out, we really appreciate that!

View file

@ -12,4 +12,3 @@
# mc_data chat_type/chat.json
# mc_data dimension_type/overworld.json
#

View file

@ -1,18 +1,735 @@
# You can use this file to change the access modifiers on a member
# This line would make the field rollAmount public in Bee
#public net.minecraft.world.entity.animal.Bee rollAmount
# This line would make the field public and remove the final modifier
#public-f net.minecraft.network.protocol.game.ClientboundChatPacket sender
# Leave out the member and it will apply to the class itself
# More info, see here https://mcforge.readthedocs.io/en/latest/advanced/accesstransformers/#access-modifiers
# Remap/Decompile fix (unclear why this is happening)
public net.minecraft.server.MinecraftServer doRunTask(Lnet/minecraft/server/TickTask;)V
# AT remap issue? todo 1.18
public net.minecraft.world.level.dimension.end.EndDragonFight findExitPortal()Lnet/minecraft/world/level/block/state/pattern/BlockPattern$BlockPatternMatch;
# This file is auto generated, any changes may be overridden!
# See CONTRIBUTING.md on how to add access transformers.
private-f net.minecraft.network.CompressionDecoder inflater
private-f net.minecraft.server.MinecraftServer connection
private-f net.minecraft.server.MinecraftServer levels
private-f net.minecraft.world.item.ItemStack components
private-f net.minecraft.world.item.ItemStack item
private-f net.minecraft.world.level.block.entity.BeehiveBlockEntity stored
protected net.minecraft.world.entity.LivingEntity skipDropExperience
protected-f net.minecraft.server.MinecraftServer worldData
public net.minecraft.ChatFormatting code
public net.minecraft.Util onThreadException(Ljava/lang/Thread;Ljava/lang/Throwable;)V
public net.minecraft.advancements.Advancement decorateName(Lnet/minecraft/advancements/DisplayInfo;)Lnet/minecraft/network/chat/Component;
public net.minecraft.commands.CommandSourceStack source
public net.minecraft.commands.arguments.DimensionArgument ERROR_INVALID_VALUE
public net.minecraft.commands.arguments.blocks.BlockInput tag
public net.minecraft.core.MappedRegistry validateWrite(Lnet/minecraft/resources/ResourceKey;)V
public net.minecraft.nbt.ListTag <init>(Ljava/util/List;B)V
public net.minecraft.nbt.TagParser readArrayTag()Lnet/minecraft/nbt/Tag;
# TODO 1.20 remapSpigotAt.at doesn't remap the return type for this method for some reason
public net/minecraft/world/entity/Display$TextDisplay getText()Lnet/minecraft/network/chat/Component;
public net/minecraft/world/entity/Display$BlockDisplay getBlockState()Lnet/minecraft/world/level/block/state/BlockState;
public net.minecraft.nbt.TagParser type(Ljava/lang/String;)Lnet/minecraft/nbt/Tag;
public net.minecraft.network.Connection address
public net.minecraft.network.Connection channel
public net.minecraft.network.chat.HoverEvent$ItemStackInfo components
public net.minecraft.network.chat.HoverEvent$ItemStackInfo count
public net.minecraft.network.chat.HoverEvent$ItemStackInfo item
public net.minecraft.network.chat.TextColor name
public net.minecraft.network.chat.contents.TranslatableContents filterAllowedArguments(Ljava/lang/Object;)Lcom/mojang/serialization/DataResult;
public net.minecraft.network.chat.numbers.FixedFormat value
public net.minecraft.network.chat.numbers.StyledFormat style
public net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket <init>(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/entity/BlockEntityType;Lnet/minecraft/nbt/CompoundTag;)V
public net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket blockState
public net.minecraft.network.protocol.game.ClientboundSetDefaultSpawnPositionPacket pos
public net.minecraft.network.protocol.game.ServerboundMovePlayerPacket hasPos
public net.minecraft.network.protocol.game.ServerboundMovePlayerPacket hasRot
public net.minecraft.network.protocol.game.ServerboundMovePlayerPacket x
public net.minecraft.network.protocol.game.ServerboundMovePlayerPacket xRot
public net.minecraft.network.protocol.game.ServerboundMovePlayerPacket y
public net.minecraft.network.protocol.game.ServerboundMovePlayerPacket yRot
public net.minecraft.network.protocol.game.ServerboundMovePlayerPacket z
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;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
public net.minecraft.server.MinecraftServer fixerUpper
public net.minecraft.server.MinecraftServer playerDataStorage
public net.minecraft.server.MinecraftServer prepareLevels(Lnet/minecraft/server/level/progress/ChunkProgressListener;)V
public net.minecraft.server.MinecraftServer progressListenerFactory
public net.minecraft.server.MinecraftServer resources
public net.minecraft.server.MinecraftServer serverThread
public net.minecraft.server.MinecraftServer$ReloadableResources
public net.minecraft.server.RegistryLayer STATIC_ACCESS
public net.minecraft.server.ReloadableServerResources
public net.minecraft.server.ServerAdvancementManager advancements
public net.minecraft.server.dedicated.DedicatedServerProperties$WorldDimensionData
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
public net.minecraft.server.level.ChunkHolder playerProvider
public net.minecraft.server.level.ChunkLevel ENTITY_TICKING_LEVEL
public net.minecraft.server.level.ChunkMap addEntity(Lnet/minecraft/world/entity/Entity;)V
public net.minecraft.server.level.ChunkMap anyPlayerCloseEnoughForSpawning(Lnet/minecraft/world/level/ChunkPos;)Z
public net.minecraft.server.level.ChunkMap distanceManager
public net.minecraft.server.level.ChunkMap entityMap
public net.minecraft.server.level.ChunkMap getVisibleChunkIfPresent(J)Lnet/minecraft/server/level/ChunkHolder;
public net.minecraft.server.level.ChunkMap level
public net.minecraft.server.level.ChunkMap progressListener
public net.minecraft.server.level.ChunkMap save(Lnet/minecraft/world/level/chunk/ChunkAccess;)Z
public net.minecraft.server.level.ChunkMap serverViewDistance
public net.minecraft.server.level.ChunkMap setServerViewDistance(I)V
public net.minecraft.server.level.ChunkMap toDrop
public net.minecraft.server.level.ChunkMap updatingChunkMap
public net.minecraft.server.level.ChunkMap visibleChunkMap
public net.minecraft.server.level.ChunkMap$TrackedEntity
public net.minecraft.server.level.ChunkMap$TrackedEntity seenBy
public net.minecraft.server.level.ChunkMap$TrackedEntity serverEntity
public net.minecraft.server.level.DistanceManager simulationDistance
public net.minecraft.server.level.DistanceManager tickets
public net.minecraft.server.level.ServerBossEvent broadcast(Ljava/util/function/Function;)V
public net.minecraft.server.level.ServerBossEvent visible
public net.minecraft.server.level.ServerChunkCache mainThread
public net.minecraft.server.level.ServerChunkCache mainThreadProcessor
public net.minecraft.server.level.ServerChunkCache spawnEnemies
public net.minecraft.server.level.ServerChunkCache spawnFriendlies
public net.minecraft.server.level.ServerChunkCache$MainThreadExecutor
public net.minecraft.server.level.ServerLevel chunkSource
public net.minecraft.server.level.ServerLevel entityManager
public net.minecraft.server.level.ServerLevel findLightningRod(Lnet/minecraft/core/BlockPos;)Ljava/util/Optional;
public net.minecraft.server.level.ServerLevel getEntities()Lnet/minecraft/world/level/entity/LevelEntityGetter;
public net.minecraft.server.level.ServerLevel serverLevelData
public net.minecraft.server.level.ServerPlayer completeUsingItem()V
public net.minecraft.server.level.ServerPlayer containerSynchronizer
public net.minecraft.server.level.ServerPlayer findRespawnAndUseSpawnBlock(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/core/BlockPos;FZZ)Ljava/util/Optional;
public net.minecraft.server.level.ServerPlayer initMenu(Lnet/minecraft/world/inventory/AbstractContainerMenu;)V
public net.minecraft.server.level.ServerPlayer isChangingDimension
public net.minecraft.server.level.ServerPlayer language
public net.minecraft.server.level.ServerPlayer lastSentExp
public net.minecraft.server.level.ServerPlayer nextContainerCounter()V
public net.minecraft.server.level.ServerPlayer particleStatus
public net.minecraft.server.level.ServerPlayer seenCredits
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.ServerPlayerGameMode level
public net.minecraft.server.level.Ticket key
public net.minecraft.server.network.ServerGamePacketListenerImpl isChatMessageIllegal(Ljava/lang/String;)Z
public net.minecraft.server.network.ServerLoginPacketListenerImpl authenticatedProfile
public net.minecraft.server.network.ServerLoginPacketListenerImpl connection
public net.minecraft.server.network.ServerLoginPacketListenerImpl state
public net.minecraft.server.network.ServerLoginPacketListenerImpl$State
public net.minecraft.server.packs.VanillaPackResourcesBuilder safeGetPath(Ljava/net/URI;)Ljava/nio/file/Path;
public net.minecraft.server.packs.repository.Pack resources
public net.minecraft.server.players.PlayerList playerIo
public net.minecraft.server.players.PlayerList players
public net.minecraft.server.players.PlayerList updateEntireScoreboard(Lnet/minecraft/server/ServerScoreboard;Lnet/minecraft/server/level/ServerPlayer;)V
public net.minecraft.server.players.StoredUserEntry getUser()Ljava/lang/Object;
public net.minecraft.stats.ServerRecipeBook known
public net.minecraft.tags.TagEntry id
public net.minecraft.tags.TagEntry required
public net.minecraft.tags.TagEntry tag
public net.minecraft.util.datafix.fixes.BlockStateData register(ILjava/lang/String;[Ljava/lang/String;)V
public net.minecraft.util.datafix.fixes.ItemIdFix ITEM_NAMES
public net.minecraft.util.datafix.fixes.ItemSpawnEggFix ID_TO_ENTITY
public net.minecraft.world.BossEvent color
public net.minecraft.world.BossEvent name
public net.minecraft.world.BossEvent overlay
public net.minecraft.world.CompoundContainer container1
public net.minecraft.world.CompoundContainer container2
public net.minecraft.world.SimpleContainer items
public net.minecraft.world.damagesource.DamageSource <init>(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.MobEffectInstance hiddenEffect
public net.minecraft.world.entity.AreaEffectCloud durationOnUse
public net.minecraft.world.entity.AreaEffectCloud ownerUUID
public net.minecraft.world.entity.AreaEffectCloud potionContents
public net.minecraft.world.entity.AreaEffectCloud radiusOnUse
public net.minecraft.world.entity.AreaEffectCloud radiusPerTick
public net.minecraft.world.entity.AreaEffectCloud reapplicationDelay
public net.minecraft.world.entity.AreaEffectCloud updateColor()V
public net.minecraft.world.entity.AreaEffectCloud waitTime
public net.minecraft.world.entity.Display DATA_POS_ROT_INTERPOLATION_DURATION_ID
public net.minecraft.world.entity.Display createTransformation(Lnet/minecraft/network/syncher/SynchedEntityData;)Lcom/mojang/math/Transformation;
public net.minecraft.world.entity.Display getBillboardConstraints()Lnet/minecraft/world/entity/Display$BillboardConstraints;
public net.minecraft.world.entity.Display getBrightnessOverride()Lnet/minecraft/util/Brightness;
public net.minecraft.world.entity.Display getGlowColorOverride()I
public net.minecraft.world.entity.Display getHeight()F
public net.minecraft.world.entity.Display getShadowRadius()F
public net.minecraft.world.entity.Display getShadowStrength()F
public net.minecraft.world.entity.Display getTransformationInterpolationDelay()I
public net.minecraft.world.entity.Display getTransformationInterpolationDuration()I
public net.minecraft.world.entity.Display getViewRange()F
public net.minecraft.world.entity.Display getWidth()F
public net.minecraft.world.entity.Display setBillboardConstraints(Lnet/minecraft/world/entity/Display$BillboardConstraints;)V
public net.minecraft.world.entity.Display setBrightnessOverride(Lnet/minecraft/util/Brightness;)V
public net.minecraft.world.entity.Display setGlowColorOverride(I)V
public net.minecraft.world.entity.Display setHeight(F)V
public net.minecraft.world.entity.Display setShadowRadius(F)V
public net.minecraft.world.entity.Display setShadowStrength(F)V
public net.minecraft.world.entity.Display setTransformation(Lcom/mojang/math/Transformation;)V
public net.minecraft.world.entity.Display setTransformationInterpolationDelay(I)V
public net.minecraft.world.entity.Display setTransformationInterpolationDuration(I)V
public net.minecraft.world.entity.Display setViewRange(F)V
public net.minecraft.world.entity.Display setWidth(F)V
public net.minecraft.world.entity.Display$BlockDisplay getBlockState()Lnet/minecraft/world/level/block/state/BlockState;
public net.minecraft.world.entity.Display$BlockDisplay setBlockState(Lnet/minecraft/world/level/block/state/BlockState;)V
public net.minecraft.world.entity.Display$ItemDisplay getItemStack()Lnet/minecraft/world/item/ItemStack;
public net.minecraft.world.entity.Display$ItemDisplay getItemTransform()Lnet/minecraft/world/item/ItemDisplayContext;
public net.minecraft.world.entity.Display$ItemDisplay setItemStack(Lnet/minecraft/world/item/ItemStack;)V
public net.minecraft.world.entity.Display$ItemDisplay setItemTransform(Lnet/minecraft/world/item/ItemDisplayContext;)V
public net.minecraft.world.entity.Display$TextDisplay DATA_BACKGROUND_COLOR_ID
public net.minecraft.world.entity.Display$TextDisplay DATA_LINE_WIDTH_ID
public net.minecraft.world.entity.Display$TextDisplay getBackgroundColor()I
public net.minecraft.world.entity.Display$TextDisplay getFlags()B
public net.minecraft.world.entity.Display$TextDisplay getLineWidth()I
public net.minecraft.world.entity.Display$TextDisplay getText()Lnet/minecraft/network/chat/Component;
public net.minecraft.world.entity.Display$TextDisplay getTextOpacity()B
public net.minecraft.world.entity.Display$TextDisplay setFlags(B)V
public net.minecraft.world.entity.Display$TextDisplay setText(Lnet/minecraft/network/chat/Component;)V
public net.minecraft.world.entity.Display$TextDisplay setTextOpacity(B)V
public net.minecraft.world.entity.Entity FLAG_INVISIBLE
public net.minecraft.world.entity.Entity getEncodeId()Ljava/lang/String;
public net.minecraft.world.entity.Entity getFireImmuneTicks()I
public net.minecraft.world.entity.Entity getSharedFlag(I)Z
public net.minecraft.world.entity.Entity hasVisualFire
public net.minecraft.world.entity.Entity isInBubbleColumn()Z
public net.minecraft.world.entity.Entity isInRain()Z
public net.minecraft.world.entity.Entity isInvulnerableToBase(Lnet/minecraft/world/damagesource/DamageSource;)Z
public net.minecraft.world.entity.Entity onGround
public net.minecraft.world.entity.Entity passengers
public net.minecraft.world.entity.Entity portalCooldown
public net.minecraft.world.entity.Entity random
public net.minecraft.world.entity.Entity setLevel(Lnet/minecraft/world/level/Level;)V
public net.minecraft.world.entity.Entity setRot(FF)V
public net.minecraft.world.entity.Entity setSharedFlag(IZ)V
public net.minecraft.world.entity.Entity unsetRemoved()V
public net.minecraft.world.entity.Entity wasTouchingWater
public net.minecraft.world.entity.ExperienceOrb count
public net.minecraft.world.entity.ExperienceOrb value
public net.minecraft.world.entity.GlowSquid setDarkTicks(I)V
public net.minecraft.world.entity.Interaction attack
public net.minecraft.world.entity.Interaction getHeight()F
public net.minecraft.world.entity.Interaction getResponse()Z
public net.minecraft.world.entity.Interaction getWidth()F
public net.minecraft.world.entity.Interaction interaction
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.ItemBasedSteering boostTime
public net.minecraft.world.entity.ItemBasedSteering boostTimeTotal()I
public net.minecraft.world.entity.ItemBasedSteering boosting
public net.minecraft.world.entity.LightningBolt flashes
public net.minecraft.world.entity.LightningBolt life
public net.minecraft.world.entity.LightningBolt visualOnly
public net.minecraft.world.entity.LivingEntity DATA_ARROW_COUNT_ID
public net.minecraft.world.entity.LivingEntity DATA_HEALTH_ID
public net.minecraft.world.entity.LivingEntity LIVING_ENTITY_FLAG_SPIN_ATTACK
public net.minecraft.world.entity.LivingEntity activeEffects
public net.minecraft.world.entity.LivingEntity completeUsingItem()V
public net.minecraft.world.entity.LivingEntity detectEquipmentUpdates()V
public net.minecraft.world.entity.LivingEntity effectsDirty
public net.minecraft.world.entity.LivingEntity entityEventForEquipmentBreak(Lnet/minecraft/world/entity/EquipmentSlot;)B
public net.minecraft.world.entity.LivingEntity getDeathSound()Lnet/minecraft/sounds/SoundEvent;
public net.minecraft.world.entity.LivingEntity getSoundVolume()F
public net.minecraft.world.entity.LivingEntity jumping
public net.minecraft.world.entity.LivingEntity lastHurt
public net.minecraft.world.entity.LivingEntity lastHurtByMob
public net.minecraft.world.entity.LivingEntity lastHurtByMobTimestamp
public net.minecraft.world.entity.LivingEntity lastHurtByPlayer
public net.minecraft.world.entity.LivingEntity lastHurtByPlayerTime
public net.minecraft.world.entity.LivingEntity setLivingEntityFlag(IZ)V
public net.minecraft.world.entity.LivingEntity useItemRemaining
public net.minecraft.world.entity.Mob armorDropChances
public net.minecraft.world.entity.Mob getEquipmentDropChance(Lnet/minecraft/world/entity/EquipmentSlot;)F
public net.minecraft.world.entity.Mob handDropChances
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.OminousItemSpawner setItem(Lnet/minecraft/world/item/ItemStack;)V
public net.minecraft.world.entity.OminousItemSpawner spawnItemAfterTicks
public net.minecraft.world.entity.ai.attributes.AttributeSupplier getAttributeInstance(Lnet/minecraft/core/Holder;)Lnet/minecraft/world/entity/ai/attributes/AttributeInstance;
public net.minecraft.world.entity.ai.control.MoveControl$Operation
public net.minecraft.world.entity.ai.gossip.GossipContainer gossips
public net.minecraft.world.entity.ai.gossip.GossipContainer$EntityGossips
public net.minecraft.world.entity.ai.gossip.GossipContainer$EntityGossips <init>()V
public net.minecraft.world.entity.ai.navigation.PathNavigation pathFinder
public net.minecraft.world.entity.ambient.Bat targetPosition
public net.minecraft.world.entity.animal.AbstractSchoolingFish leader
public net.minecraft.world.entity.animal.AbstractSchoolingFish schoolSize
public net.minecraft.world.entity.animal.Animal inLove
public net.minecraft.world.entity.animal.Animal loveCause
public net.minecraft.world.entity.animal.Bee hivePos
public net.minecraft.world.entity.animal.Bee isRolling()Z
public net.minecraft.world.entity.animal.Bee numCropsGrownSincePollination
public net.minecraft.world.entity.animal.Bee setHasNectar(Z)V
public net.minecraft.world.entity.animal.Bee setHasStung(Z)V
public net.minecraft.world.entity.animal.Bee setRolling(Z)V
public net.minecraft.world.entity.animal.Bee stayOutOfHiveCountdown
public net.minecraft.world.entity.animal.Bee ticksWithoutNectarSinceExitingHive
public net.minecraft.world.entity.animal.Cat isRelaxStateOne()Z
public net.minecraft.world.entity.animal.Cat setCollarColor(Lnet/minecraft/world/item/DyeColor;)V
public net.minecraft.world.entity.animal.Cat setRelaxStateOne(Z)V
public net.minecraft.world.entity.animal.Fox DATA_TRUSTED_ID_0
public net.minecraft.world.entity.animal.Fox DATA_TRUSTED_ID_1
public net.minecraft.world.entity.animal.Fox isDefending()Z
public net.minecraft.world.entity.animal.Fox setDefending(Z)V
public net.minecraft.world.entity.animal.Fox setFaceplanted(Z)V
public net.minecraft.world.entity.animal.Fox setSleeping(Z)V
public net.minecraft.world.entity.animal.MushroomCow stewEffects
public net.minecraft.world.entity.animal.Ocelot isTrusting()Z
public net.minecraft.world.entity.animal.Ocelot setTrusting(Z)V
public net.minecraft.world.entity.animal.Panda getEatCounter()I
public net.minecraft.world.entity.animal.Panda setEatCounter(I)V
public net.minecraft.world.entity.animal.Pig steering
public net.minecraft.world.entity.animal.Rabbit moreCarrotTicks
public net.minecraft.world.entity.animal.Rabbit registerGoals()V
public net.minecraft.world.entity.animal.TropicalFish getPackedVariant()I
public net.minecraft.world.entity.animal.TropicalFish setPackedVariant(I)V
public net.minecraft.world.entity.animal.Turtle getHomePos()Lnet/minecraft/core/BlockPos;
public net.minecraft.world.entity.animal.Turtle isGoingHome()Z
public net.minecraft.world.entity.animal.Turtle isTravelling()Z
public net.minecraft.world.entity.animal.Turtle setGoingHome(Z)V
public net.minecraft.world.entity.animal.Turtle setHasEgg(Z)V
public net.minecraft.world.entity.animal.Turtle setTravelling(Z)V
public net.minecraft.world.entity.animal.Wolf isWet
public net.minecraft.world.entity.animal.Wolf setCollarColor(Lnet/minecraft/world/item/DyeColor;)V
public net.minecraft.world.entity.animal.allay.Allay canDuplicate()Z
public net.minecraft.world.entity.animal.allay.Allay duplicateAllay()V
public net.minecraft.world.entity.animal.allay.Allay duplicationCooldown
public net.minecraft.world.entity.animal.allay.Allay jukeboxPos
public net.minecraft.world.entity.animal.allay.Allay resetDuplicationCooldown()V
public net.minecraft.world.entity.animal.frog.Tadpole age
public net.minecraft.world.entity.animal.goat.Goat DATA_HAS_LEFT_HORN
public net.minecraft.world.entity.animal.goat.Goat DATA_HAS_RIGHT_HORN
public net.minecraft.world.entity.animal.horse.AbstractHorse createInventory()V
public net.minecraft.world.entity.animal.horse.AbstractHorse inventory
public net.minecraft.world.entity.animal.horse.Horse setVariantAndMarkings(Lnet/minecraft/world/entity/animal/horse/Variant;Lnet/minecraft/world/entity/animal/horse/Markings;)V
public net.minecraft.world.entity.animal.horse.SkeletonHorse trapTime
public net.minecraft.world.entity.animal.sniffer.Sniffer calculateDigPosition()Ljava/util/Optional;
public net.minecraft.world.entity.animal.sniffer.Sniffer canDig()Z
public net.minecraft.world.entity.animal.sniffer.Sniffer getExploredPositions()Ljava/util/stream/Stream;
public net.minecraft.world.entity.animal.sniffer.Sniffer getState()Lnet/minecraft/world/entity/animal/sniffer/Sniffer$State;
public net.minecraft.world.entity.animal.sniffer.Sniffer storeExploredPosition(Lnet/minecraft/core/BlockPos;)Lnet/minecraft/world/entity/animal/sniffer/Sniffer;
public net.minecraft.world.entity.boss.enderdragon.EnderDragon subEntities
public net.minecraft.world.entity.boss.wither.WitherBoss bossEvent
public net.minecraft.world.entity.decoration.ArmorStand bodyPose
public net.minecraft.world.entity.decoration.ArmorStand disabledSlots
public net.minecraft.world.entity.decoration.ArmorStand headPose
public net.minecraft.world.entity.decoration.ArmorStand isDisabled(Lnet/minecraft/world/entity/EquipmentSlot;)Z
public net.minecraft.world.entity.decoration.ArmorStand leftArmPose
public net.minecraft.world.entity.decoration.ArmorStand leftLegPose
public net.minecraft.world.entity.decoration.ArmorStand rightArmPose
public net.minecraft.world.entity.decoration.ArmorStand rightLegPose
public net.minecraft.world.entity.decoration.ArmorStand setMarker(Z)V
public net.minecraft.world.entity.decoration.ArmorStand setSmall(Z)V
public net.minecraft.world.entity.decoration.HangingEntity setDirection(Lnet/minecraft/core/Direction;)V
public net.minecraft.world.entity.decoration.ItemFrame DATA_ITEM
public net.minecraft.world.entity.decoration.ItemFrame DATA_ROTATION
public net.minecraft.world.entity.decoration.ItemFrame dropChance
public net.minecraft.world.entity.decoration.ItemFrame fixed
public net.minecraft.world.entity.decoration.ItemFrame setDirection(Lnet/minecraft/core/Direction;)V
public net.minecraft.world.entity.item.FallingBlockEntity <init>(Lnet/minecraft/world/level/Level;DDDLnet/minecraft/world/level/block/state/BlockState;)V
public net.minecraft.world.entity.item.FallingBlockEntity blockState
public net.minecraft.world.entity.item.FallingBlockEntity cancelDrop
public net.minecraft.world.entity.item.FallingBlockEntity fallDamageMax
public net.minecraft.world.entity.item.FallingBlockEntity fallDamagePerDistance
public net.minecraft.world.entity.item.FallingBlockEntity hurtEntities
public net.minecraft.world.entity.item.ItemEntity age
public net.minecraft.world.entity.item.ItemEntity health
public net.minecraft.world.entity.item.ItemEntity pickupDelay
public net.minecraft.world.entity.item.ItemEntity target
public net.minecraft.world.entity.item.ItemEntity thrower
public net.minecraft.world.entity.item.PrimedTnt explosionPower
public net.minecraft.world.entity.item.PrimedTnt owner
public net.minecraft.world.entity.monster.Creeper explodeCreeper()V
public net.minecraft.world.entity.monster.Creeper explosionRadius
public net.minecraft.world.entity.monster.Creeper maxSwell
public net.minecraft.world.entity.monster.Creeper swell
public net.minecraft.world.entity.monster.Drowned groundNavigation
public net.minecraft.world.entity.monster.Drowned waterNavigation
public net.minecraft.world.entity.monster.EnderMan teleport()Z
public net.minecraft.world.entity.monster.EnderMan teleportTowards(Lnet/minecraft/world/entity/Entity;)Z
public net.minecraft.world.entity.monster.Endermite life
public net.minecraft.world.entity.monster.Evoker getWololoTarget()Lnet/minecraft/world/entity/animal/Sheep;
public net.minecraft.world.entity.monster.Evoker setWololoTarget(Lnet/minecraft/world/entity/animal/Sheep;)V
public net.minecraft.world.entity.monster.Guardian randomStrollGoal
public net.minecraft.world.entity.monster.Guardian setActiveAttackTarget(I)V
public net.minecraft.world.entity.monster.Guardian$GuardianAttackGoal
public net.minecraft.world.entity.monster.Guardian$GuardianAttackGoal attackTime
public net.minecraft.world.entity.monster.Phantom anchorPoint
public net.minecraft.world.entity.monster.Pillager inventory
public net.minecraft.world.entity.monster.Ravager attackTick
public net.minecraft.world.entity.monster.Ravager roarTick
public net.minecraft.world.entity.monster.Ravager stunnedTick
public net.minecraft.world.entity.monster.Shulker DATA_COLOR_ID
public net.minecraft.world.entity.monster.Shulker getRawPeekAmount()I
public net.minecraft.world.entity.monster.Shulker setAttachFace(Lnet/minecraft/core/Direction;)V
public net.minecraft.world.entity.monster.Shulker setRawPeekAmount(I)V
public net.minecraft.world.entity.monster.Skeleton DATA_STRAY_CONVERSION_ID
public net.minecraft.world.entity.monster.Skeleton conversionTime
public net.minecraft.world.entity.monster.Skeleton inPowderSnowTime
public net.minecraft.world.entity.monster.SpellcasterIllager getCurrentSpell()Lnet/minecraft/world/entity/monster/SpellcasterIllager$IllagerSpell;
public net.minecraft.world.entity.monster.SpellcasterIllager$IllagerSpell
public net.minecraft.world.entity.monster.Strider steering
public net.minecraft.world.entity.monster.Vex hasLimitedLife
public net.minecraft.world.entity.monster.Vex limitedLifeTicks
public net.minecraft.world.entity.monster.Vindicator DOOR_BREAKING_PREDICATE
public net.minecraft.world.entity.monster.Vindicator isJohnny
public net.minecraft.world.entity.monster.Witch usingTime
public net.minecraft.world.entity.monster.Zombie DATA_DROWNED_CONVERSION_ID
public net.minecraft.world.entity.monster.Zombie DOOR_BREAKING_PREDICATE
public net.minecraft.world.entity.monster.Zombie conversionTime
public net.minecraft.world.entity.monster.Zombie isSunSensitive()Z
public net.minecraft.world.entity.monster.Zombie startUnderWaterConversion(I)V
public net.minecraft.world.entity.monster.ZombieVillager DATA_CONVERTING_ID
public net.minecraft.world.entity.monster.ZombieVillager conversionStarter
public net.minecraft.world.entity.monster.ZombieVillager startConverting(Ljava/util/UUID;I)V
public net.minecraft.world.entity.monster.ZombieVillager villagerConversionTime
public net.minecraft.world.entity.monster.hoglin.Hoglin cannotBeHunted
public net.minecraft.world.entity.monster.hoglin.Hoglin isImmuneToZombification()Z
public net.minecraft.world.entity.monster.hoglin.Hoglin timeInOverworld
public net.minecraft.world.entity.monster.piglin.AbstractPiglin isImmuneToZombification()Z
public net.minecraft.world.entity.monster.piglin.AbstractPiglin timeInOverworld
public net.minecraft.world.entity.monster.piglin.Piglin cannotHunt
public net.minecraft.world.entity.monster.piglin.Piglin inventory
public net.minecraft.world.entity.monster.piglin.Piglin isChargingCrossbow()Z
public net.minecraft.world.entity.monster.warden.WardenSpawnTracker cooldownTicks
public net.minecraft.world.entity.monster.warden.WardenSpawnTracker increaseWarningLevel()V
public net.minecraft.world.entity.monster.warden.WardenSpawnTracker ticksSinceLastWarning
public net.minecraft.world.entity.npc.Villager increaseMerchantCareer()V
public net.minecraft.world.entity.npc.Villager numberOfRestocksToday
public net.minecraft.world.entity.npc.Villager releaseAllPois()V
public net.minecraft.world.entity.npc.Villager setUnhappy()V
public net.minecraft.world.entity.npc.WanderingTrader getWanderTarget()Lnet/minecraft/core/BlockPos;
public net.minecraft.world.entity.player.Abilities flyingSpeed
public net.minecraft.world.entity.player.Abilities walkingSpeed
public net.minecraft.world.entity.player.Inventory compartments
public net.minecraft.world.entity.player.Player DATA_PLAYER_MODE_CUSTOMISATION
public net.minecraft.world.entity.player.Player closeContainer()V
public net.minecraft.world.entity.player.Player enchantmentSeed
public net.minecraft.world.entity.player.Player getFireImmuneTicks()I
public net.minecraft.world.entity.player.Player removeEntitiesOnShoulder()V
public net.minecraft.world.entity.player.Player setShoulderEntityLeft(Lnet/minecraft/nbt/CompoundTag;)V
public net.minecraft.world.entity.player.Player setShoulderEntityRight(Lnet/minecraft/nbt/CompoundTag;)V
public net.minecraft.world.entity.player.Player sleepCounter
public net.minecraft.world.entity.projectile.AbstractHurtingProjectile assignDirectionalMovement(Lnet/minecraft/world/phys/Vec3;D)V
public net.minecraft.world.entity.projectile.Arrow NO_EFFECT_COLOR
public net.minecraft.world.entity.projectile.Arrow getPotionContents()Lnet/minecraft/world/item/alchemy/PotionContents;
public net.minecraft.world.entity.projectile.Arrow setPotionContents(Lnet/minecraft/world/item/alchemy/PotionContents;)V
public net.minecraft.world.entity.projectile.Arrow updateColor()V
public net.minecraft.world.entity.projectile.EvokerFangs warmupDelayTicks
public net.minecraft.world.entity.projectile.EyeOfEnder life
public net.minecraft.world.entity.projectile.EyeOfEnder surviveAfterDeath
public net.minecraft.world.entity.projectile.EyeOfEnder tx
public net.minecraft.world.entity.projectile.EyeOfEnder ty
public net.minecraft.world.entity.projectile.EyeOfEnder tz
public net.minecraft.world.entity.projectile.FireworkRocketEntity DATA_ATTACHED_TO_TARGET
public net.minecraft.world.entity.projectile.FireworkRocketEntity DATA_ID_FIREWORKS_ITEM
public net.minecraft.world.entity.projectile.FireworkRocketEntity DATA_SHOT_AT_ANGLE
public net.minecraft.world.entity.projectile.FireworkRocketEntity attachedToEntity
public net.minecraft.world.entity.projectile.FireworkRocketEntity getDefaultItem()Lnet/minecraft/world/item/ItemStack;
public net.minecraft.world.entity.projectile.FireworkRocketEntity life
public net.minecraft.world.entity.projectile.FireworkRocketEntity lifetime
public net.minecraft.world.entity.projectile.FishingHook DATA_HOOKED_ENTITY
public net.minecraft.world.entity.projectile.FishingHook calculateOpenWater(Lnet/minecraft/core/BlockPos;)Z
public net.minecraft.world.entity.projectile.FishingHook currentState
public net.minecraft.world.entity.projectile.FishingHook fishAngle
public net.minecraft.world.entity.projectile.FishingHook hookedIn
public net.minecraft.world.entity.projectile.FishingHook outOfWaterTime
public net.minecraft.world.entity.projectile.FishingHook pullEntity(Lnet/minecraft/world/entity/Entity;)V
public net.minecraft.world.entity.projectile.FishingHook setHookedEntity(Lnet/minecraft/world/entity/Entity;)V
public net.minecraft.world.entity.projectile.FishingHook timeUntilHooked
public net.minecraft.world.entity.projectile.FishingHook timeUntilLured
public net.minecraft.world.entity.projectile.FishingHook$FishHookState
public net.minecraft.world.entity.projectile.LargeFireball explosionPower
public net.minecraft.world.entity.projectile.Projectile cachedOwner
public net.minecraft.world.entity.projectile.Projectile hasBeenShot
public net.minecraft.world.entity.projectile.Projectile leftOwner
public net.minecraft.world.entity.projectile.Projectile ownerUUID
public net.minecraft.world.entity.projectile.ShulkerBullet currentMoveDirection
public net.minecraft.world.entity.projectile.ShulkerBullet flightSteps
public net.minecraft.world.entity.projectile.ShulkerBullet targetDeltaX
public net.minecraft.world.entity.projectile.ShulkerBullet targetDeltaY
public net.minecraft.world.entity.projectile.ShulkerBullet targetDeltaZ
public net.minecraft.world.entity.projectile.SpectralArrow duration
public net.minecraft.world.entity.projectile.ThrownPotion isLingering()Z
public net.minecraft.world.entity.projectile.ThrownTrident dealtDamage
public net.minecraft.world.entity.projectile.windcharge.AbstractWindCharge explode(Lnet/minecraft/world/phys/Vec3;)V
public net.minecraft.world.entity.projectile.windcharge.BreezeWindCharge explode(Lnet/minecraft/world/phys/Vec3;)V
public net.minecraft.world.entity.projectile.windcharge.WindCharge explode(Lnet/minecraft/world/phys/Vec3;)V
public net.minecraft.world.entity.raid.Raid heroesOfTheVillage
public net.minecraft.world.entity.raid.Raid numGroups
public net.minecraft.world.entity.raid.Raid raidEvent
public net.minecraft.world.entity.raid.Raid raidOmenLevel
public net.minecraft.world.entity.raid.Raid ticksActive
public net.minecraft.world.entity.raid.Raid totalHealth
public net.minecraft.world.entity.raid.Raider$HoldGroundAttackGoal
public net.minecraft.world.entity.raid.Raids raidMap
public net.minecraft.world.entity.vehicle.AbstractBoat getDropItem()Lnet/minecraft/world/item/Item;
public net.minecraft.world.entity.vehicle.AbstractBoat getStatus()Lnet/minecraft/world/entity/vehicle/AbstractBoat$Status;
public net.minecraft.world.entity.vehicle.AbstractBoat status
public net.minecraft.world.entity.vehicle.AbstractMinecartContainer lootTable
public net.minecraft.world.entity.vehicle.AbstractMinecartContainer lootTableSeed
public net.minecraft.world.entity.vehicle.MinecartCommandBlock DATA_ID_COMMAND_NAME
public net.minecraft.world.entity.vehicle.MinecartFurnace fuel
public net.minecraft.world.entity.vehicle.MinecartTNT explode(D)V
public net.minecraft.world.entity.vehicle.MinecartTNT explosionPowerBase
public net.minecraft.world.entity.vehicle.MinecartTNT explosionSpeedFactor
public net.minecraft.world.entity.vehicle.MinecartTNT fuse
public net.minecraft.world.flag.FeatureFlag mask
public net.minecraft.world.flag.FeatureFlag universe
public net.minecraft.world.flag.FeatureFlagRegistry names
public net.minecraft.world.food.FoodData exhaustionLevel
public net.minecraft.world.food.FoodData foodLevel
public net.minecraft.world.food.FoodData saturationLevel
public net.minecraft.world.inventory.AbstractContainerMenu quickcraftSlots
public net.minecraft.world.inventory.AbstractContainerMenu quickcraftStatus
public net.minecraft.world.inventory.AbstractContainerMenu quickcraftType
public net.minecraft.world.inventory.AbstractContainerMenu resetQuickCraft()V
public net.minecraft.world.inventory.AbstractCraftingMenu craftSlots
public net.minecraft.world.inventory.AbstractCraftingMenu resultSlots
public net.minecraft.world.inventory.AnvilMenu cost
public net.minecraft.world.inventory.AnvilMenu itemName
public net.minecraft.world.inventory.AnvilMenu repairItemCountCost
public net.minecraft.world.inventory.BrewingStandMenu brewingStandData
public net.minecraft.world.inventory.CraftingMenu access
public net.minecraft.world.inventory.DispenserMenu dispenser
public net.minecraft.world.inventory.HorseInventoryMenu SLOT_BODY_ARMOR
public net.minecraft.world.inventory.MerchantContainer selectionHint
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.ItemCooldowns cooldowns
public net.minecraft.world.item.ItemCooldowns tickCount
public net.minecraft.world.item.ItemCooldowns$CooldownInstance
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/world/level/Level;IIIZZLnet/minecraft/resources/ResourceKey;)Lnet/minecraft/world/level/saveddata/maps/MapId;
public net.minecraft.world.item.StandingAndWallBlockItem wallBlock
public net.minecraft.world.item.component.ItemContainerContents MAX_SIZE
public net.minecraft.world.item.component.ItemContainerContents items
public net.minecraft.world.item.context.UseOnContext <init>(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
public net.minecraft.world.item.crafting.RecipeManager recipes
public net.minecraft.world.item.crafting.RecipeMap byKey
public net.minecraft.world.item.crafting.RecipeMap byType
public net.minecraft.world.item.enchantment.ItemEnchantments showInTooltip
public net.minecraft.world.item.trading.MerchantOffer demand
public net.minecraft.world.item.trading.MerchantOffer result
public net.minecraft.world.item.trading.MerchantOffer specialPriceDiff
public net.minecraft.world.item.trading.MerchantOffer uses
public net.minecraft.world.level.BaseSpawner delay(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;)V
public net.minecraft.world.level.BaseSpawner isNearPlayer(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;)Z
public net.minecraft.world.level.BaseSpawner maxNearbyEntities
public net.minecraft.world.level.BaseSpawner maxSpawnDelay
public net.minecraft.world.level.BaseSpawner minSpawnDelay
public net.minecraft.world.level.BaseSpawner nextSpawnData
public net.minecraft.world.level.BaseSpawner requiredPlayerRange
public net.minecraft.world.level.BaseSpawner setNextSpawnData(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/SpawnData;)V
public net.minecraft.world.level.BaseSpawner spawnCount
public net.minecraft.world.level.BaseSpawner spawnDelay
public net.minecraft.world.level.BaseSpawner spawnPotentials
public net.minecraft.world.level.BaseSpawner spawnRange
public net.minecraft.world.level.GameRules$Value onChanged(Lnet/minecraft/server/MinecraftServer;)V
public net.minecraft.world.level.Level getEntities()Lnet/minecraft/world/level/entity/LevelEntityGetter;
public net.minecraft.world.level.Level levelData
public net.minecraft.world.level.Level rainLevel
public net.minecraft.world.level.Level thread
public net.minecraft.world.level.Level thunderLevel
public net.minecraft.world.level.NaturalSpawner SPAWNING_CATEGORIES
public net.minecraft.world.level.StructureManager level
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.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.ChiseledBookShelfBlock getHitSlot(Lnet/minecraft/world/phys/BlockHitResult;Lnet/minecraft/world/level/block/state/BlockState;)Ljava/util/OptionalInt;
public net.minecraft.world.level.block.ChiseledBookShelfBlock getSection(F)I
public net.minecraft.world.level.block.ComposterBlock$EmptyContainer
public net.minecraft.world.level.block.ComposterBlock$InputContainer
public net.minecraft.world.level.block.ComposterBlock$OutputContainer
public net.minecraft.world.level.block.DispenserBlock dispenseFrom(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/BlockPos;)V
public net.minecraft.world.level.block.DropperBlock dispenseFrom(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/BlockPos;)V
public net.minecraft.world.level.block.FireBlock igniteOdds
public net.minecraft.world.level.block.RedStoneWireBlock canSurvive(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/LevelReader;Lnet/minecraft/core/BlockPos;)Z
public net.minecraft.world.level.block.RedStoneWireBlock shouldSignal
public net.minecraft.world.level.block.ShulkerBoxBlock color
public net.minecraft.world.level.block.SoundType breakSound
public net.minecraft.world.level.block.SoundType hitSound
public net.minecraft.world.level.block.TurtleEggBlock decreaseEggs(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;)V
public net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity cookingTimer
public net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity cookingTotalTime
public net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity getTotalCookTime(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity;)I
public net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity litTimeRemaining
public net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity recipesUsed
public net.minecraft.world.level.block.entity.BarrelBlockEntity openersCounter
public net.minecraft.world.level.block.entity.BarrelBlockEntity playSound(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/sounds/SoundEvent;)V
public net.minecraft.world.level.block.entity.BarrelBlockEntity updateBlockState(Lnet/minecraft/world/level/block/state/BlockState;Z)V
public net.minecraft.world.level.block.entity.BaseContainerBlockEntity lockKey
public net.minecraft.world.level.block.entity.BaseContainerBlockEntity name
public net.minecraft.world.level.block.entity.BeaconBlockEntity levels
public net.minecraft.world.level.block.entity.BeaconBlockEntity lockKey
public net.minecraft.world.level.block.entity.BeaconBlockEntity name
public net.minecraft.world.level.block.entity.BeaconBlockEntity primaryPower
public net.minecraft.world.level.block.entity.BeaconBlockEntity secondaryPower
public net.minecraft.world.level.block.entity.BedBlockEntity color
public net.minecraft.world.level.block.entity.BeehiveBlockEntity savedFlowerPos
public net.minecraft.world.level.block.entity.BellBlockEntity resonating
public net.minecraft.world.level.block.entity.BellBlockEntity resonationTicks
public net.minecraft.world.level.block.entity.BlockEntity saveId(Lnet/minecraft/nbt/CompoundTag;)V
public net.minecraft.world.level.block.entity.BlockEntityType validBlocks
public net.minecraft.world.level.block.entity.BrewingStandBlockEntity brewTime
public net.minecraft.world.level.block.entity.BrewingStandBlockEntity fuel
public net.minecraft.world.level.block.entity.BrushableBlockEntity item
public net.minecraft.world.level.block.entity.BrushableBlockEntity lootTable
public net.minecraft.world.level.block.entity.BrushableBlockEntity lootTableSeed
public net.minecraft.world.level.block.entity.CampfireBlockEntity cookingProgress
public net.minecraft.world.level.block.entity.CampfireBlockEntity cookingTime
public net.minecraft.world.level.block.entity.ChestBlockEntity openersCounter
public net.minecraft.world.level.block.entity.ChestBlockEntity playSound(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/sounds/SoundEvent;)V
public net.minecraft.world.level.block.entity.ChiseledBookShelfBlockEntity lastInteractedSlot
public net.minecraft.world.level.block.entity.ConduitBlockEntity destroyTarget
public net.minecraft.world.level.block.entity.ConduitBlockEntity destroyTargetUUID
public net.minecraft.world.level.block.entity.ConduitBlockEntity effectBlocks
public net.minecraft.world.level.block.entity.ConduitBlockEntity getDestroyRangeAABB(Lnet/minecraft/core/BlockPos;)Lnet/minecraft/world/phys/AABB;
public net.minecraft.world.level.block.entity.CrafterBlockEntity craftingTicksRemaining
public net.minecraft.world.level.block.entity.DecoratedPotBlockEntity decorations
public net.minecraft.world.level.block.entity.EnderChestBlockEntity openersCounter
public net.minecraft.world.level.block.entity.HopperBlockEntity cooldownTime
public net.minecraft.world.level.block.entity.HopperBlockEntity setCooldown(I)V
public net.minecraft.world.level.block.entity.LecternBlockEntity bookAccess
public net.minecraft.world.level.block.entity.LecternBlockEntity setPage(I)V
public net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity lootTable
public net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity lootTableSeed
public net.minecraft.world.level.block.entity.SculkCatalystBlockEntity$CatalystListener bloom(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/util/RandomSource;)V
public net.minecraft.world.level.block.entity.SculkSensorBlockEntity lastVibrationFrequency
public net.minecraft.world.level.block.entity.SculkShriekerBlockEntity warningLevel
public net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity openCount
public net.minecraft.world.level.block.entity.SignBlockEntity playerWhoMayEdit
public net.minecraft.world.level.block.entity.SkullBlockEntity noteBlockSound
public net.minecraft.world.level.block.entity.SkullBlockEntity owner
public net.minecraft.world.level.block.entity.StructureBlockEntity author
public net.minecraft.world.level.block.entity.StructureBlockEntity ignoreEntities
public net.minecraft.world.level.block.entity.StructureBlockEntity integrity
public net.minecraft.world.level.block.entity.StructureBlockEntity metaData
public net.minecraft.world.level.block.entity.StructureBlockEntity mirror
public net.minecraft.world.level.block.entity.StructureBlockEntity mode
public net.minecraft.world.level.block.entity.StructureBlockEntity rotation
public net.minecraft.world.level.block.entity.StructureBlockEntity seed
public net.minecraft.world.level.block.entity.StructureBlockEntity showAir
public net.minecraft.world.level.block.entity.StructureBlockEntity showBoundingBox
public net.minecraft.world.level.block.entity.StructureBlockEntity structurePos
public net.minecraft.world.level.block.entity.StructureBlockEntity structureSize
public net.minecraft.world.level.block.entity.TheEndGatewayBlockEntity age
public net.minecraft.world.level.block.entity.TheEndGatewayBlockEntity exactTeleport
public net.minecraft.world.level.block.entity.TheEndGatewayBlockEntity exitPortal
public net.minecraft.world.level.block.entity.TrialSpawnerBlockEntity trialSpawner
public net.minecraft.world.level.block.entity.trialspawner.TrialSpawner isOminous
public net.minecraft.world.level.block.entity.trialspawner.TrialSpawner stateAccessor
public net.minecraft.world.level.block.entity.trialspawner.TrialSpawnerData currentMobs
public net.minecraft.world.level.block.entity.trialspawner.TrialSpawnerData detectedPlayers
public net.minecraft.world.level.block.entity.trialspawner.TrialSpawnerData nextSpawnData
public net.minecraft.world.level.block.state.BlockBehaviour getMenuProvider(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;)Lnet/minecraft/world/MenuProvider;
public net.minecraft.world.level.block.state.BlockBehaviour hasCollision
public net.minecraft.world.level.block.state.BlockBehaviour$BlockStateBase destroySpeed
public net.minecraft.world.level.block.state.StateHolder PROPERTY_ENTRY_TO_STRING_FUNCTION
public net.minecraft.world.level.block.state.properties.IntegerProperty max
public net.minecraft.world.level.block.state.properties.IntegerProperty min
public net.minecraft.world.level.chunk.ChunkAccess blockEntities
public net.minecraft.world.level.chunk.ChunkAccess heightmaps
public net.minecraft.world.level.chunk.ChunkGenerator generationSettingsGetter
public net.minecraft.world.level.chunk.LevelChunk level
public net.minecraft.world.level.chunk.LevelChunk loaded
public net.minecraft.world.level.chunk.LevelChunkSection states
public net.minecraft.world.level.chunk.PalettedContainer registry
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.chunk.storage.SerializableChunkData BLOCK_STATE_CODEC
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.entity.PersistentEntitySectionManager ensureChunkQueuedForLoad(J)V
public net.minecraft.world.level.entity.PersistentEntitySectionManager permanentStorage
public net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator settings
public net.minecraft.world.level.levelgen.SurfaceRules$Condition
public net.minecraft.world.level.levelgen.SurfaceRules$Context
public net.minecraft.world.level.levelgen.SurfaceRules$Context blockX
public net.minecraft.world.level.levelgen.SurfaceRules$Context blockY
public net.minecraft.world.level.levelgen.SurfaceRules$Context blockZ
public net.minecraft.world.level.levelgen.SurfaceRules$Context context
public net.minecraft.world.level.levelgen.SurfaceRules$Context randomState
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.structure.placement.StructurePlacement exclusionZone
public net.minecraft.world.level.levelgen.structure.placement.StructurePlacement frequency
public net.minecraft.world.level.levelgen.structure.placement.StructurePlacement frequencyReductionMethod
public net.minecraft.world.level.levelgen.structure.placement.StructurePlacement locateOffset
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/ResourceLocation;)Ljava/util/Optional;
public net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager loadFromResource(Lnet/minecraft/resources/ResourceLocation;)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 structureRepository
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
public net.minecraft.world.level.saveddata.maps.MapItemSavedData carriedBy
public net.minecraft.world.level.saveddata.maps.MapItemSavedData carriedByPlayers
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.scores.Objective displayName
public net.minecraft.world.scores.criteria.ObjectiveCriteria CRITERIA_CACHE
public-f net.minecraft.server.MinecraftServer potionBrewing
public-f net.minecraft.server.MinecraftServer storageSource
public-f net.minecraft.server.ReloadableServerResources commands
public-f net.minecraft.server.dedicated.DedicatedServer serverLinks
public-f net.minecraft.server.dedicated.DedicatedServer settings
public-f net.minecraft.server.dedicated.DedicatedServerProperties pauseWhenEmptySeconds
public-f net.minecraft.server.level.TicketType timeout
public-f net.minecraft.server.players.PlayerList maxPlayers
public-f net.minecraft.world.entity.LivingEntity combatTracker
public-f net.minecraft.world.entity.LivingEntity invulnerableDuration
public-f net.minecraft.world.entity.Mob goalSelector
public-f net.minecraft.world.entity.Mob targetSelector
public-f net.minecraft.world.entity.ai.attributes.RangedAttribute maxValue
public-f net.minecraft.world.entity.player.Player gameProfile
public-f net.minecraft.world.inventory.AbstractContainerMenu dataSlots
public-f net.minecraft.world.inventory.AbstractContainerMenu lastSlots
public-f net.minecraft.world.inventory.AbstractContainerMenu remoteDataSlots
public-f net.minecraft.world.inventory.AbstractContainerMenu remoteSlots
public-f net.minecraft.world.inventory.AbstractContainerMenu slots
public-f net.minecraft.world.item.enchantment.ItemEnchantments$Mutable showInTooltip
public-f net.minecraft.world.item.trading.MerchantOffer baseCostA
public-f net.minecraft.world.item.trading.MerchantOffer costB
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.entity.BannerBlockEntity baseColor
public-f net.minecraft.world.level.block.entity.trialspawner.TrialSpawner normalConfig
public-f net.minecraft.world.level.block.entity.trialspawner.TrialSpawner ominousConfig
public-f net.minecraft.world.level.block.entity.trialspawner.TrialSpawner requiredPlayerRange
public-f net.minecraft.world.level.block.entity.trialspawner.TrialSpawner targetCooldownLength
public-f net.minecraft.world.level.saveddata.maps.MapItemSavedData centerX
public-f net.minecraft.world.level.saveddata.maps.MapItemSavedData centerZ
public-f net.minecraft.world.level.saveddata.maps.MapItemSavedData dimension
public-f net.minecraft.world.level.saveddata.maps.MapItemSavedData locked
public-f net.minecraft.world.level.saveddata.maps.MapItemSavedData scale
public-f net.minecraft.world.level.saveddata.maps.MapItemSavedData trackingPosition
public-f net.minecraft.world.level.saveddata.maps.MapItemSavedData unlimitedTracking

View file

@ -1,4 +1,5 @@
import io.papermc.paperweight.util.*
import io.papermc.paperweight.util.constants.*
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent
import java.io.IOException
@ -8,9 +9,10 @@ import java.nio.file.Files
import java.nio.file.SimpleFileVisitor
import kotlin.io.path.*
import java.nio.file.Path
import kotlin.random.Random
plugins {
id("io.papermc.paperweight.core") version "2.0.0-SNAPSHOT" apply false
id("io.papermc.paperweight.core") version "2.0.0-beta.8" apply false
}
subprojects {
@ -23,10 +25,6 @@ subprojects {
}
}
dependencies {
"testRuntimeOnly"("org.junit.platform:junit-platform-launcher")
}
tasks.withType<AbstractArchiveTask>().configureEach {
isPreserveFileTimestamps = false
isReproducibleFileOrder = true
@ -89,7 +87,6 @@ tasks.register("gibWork") {
val patchesFolder = layout.projectDirectory.dir("paper-server/patches/").convertToPath()
val storage = layout.cache.resolve("last-updating-folder").also { it.parent.createDirectories() }
@OptIn(ExperimentalPathApi::class)
doLast {
val html = URI(issue).toURL().readText()
@ -98,18 +95,26 @@ tasks.register("gibWork") {
val end = html.indexOf("```", start + beginMarker.length)
val taskList = html.substring(start + beginMarker.length, end)
val next = taskList.split("\\n").first { it.startsWith("- [ ]") }.replace("- [ ] ", "")
// Extract all incomplete tasks and select a random one
val incompleteTasks = taskList.split("\\n").filter { it.startsWith("- [ ]") }.map { it.replace("- [ ] ", "") }
if (incompleteTasks.isEmpty()) {
error("No incomplete tasks found in the task list.")
}
val next = incompleteTasks[Random.nextInt(incompleteTasks.size)]
println("checking out $next...")
val dir = patchesFolder.resolve("unapplied").resolve(next)
if (!dir.exists()) {
error("Unapplied patch folder $next does not exist, did someone else already check it out and forgot to mark it?")
}
dir.copyToRecursively(
patchesFolder.resolve("sources").resolve(next)
.also { it.createDirectories() }, overwrite = true, followLinks = false
)
dir.deleteRecursively()
dir.listDirectoryEntries("*.patch").forEach { patch ->
patch.copyTo(patchesFolder.resolve("sources").resolve(next).resolve(patch.fileName).also { it.createDirectories() }, overwrite = true)
patch.deleteIfExists()
}
if (dir.listDirectoryEntries().isEmpty()) {
dir.deleteIfExists()
}
storage.writeText(next)
println("please tick the box in the issue: $issue")
@ -133,23 +138,24 @@ tasks.register("showWork") {
}
tasks.register("checkWork") {
notCompatibleWithConfigurationCache("This task is interactive")
fun expandUserHome(path: String): Path {
return Path.of(path.replaceFirst("^~".toRegex(), System.getProperty("user.home")))
}
val input = layout.cache.resolve("last-updating-folder").readText()
val patchFolder = layout.projectDirectory.file("paper-server/patches/sources").convertToPath().resolve(input)
val sourceFolder = layout.projectDirectory.file("paper-server/src/vanilla/java/").convertToPath().resolve(input)
val targetFolder = expandUserHome(
providers.gradleProperty("cleanPaperRepo").orNull
?: error("cleanPaperRepo is required, define it in gradle.properties")
).resolve(input)
val input = providers.fileContents(layout.projectDirectory.file("$CACHE_PATH/last-updating-folder")).asText.map { it.trim() }
val patchFolder = layout.projectDirectory.dir("paper-server/patches/sources").dir(input)
val sourceFolder = layout.projectDirectory.dir("paper-server/src/minecraft/java").dir(input)
val targetFolder = providers.gradleProperty("cleanPaperRepo").map {
expandUserHome(it).resolve(input.get())
}
fun copy(back: Boolean = false) {
patchFolder.listDirectoryEntries().forEach {
val relative = patchFolder.relativize(it).toString().replace(".patch", "")
val source = sourceFolder.resolve(relative)
val target = targetFolder.resolve(relative)
patchFolder.path.listDirectoryEntries().forEach {
val relative = patchFolder.path.relativize(it).toString().replace(".patch", "")
val source = sourceFolder.path.resolve(relative)
val target = targetFolder.get().resolve(relative)
if (target.isDirectory()) { return@forEach }
if (back) {
target.copyTo(source, overwrite = true)
} else {
@ -159,8 +165,11 @@ tasks.register("checkWork") {
}
doLast {
if (!targetFolder.isPresent) {
error("cleanPaperRepo is required, define it in gradle.properties")
}
copy()
val files = patchFolder.listDirectoryEntries().map { it.fileName.toString().replace(".patch", "") }
val files = patchFolder.path.listDirectoryEntries().map { it.fileName.toString().replace(".patch", "") }
println("Copied $files from $sourceFolder to $targetFolder")
println("Make the files compile, then press enter to copy them back!")
System.`in`.read()

View file

@ -1,99 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Thu, 3 Mar 2016 02:07:55 -0600
Subject: [PATCH] Optimize isInWorldBounds and getBlockState for inlining
Hot methods, so reduce # of instructions for the method.
Move is valid location test to the BlockPosition class so that it can access local variables.
Replace all calls to the new place to the unnecessary forward.
Optimize getType and getBlockData to manually inline and optimize the calls
diff --git a/src/main/java/net/minecraft/core/Vec3i.java b/src/main/java/net/minecraft/core/Vec3i.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/core/Vec3i.java
+++ b/src/main/java/net/minecraft/core/Vec3i.java
@@ -0,0 +0,0 @@ public class Vec3i implements Comparable<Vec3i> {
);
}
+ // Paper start
+ public final boolean isInsideBuildHeightAndWorldBoundsHorizontal(net.minecraft.world.level.LevelHeightAccessor levelHeightAccessor) {
+ return getX() >= -30000000 && getZ() >= -30000000 && getX() < 30000000 && getZ() < 30000000 && !levelHeightAccessor.isOutsideBuildHeight(getY());
+ }
+ // Paper end
+
public Vec3i(int x, int y, int z) {
this.x = x;
this.y = y;
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -0,0 +0,0 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
// Paper end
public boolean isInWorldBounds(BlockPos pos) {
- return !this.isOutsideBuildHeight(pos) && Level.isInWorldBoundsHorizontal(pos);
+ return pos.isInsideBuildHeightAndWorldBoundsHorizontal(this); // Paper - use better/optimized check
}
public static boolean isInSpawnableBounds(BlockPos pos) {
diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
@@ -0,0 +0,0 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh
return GameEventListenerRegistry.NOOP;
}
+ public abstract BlockState getBlockState(final int x, final int y, final int z); // Paper
@Nullable
public abstract BlockState setBlockState(BlockPos pos, BlockState state, boolean moved);
diff --git a/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java b/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java
@@ -0,0 +0,0 @@ public class ImposterProtoChunk extends ProtoChunk {
public BlockState getBlockState(BlockPos pos) {
return this.wrapped.getBlockState(pos);
}
+ // Paper start
+ @Override
+ public final BlockState getBlockState(final int x, final int y, final int z) {
+ return this.wrapped.getBlockStateFinal(x, y, z);
+ }
+ // Paper end
@Override
public FluidState getFluidState(BlockPos pos) {
diff --git a/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java b/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java
@@ -0,0 +0,0 @@ public class ProtoChunk extends ChunkAccess {
@Override
public BlockState getBlockState(BlockPos pos) {
- int i = pos.getY();
- if (this.isOutsideBuildHeight(i)) {
+ // Paper start
+ return getBlockState(pos.getX(), pos.getY(), pos.getZ());
+ }
+ public BlockState getBlockState(final int x, final int y, final int z) {
+ if (this.isOutsideBuildHeight(y)) {
return Blocks.VOID_AIR.defaultBlockState();
} else {
- LevelChunkSection levelChunkSection = this.getSection(this.getSectionIndex(i));
- return levelChunkSection.hasOnlyAir() ? Blocks.AIR.defaultBlockState() : levelChunkSection.getBlockState(pos.getX() & 15, i & 15, pos.getZ() & 15);
+ LevelChunkSection levelChunkSection = this.getSections()[this.getSectionIndex(y)];
+ return levelChunkSection.hasOnlyAir() ? Blocks.AIR.defaultBlockState() : levelChunkSection.getBlockState(x & 15, y & 15, z & 15);
}
}
+ // Paper end
@Override
public FluidState getFluidState(BlockPos pos) {

View file

@ -1,124 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Fri, 29 Apr 2016 20:02:00 -0400
Subject: [PATCH] Improve Maps (in item frames) performance and bug fixes
Maps used a modified version of rendering to support plugin controlled
imaging on maps. The Craft Map Renderer is much slower than Vanilla,
causing maps in item frames to cause a noticeable hit on server performance.
This updates the map system to not use the Craft system if we detect that no
custom renderers are in use, defaulting to the much simpler Vanilla system.
Additionally, numerous issues to player position tracking on maps has been fixed.
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -0,0 +0,0 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
{
if ( iter.next().player == entity )
{
+ map.decorations.remove(entity.getName().getString()); // Paper
iter.remove();
}
}
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -0,0 +0,0 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player {
this.awardStat(Stats.DROP);
}
+ // Paper start - remove player from map on drop
+ if (itemstack.getItem() == net.minecraft.world.item.Items.FILLED_MAP) {
+ net.minecraft.world.level.saveddata.maps.MapItemSavedData worldmap = net.minecraft.world.item.MapItem.getSavedData(itemstack, this.level());
+ if (worldmap != null) {
+ worldmap.tickCarriedBy(this, itemstack);
+ }
+ }
+ // Paper end
return entityitem;
}
}
diff --git a/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java b/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
+++ b/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
@@ -0,0 +0,0 @@ public class MapItemSavedData extends SavedData {
public final Map<String, MapDecoration> decorations = Maps.newLinkedHashMap();
private final Map<String, MapFrame> frameMarkers = Maps.newHashMap();
private int trackedDecorationCount;
+ private org.bukkit.craftbukkit.map.RenderData vanillaRender = new org.bukkit.craftbukkit.map.RenderData(); // Paper
// CraftBukkit start
public final CraftMapView mapView;
@@ -0,0 +0,0 @@ public class MapItemSavedData extends SavedData {
// CraftBukkit start
this.mapView = new CraftMapView(this);
this.server = (CraftServer) org.bukkit.Bukkit.getServer();
+ this.vanillaRender.buffer = colors; // Paper
// CraftBukkit end
}
@@ -0,0 +0,0 @@ public class MapItemSavedData extends SavedData {
if (abyte.length == 16384) {
worldmap.colors = abyte;
}
+ worldmap.vanillaRender.buffer = abyte; // Paper
RegistryOps<Tag> registryops = registries.createSerializationContext(NbtOps.INSTANCE);
List<MapBanner> list = (List) MapBanner.LIST_CODEC.parse(registryops, nbt.get("banners")).resultOrPartial((s) -> {
@@ -0,0 +0,0 @@ public class MapItemSavedData extends SavedData {
--this.trackedDecorationCount;
}
- this.setDecorationsDirty();
+ if (mapicon != null) this.setDecorationsDirty(); // Paper - only mark dirty if a change occurs
}
public static void addTargetDecoration(ItemStack stack, BlockPos pos, String id, Holder<MapDecorationType> decorationType) {
@@ -0,0 +0,0 @@ public class MapItemSavedData extends SavedData {
public class HoldingPlayer {
+ // Paper start
+ private void addSeenPlayers(java.util.Collection<MapDecoration> icons) {
+ org.bukkit.entity.Player player = (org.bukkit.entity.Player) this.player.getBukkitEntity();
+ MapItemSavedData.this.decorations.forEach((name, mapIcon) -> {
+ // If this cursor is for a player check visibility with vanish system
+ org.bukkit.entity.Player other = org.bukkit.Bukkit.getPlayerExact(name); // Spigot
+ if (other == null || player.canSee(other)) {
+ icons.add(mapIcon);
+ }
+ });
+ }
+ private boolean shouldUseVanillaMap() {
+ return mapView.getRenderers().size() == 1 && mapView.getRenderers().get(0).getClass() == org.bukkit.craftbukkit.map.CraftMapRenderer.class;
+ }
+ // Paper end
public final Player player;
private boolean dirtyData = true;
private int minDirtyX;
@@ -0,0 +0,0 @@ public class MapItemSavedData extends SavedData {
@Nullable
Packet<?> nextUpdatePacket(MapId mapId) {
MapItemSavedData.MapPatch worldmap_c;
- org.bukkit.craftbukkit.map.RenderData render = MapItemSavedData.this.mapView.render((org.bukkit.craftbukkit.entity.CraftPlayer) this.player.getBukkitEntity()); // CraftBukkit
+ if (!this.dirtyData && this.tick % 5 != 0) { this.tick++; return null; } // Paper - this won't end up sending, so don't render it!
+ boolean vanillaMaps = shouldUseVanillaMap(); // Paper
+ org.bukkit.craftbukkit.map.RenderData render = !vanillaMaps ? MapItemSavedData.this.mapView.render((org.bukkit.craftbukkit.entity.CraftPlayer) this.player.getBukkitEntity()) : MapItemSavedData.this.vanillaRender; // CraftBukkit // Paper
if (this.dirtyData) {
this.dirtyData = false;
@@ -0,0 +0,0 @@ public class MapItemSavedData extends SavedData {
// CraftBukkit start
java.util.Collection<MapDecoration> icons = new java.util.ArrayList<MapDecoration>();
+ if (vanillaMaps) addSeenPlayers(icons); // Paper
+
for (org.bukkit.map.MapCursor cursor : render.cursors) {
if (cursor.isVisible()) {
icons.add(new MapDecoration(CraftMapCursor.CraftType.bukkitToMinecraftHolder(cursor.getType()), cursor.getX(), cursor.getY(), cursor.getDirection(), Optional.ofNullable(PaperAdventure.asVanilla(cursor.caption()))));

View file

@ -1,292 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Byteflux <byte@byteflux.net>
Date: Wed, 2 Mar 2016 02:17:54 -0600
Subject: [PATCH] Flat bedrock generator settings
== AT ==
public net.minecraft.world.level.levelgen.SurfaceRules$Condition
public net.minecraft.world.level.levelgen.SurfaceRules$Context
public net.minecraft.world.level.levelgen.SurfaceRules$Context blockX
public net.minecraft.world.level.levelgen.SurfaceRules$Context blockY
public net.minecraft.world.level.levelgen.SurfaceRules$Context blockZ
public net.minecraft.world.level.levelgen.SurfaceRules$Context context
public net.minecraft.world.level.levelgen.SurfaceRules$Context randomState
public net.minecraft.world.level.levelgen.SurfaceRules$LazyYCondition
public net.minecraft.world.level.levelgen.SurfaceRules$LazyCondition
public net.minecraft.world.level.levelgen.SurfaceRules$VerticalGradientConditionSource
public net.minecraft.world.level.levelgen.SurfaceRules$SurfaceRule
Co-authored-by: Noah van der Aa <ndvdaa@gmail.com>
diff --git a/src/main/java/io/papermc/paper/world/worldgen/OptionallyFlatBedrockConditionSource.java b/src/main/java/io/papermc/paper/world/worldgen/OptionallyFlatBedrockConditionSource.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/io/papermc/paper/world/worldgen/OptionallyFlatBedrockConditionSource.java
@@ -0,0 +0,0 @@
+package io.papermc.paper.world.worldgen;
+
+import com.mojang.serialization.Codec;
+import com.mojang.serialization.MapCodec;
+import com.mojang.serialization.codecs.RecordCodecBuilder;
+import net.minecraft.core.Registry;
+import net.minecraft.core.registries.BuiltInRegistries;
+import net.minecraft.core.registries.Registries;
+import net.minecraft.resources.ResourceKey;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.util.KeyDispatchDataCodec;
+import net.minecraft.util.Mth;
+import net.minecraft.util.RandomSource;
+import net.minecraft.world.level.levelgen.PositionalRandomFactory;
+import net.minecraft.world.level.levelgen.SurfaceRules;
+import net.minecraft.world.level.levelgen.VerticalAnchor;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+// Modelled off of SurfaceRules$VerticalGradientConditionSource
+@DefaultQualifier(NonNull.class)
+public record OptionallyFlatBedrockConditionSource(ResourceLocation randomName, VerticalAnchor trueAtAndBelow, VerticalAnchor falseAtAndAbove, boolean isRoof) implements SurfaceRules.ConditionSource {
+
+ private static final ResourceKey<MapCodec<? extends SurfaceRules.ConditionSource>> CODEC_RESOURCE_KEY = ResourceKey.create(
+ Registries.MATERIAL_CONDITION,
+ ResourceLocation.fromNamespaceAndPath(ResourceLocation.PAPER_NAMESPACE, "optionally_flat_bedrock_condition_source")
+ );
+ private static final KeyDispatchDataCodec<OptionallyFlatBedrockConditionSource> CODEC = KeyDispatchDataCodec.of(RecordCodecBuilder.mapCodec((instance) -> {
+ return instance.group(
+ ResourceLocation.CODEC.fieldOf("random_name").forGetter(OptionallyFlatBedrockConditionSource::randomName),
+ VerticalAnchor.CODEC.fieldOf("true_at_and_below").forGetter(OptionallyFlatBedrockConditionSource::trueAtAndBelow),
+ VerticalAnchor.CODEC.fieldOf("false_at_and_above").forGetter(OptionallyFlatBedrockConditionSource::falseAtAndAbove),
+ Codec.BOOL.fieldOf("is_roof").forGetter(OptionallyFlatBedrockConditionSource::isRoof)
+ ).apply(instance, OptionallyFlatBedrockConditionSource::new);
+ }));
+
+ public static void bootstrap() {
+ Registry.register(BuiltInRegistries.MATERIAL_CONDITION, CODEC_RESOURCE_KEY, CODEC.codec());
+ }
+
+ @Override
+ public KeyDispatchDataCodec<? extends SurfaceRules.ConditionSource> codec() {
+ return CODEC;
+ }
+
+ @Override
+ public SurfaceRules.Condition apply(final SurfaceRules.Context context) {
+ boolean hasFlatBedrock = context.context.getWorld().paperConfig().environment.generateFlatBedrock;
+ int tempTrueAtAndBelowY = this.trueAtAndBelow().resolveY(context.context);
+ int tempFalseAtAndAboveY = this.falseAtAndAbove().resolveY(context.context);
+
+ int flatYLevel = this.isRoof ? Math.max(tempFalseAtAndAboveY, tempTrueAtAndBelowY) - 1 : Math.min(tempFalseAtAndAboveY, tempTrueAtAndBelowY);
+ final int trueAtAndBelowY = hasFlatBedrock ? flatYLevel : tempTrueAtAndBelowY;
+ final int falseAtAndAboveY = hasFlatBedrock ? flatYLevel : tempFalseAtAndAboveY;
+
+ final PositionalRandomFactory positionalRandomFactory = context.randomState.getOrCreateRandomFactory(this.randomName());
+
+ class VerticalGradientCondition extends SurfaceRules.LazyYCondition {
+ VerticalGradientCondition(SurfaceRules.Context context) {
+ super(context);
+ }
+
+ @Override
+ protected boolean compute() {
+ int blockY = this.context.blockY;
+ if (blockY <= trueAtAndBelowY) {
+ return true;
+ } else if (blockY >= falseAtAndAboveY) {
+ return false;
+ } else {
+ double d = Mth.map(blockY, trueAtAndBelowY, falseAtAndAboveY, 1.0D, 0.0D);
+ RandomSource randomSource = positionalRandomFactory.at(this.context.blockX, blockY, this.context.blockZ);
+ return (double)randomSource.nextFloat() < d;
+ }
+ }
+ }
+
+ return new VerticalGradientCondition(context);
+ }
+}
diff --git a/src/main/java/net/minecraft/server/Bootstrap.java b/src/main/java/net/minecraft/server/Bootstrap.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/Bootstrap.java
+++ b/src/main/java/net/minecraft/server/Bootstrap.java
@@ -0,0 +0,0 @@ public class Bootstrap {
CauldronInteraction.bootStrap();
// Paper start
BuiltInRegistries.bootStrap(() -> {
+ io.papermc.paper.world.worldgen.OptionallyFlatBedrockConditionSource.bootstrap(); // Paper - Flat bedrock generator settings
});
// Paper end
CreativeModeTabs.validate();
diff --git a/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java b/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java
+++ b/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java
@@ -0,0 +0,0 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator {
@Override
public void buildSurface(WorldGenRegion region, StructureManager structures, RandomState noiseConfig, ChunkAccess chunk) {
if (!SharedConstants.debugVoidTerrain(chunk.getPos())) {
- WorldGenerationContext worldgenerationcontext = new WorldGenerationContext(this, region);
+ WorldGenerationContext worldgenerationcontext = new WorldGenerationContext(this, region, region.getMinecraftWorld()); // Paper - Flat bedrock generator settings
this.buildSurface(chunk, worldgenerationcontext, noiseConfig, structures, region.getBiomeManager(), region.registryAccess().lookupOrThrow(Registries.BIOME), Blender.of(region));
}
@@ -0,0 +0,0 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator {
return this.createNoiseChunk(ichunkaccess1, structureAccessor, Blender.of(chunkRegion), noiseConfig);
});
Aquifer aquifer = noisechunk.aquifer();
- CarvingContext carvingcontext = new CarvingContext(this, chunkRegion.registryAccess(), chunk.getHeightAccessorForGeneration(), noisechunk, noiseConfig, ((NoiseGeneratorSettings) this.settings.value()).surfaceRule());
+ CarvingContext carvingcontext = new CarvingContext(this, chunkRegion.registryAccess(), chunk.getHeightAccessorForGeneration(), noisechunk, noiseConfig, ((NoiseGeneratorSettings) this.settings.value()).surfaceRule(), chunkRegion.getMinecraftWorld()); // Paper - Flat bedrock generator settings
CarvingMask carvingmask = ((ProtoChunk) chunk).getOrCreateCarvingMask();
for (int j = -8; j <= 8; ++j) {
diff --git a/src/main/java/net/minecraft/world/level/levelgen/WorldGenerationContext.java b/src/main/java/net/minecraft/world/level/levelgen/WorldGenerationContext.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/levelgen/WorldGenerationContext.java
+++ b/src/main/java/net/minecraft/world/level/levelgen/WorldGenerationContext.java
@@ -0,0 +0,0 @@ import net.minecraft.world.level.chunk.ChunkGenerator;
public class WorldGenerationContext {
private final int minY;
private final int height;
+ private final @javax.annotation.Nullable net.minecraft.world.level.Level level; // Paper - Flat bedrock generator settings
- public WorldGenerationContext(ChunkGenerator generator, LevelHeightAccessor world) {
+ public WorldGenerationContext(ChunkGenerator generator, LevelHeightAccessor world) { this(generator, world, null); } // Paper - Flat bedrock generator settings
+ public WorldGenerationContext(ChunkGenerator generator, LevelHeightAccessor world, @org.jetbrains.annotations.Nullable net.minecraft.world.level.Level level) { // Paper - Flat bedrock generator settings
this.minY = Math.max(world.getMinY(), generator.getMinY());
this.height = Math.min(world.getHeight(), generator.getGenDepth());
+ this.level = level; // Paper - Flat bedrock generator settings
}
public int getMinGenY() {
@@ -0,0 +0,0 @@ public class WorldGenerationContext {
public int getGenDepth() {
return this.height;
}
+
+ // Paper start - Flat bedrock generator settings
+ public net.minecraft.world.level.Level getWorld() {
+ if (this.level == null) {
+ throw new NullPointerException("WorldGenerationContext was initialized without a Level, but WorldGenerationContext#getWorld was called");
+ }
+ return this.level;
+ }
+ // Paper end - Flat bedrock generator settings
}
diff --git a/src/main/java/net/minecraft/world/level/levelgen/carver/CarvingContext.java b/src/main/java/net/minecraft/world/level/levelgen/carver/CarvingContext.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/levelgen/carver/CarvingContext.java
+++ b/src/main/java/net/minecraft/world/level/levelgen/carver/CarvingContext.java
@@ -0,0 +0,0 @@ public class CarvingContext extends WorldGenerationContext {
LevelHeightAccessor heightLimitView,
NoiseChunk chunkNoiseSampler,
RandomState noiseConfig,
- SurfaceRules.RuleSource materialRule
+ SurfaceRules.RuleSource materialRule, @javax.annotation.Nullable net.minecraft.world.level.Level level // Paper - Flat bedrock generator settings
) {
- super(noiseChunkGenerator, heightLimitView);
+ super(noiseChunkGenerator, heightLimitView, level); // Paper - Flat bedrock generator settings
this.registryAccess = registryManager;
this.noiseChunk = chunkNoiseSampler;
this.randomState = noiseConfig;
diff --git a/src/main/java/net/minecraft/world/level/levelgen/placement/PlacementContext.java b/src/main/java/net/minecraft/world/level/levelgen/placement/PlacementContext.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/levelgen/placement/PlacementContext.java
+++ b/src/main/java/net/minecraft/world/level/levelgen/placement/PlacementContext.java
@@ -0,0 +0,0 @@ public class PlacementContext extends WorldGenerationContext {
private final Optional<PlacedFeature> topFeature;
public PlacementContext(WorldGenLevel world, ChunkGenerator generator, Optional<PlacedFeature> placedFeature) {
- super(generator, world);
+ super(generator, world, world.getLevel()); // Paper - Flat bedrock generator settings
this.level = world;
this.generator = generator;
this.topFeature = placedFeature;
diff --git a/src/main/resources/data/minecraft/worldgen/noise_settings/amplified.json b/src/main/resources/data/minecraft/worldgen/noise_settings/amplified.json
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/resources/data/minecraft/worldgen/noise_settings/amplified.json
+++ b/src/main/resources/data/minecraft/worldgen/noise_settings/amplified.json
@@ -0,0 +0,0 @@
{
"type": "minecraft:condition",
"if_true": {
- "type": "minecraft:vertical_gradient",
+ "type": "paper:optionally_flat_bedrock_condition_source",
+ "is_roof": false,
"false_at_and_above": {
"above_bottom": 5
},
diff --git a/src/main/resources/data/minecraft/worldgen/noise_settings/caves.json b/src/main/resources/data/minecraft/worldgen/noise_settings/caves.json
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/resources/data/minecraft/worldgen/noise_settings/caves.json
+++ b/src/main/resources/data/minecraft/worldgen/noise_settings/caves.json
@@ -0,0 +0,0 @@
"if_true": {
"type": "minecraft:not",
"invert": {
- "type": "minecraft:vertical_gradient",
+ "type": "paper:optionally_flat_bedrock_condition_source",
+ "is_roof": true,
"false_at_and_above": {
"below_top": 0
},
@@ -0,0 +0,0 @@
{
"type": "minecraft:condition",
"if_true": {
- "type": "minecraft:vertical_gradient",
+ "type": "paper:optionally_flat_bedrock_condition_source",
+ "is_roof": false,
"false_at_and_above": {
"above_bottom": 5
},
diff --git a/src/main/resources/data/minecraft/worldgen/noise_settings/large_biomes.json b/src/main/resources/data/minecraft/worldgen/noise_settings/large_biomes.json
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/resources/data/minecraft/worldgen/noise_settings/large_biomes.json
+++ b/src/main/resources/data/minecraft/worldgen/noise_settings/large_biomes.json
@@ -0,0 +0,0 @@
{
"type": "minecraft:condition",
"if_true": {
- "type": "minecraft:vertical_gradient",
+ "type": "paper:optionally_flat_bedrock_condition_source",
+ "is_roof": false,
"false_at_and_above": {
"above_bottom": 5
},
diff --git a/src/main/resources/data/minecraft/worldgen/noise_settings/nether.json b/src/main/resources/data/minecraft/worldgen/noise_settings/nether.json
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/resources/data/minecraft/worldgen/noise_settings/nether.json
+++ b/src/main/resources/data/minecraft/worldgen/noise_settings/nether.json
@@ -0,0 +0,0 @@
{
"type": "minecraft:condition",
"if_true": {
- "type": "minecraft:vertical_gradient",
+ "type": "paper:optionally_flat_bedrock_condition_source",
+ "is_roof": false,
"false_at_and_above": {
"above_bottom": 5
},
@@ -0,0 +0,0 @@
"if_true": {
"type": "minecraft:not",
"invert": {
- "type": "minecraft:vertical_gradient",
+ "type": "paper:optionally_flat_bedrock_condition_source",
+ "is_roof": true,
"false_at_and_above": {
"below_top": 0
},
diff --git a/src/main/resources/data/minecraft/worldgen/noise_settings/overworld.json b/src/main/resources/data/minecraft/worldgen/noise_settings/overworld.json
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/resources/data/minecraft/worldgen/noise_settings/overworld.json
+++ b/src/main/resources/data/minecraft/worldgen/noise_settings/overworld.json
@@ -0,0 +0,0 @@
{
"type": "minecraft:condition",
"if_true": {
- "type": "minecraft:vertical_gradient",
+ "type": "paper:optionally_flat_bedrock_condition_source",
+ "is_roof": false,
"false_at_and_above": {
"above_bottom": 5
},

View file

@ -1,775 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Fri, 13 May 2016 01:38:06 -0400
Subject: [PATCH] Entity Activation Range 2.0
Optimizes performance of Activation Range
Adds many new configurations and a new wake up inactive system
Fixes and adds new Immunities to improve gameplay behavior
Adds water Mobs to activation range config and nerfs fish
Adds flying monsters to control ghast and phantoms
Adds villagers as separate config
== AT ==
public net.minecraft.world.entity.Entity isInsidePortal
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -0,0 +0,0 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
public void tickNonPassenger(Entity entity) {
// Spigot start
- if (!org.spigotmc.ActivationRange.checkIfActive(entity)) {
+ /*if (!org.spigotmc.ActivationRange.checkIfActive(entity)) { // Paper - comment out EAR 2
entity.tickCount++;
entity.inactiveTick();
return;
- }
+ }*/ // Paper - comment out EAR 2
// Spigot end
entity.setOldPosAndRot();
ProfilerFiller gameprofilerfiller = Profiler.get();
@@ -0,0 +0,0 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
return BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString();
});
gameprofilerfiller.incrementCounter("tickNonPassenger");
+ final boolean isActive = org.spigotmc.ActivationRange.checkIfActive(entity); // Paper - EAR 2
+ if (isActive) { // Paper - EAR 2
entity.tick();
entity.postTick(); // CraftBukkit
+ } else { entity.inactiveTick(); } // Paper - EAR 2
gameprofilerfiller.pop();
Iterator iterator = entity.getPassengers().iterator();
while (iterator.hasNext()) {
Entity entity1 = (Entity) iterator.next();
- this.tickPassenger(entity, entity1);
+ this.tickPassenger(entity, entity1, isActive); // Paper - EAR 2
}
}
- private void tickPassenger(Entity vehicle, Entity passenger) {
+ private void tickPassenger(Entity vehicle, Entity passenger, boolean isActive) { // Paper - EAR 2
if (!passenger.isRemoved() && passenger.getVehicle() == vehicle) {
if (passenger instanceof Player || this.entityTickList.contains(passenger)) {
passenger.setOldPosAndRot();
@@ -0,0 +0,0 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
return BuiltInRegistries.ENTITY_TYPE.getKey(passenger.getType()).toString();
});
gameprofilerfiller.incrementCounter("tickPassenger");
+ // Paper start - EAR 2
+ if (isActive) {
passenger.rideTick();
passenger.postTick(); // CraftBukkit
+ } else {
+ passenger.setDeltaMovement(Vec3.ZERO);
+ passenger.inactiveTick();
+ // copied from inside of if (isPassenger()) of passengerTick, but that ifPassenger is unnecessary
+ vehicle.positionRider(passenger);
+ }
+ // Paper end - EAR 2
gameprofilerfiller.pop();
Iterator iterator = passenger.getPassengers().iterator();
while (iterator.hasNext()) {
Entity entity2 = (Entity) iterator.next();
- this.tickPassenger(passenger, entity2);
+ this.tickPassenger(passenger, entity2, isActive); // Paper - EAR 2
}
}
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -0,0 +0,0 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
// Spigot end
protected int numCollisions = 0; // Paper - Cap entity collisions
public boolean fromNetherPortal; // Paper - Add option to nerf pigmen from nether portals
+ public long activatedImmunityTick = Integer.MIN_VALUE; // Paper - EAR
+ public boolean isTemporarilyActive; // Paper - EAR
public boolean spawnedViaMobSpawner; // Paper - Yes this name is similar to above, upstream took the better one
// Paper start - Entity origin API
@javax.annotation.Nullable
@@ -0,0 +0,0 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
} else {
this.wasOnFire = this.isOnFire();
if (type == MoverType.PISTON) {
+ this.activatedTick = Math.max(this.activatedTick, MinecraftServer.currentTick + 20); // Paper - EAR 2
+ this.activatedImmunityTick = Math.max(this.activatedImmunityTick, MinecraftServer.currentTick + 20); // Paper - EAR 2
movement = this.limitPistonMovement(movement);
if (movement.equals(Vec3.ZERO)) {
return;
@@ -0,0 +0,0 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
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) {
+ setDeltaMovement(Vec3.ZERO);
+ gameprofilerfiller.pop();
+ return;
+ }
+ // Paper end
movement = this.maybeBackOffFromEdge(movement, type);
Vec3 vec3d1 = this.collide(movement);
diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/Mob.java
+++ b/src/main/java/net/minecraft/world/entity/Mob.java
@@ -0,0 +0,0 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab
return this.lookControl;
}
+ // Paper start
+ @Override
+ public void inactiveTick() {
+ super.inactiveTick();
+ if (this.goalSelector.inactiveTick()) {
+ this.goalSelector.tick();
+ }
+ if (this.targetSelector.inactiveTick()) {
+ this.targetSelector.tick();
+ }
+ }
+ // Paper end
+
public MoveControl getMoveControl() {
Entity entity = this.getControlledVehicle();
diff --git a/src/main/java/net/minecraft/world/entity/PathfinderMob.java b/src/main/java/net/minecraft/world/entity/PathfinderMob.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/PathfinderMob.java
+++ b/src/main/java/net/minecraft/world/entity/PathfinderMob.java
@@ -0,0 +0,0 @@ public abstract class PathfinderMob extends Mob {
super(type, world);
}
+ public BlockPos movingTarget; public BlockPos getMovingTarget() { return movingTarget; } // Paper
+
public float getWalkTargetValue(BlockPos pos) {
return this.getWalkTargetValue(pos, this.level());
}
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
@@ -0,0 +0,0 @@ public class GoalSelector {
private final Map<Goal.Flag, WrappedGoal> lockedFlags = new EnumMap<>(Goal.Flag.class);
private final Set<WrappedGoal> availableGoals = new ObjectLinkedOpenHashSet<>();
private final EnumSet<Goal.Flag> 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));
@@ -0,0 +0,0 @@ public class GoalSelector {
this.availableGoals.removeIf(goal -> predicate.test(goal.getGoal()));
}
+ // Paper start - EAR 2
+ public boolean inactiveTick() {
+ this.curRate++;
+ return this.curRate % 3 == 0; // TODO newGoalRate was already unused in 1.20.4, check if this is correct
+ }
+ public boolean hasTasks() {
+ for (WrappedGoal task : this.availableGoals) {
+ if (task.isRunning()) {
+ return true;
+ }
+ }
+ return false;
+ }
+ // Paper end - EAR 2
public void removeGoal(Goal goal) {
for (WrappedGoal wrappedGoal : this.availableGoals) {
if (wrappedGoal.getGoal() == goal && wrappedGoal.isRunning()) {
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
@@ -0,0 +0,0 @@ public abstract class MoveToBlockGoal extends Goal {
public MoveToBlockGoal(PathfinderMob mob, double speed, int range) {
this(mob, speed, range, 1);
}
+ // Paper start - activation range improvements
+ @Override
+ public void stop() {
+ super.stop();
+ this.blockPos = BlockPos.ZERO;
+ this.mob.movingTarget = null;
+ }
+ // Paper end
public MoveToBlockGoal(PathfinderMob mob, double speed, int range, int maxYDifference) {
this.mob = mob;
@@ -0,0 +0,0 @@ public abstract class MoveToBlockGoal extends Goal {
mutableBlockPos.setWithOffset(blockPos, m, k - 1, n);
if (this.mob.isWithinRestriction(mutableBlockPos) && this.isValidTarget(this.mob.level(), mutableBlockPos)) {
this.blockPos = mutableBlockPos;
+ this.mob.movingTarget = mutableBlockPos == BlockPos.ZERO ? null : mutableBlockPos.immutable(); // Paper
return true;
}
}
diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/npc/Villager.java
+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java
@@ -0,0 +0,0 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
@Override
public void inactiveTick() {
// SPIGOT-3874, SPIGOT-3894, SPIGOT-3846, SPIGOT-5286 :(
- if (this.level().spigotConfig.tickInactiveVillagers && this.isEffectiveAi()) {
- this.customServerAiStep((ServerLevel) this.level());
+ // Paper start
+ if (this.getUnhappyCounter() > 0) {
+ this.setUnhappyCounter(this.getUnhappyCounter() - 1);
+ }
+ if (this.isEffectiveAi()) {
+ if (this.level().spigotConfig.tickInactiveVillagers) {
+ this.customServerAiStep(this.level().getMinecraftWorld());
+ } else {
+ this.customServerAiStep(this.level().getMinecraftWorld(), true);
+ }
}
+ maybeDecayGossip();
+ // Paper end
super.inactiveTick();
}
// Spigot End
@Override
protected void customServerAiStep(ServerLevel world) {
+ // Paper start - EAR 2
+ this.customServerAiStep(world, false);
+ }
+ protected void customServerAiStep(ServerLevel world, final boolean inactive) {
+ // Paper end - EAR 2
ProfilerFiller gameprofilerfiller = Profiler.get();
gameprofilerfiller.push("villagerBrain");
- this.getBrain().tick(world, this);
+ if (!inactive) this.getBrain().tick(world, this);
gameprofilerfiller.pop();
if (this.assignProfessionWhenSpawned) {
this.assignProfessionWhenSpawned = false;
@@ -0,0 +0,0 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
this.lastTradedPlayer = null;
}
- if (!this.isNoAi() && this.random.nextInt(100) == 0) {
+ if (!inactive && !this.isNoAi() && this.random.nextInt(100) == 0) { // Paper - EAR 2
Raid raid = world.getRaidAt(this.blockPosition());
if (raid != null && raid.isActive() && !raid.isOver()) {
@@ -0,0 +0,0 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
if (this.getVillagerData().getProfession() == VillagerProfession.NONE && this.isTrading()) {
this.stopTrading();
}
+ if (inactive) return; // Paper - EAR 2
super.customServerAiStep(world);
}
diff --git a/src/main/java/net/minecraft/world/entity/vehicle/MinecartHopper.java b/src/main/java/net/minecraft/world/entity/vehicle/MinecartHopper.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/vehicle/MinecartHopper.java
+++ b/src/main/java/net/minecraft/world/entity/vehicle/MinecartHopper.java
@@ -0,0 +0,0 @@ public class MinecartHopper extends AbstractMinecartContainer implements Hopper
if (bl != this.isEnabled()) {
this.setEnabled(bl);
}
+ this.immunize(); // Paper
}
public boolean isEnabled() {
@@ -0,0 +0,0 @@ public class MinecartHopper extends AbstractMinecartContainer implements Hopper
public boolean suckInItems() {
if (HopperBlockEntity.suckInItems(this.level(), this)) {
+ this.immunize(); // Paper
return true;
} else {
for (ItemEntity itemEntity : this.level()
.getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate(0.25, 0.0, 0.25), EntitySelector.ENTITY_STILL_ALIVE)) {
if (HopperBlockEntity.addItem(this, itemEntity)) {
+ this.immunize(); // Paper
return true;
}
}
@@ -0,0 +0,0 @@ public class MinecartHopper extends AbstractMinecartContainer implements Hopper
public AbstractContainerMenu createMenu(int syncId, Inventory playerInventory) {
return new HopperMenu(syncId, playerInventory, this);
}
+
+ // Paper start
+ public void immunize() {
+ this.activatedImmunityTick = Math.max(this.activatedImmunityTick, net.minecraft.server.MinecraftServer.currentTick + 20);
+ }
+ // Paper end
+
}
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -0,0 +0,0 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
public Map<BlockPos, BlockEntity> capturedTileEntities = new java.util.LinkedHashMap<>(); // Paper - Retain block place order when capturing blockstates
public List<ItemEntity> captureDrops;
public final it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap<SpawnCategory> ticksPerSpawnCategory = new it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap<>();
+ // Paper start
+ public int wakeupInactiveRemainingAnimals;
+ public int wakeupInactiveRemainingFlying;
+ public int wakeupInactiveRemainingMonsters;
+ public int wakeupInactiveRemainingVillagers;
+ // Paper end
public boolean populating;
public final org.spigotmc.SpigotWorldConfig spigotConfig; // Spigot
// Paper start - add paper world config
diff --git a/src/main/java/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java b/src/main/java/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
@@ -0,0 +0,0 @@ public class PistonMovingBlockEntity extends BlockEntity {
}
entity.setDeltaMovement(e, g, h);
+ // Paper - EAR items stuck in 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);
+ // Paper end
break;
}
}
diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/spigotmc/ActivationRange.java
+++ b/src/main/java/org/spigotmc/ActivationRange.java
@@ -0,0 +0,0 @@
package org.spigotmc;
+import net.minecraft.core.BlockPos;
import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.ExperienceOrb;
+import net.minecraft.world.entity.FlyingMob;
import net.minecraft.world.entity.LightningBolt;
import net.minecraft.world.entity.LivingEntity;
+import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.PathfinderMob;
+import net.minecraft.world.entity.ai.Brain;
import net.minecraft.world.entity.ambient.AmbientCreature;
import net.minecraft.world.entity.animal.Animal;
+import net.minecraft.world.entity.animal.Bee;
import net.minecraft.world.entity.animal.Sheep;
+import net.minecraft.world.entity.animal.WaterAnimal;
+import net.minecraft.world.entity.animal.horse.Llama;
import net.minecraft.world.entity.boss.EnderDragonPart;
import net.minecraft.world.entity.boss.enderdragon.EndCrystal;
import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
@@ -0,0 +0,0 @@ import net.minecraft.world.entity.boss.wither.WitherBoss;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.item.PrimedTnt;
import net.minecraft.world.entity.monster.Creeper;
-import net.minecraft.world.entity.monster.Monster;
-import net.minecraft.world.entity.monster.Slime;
+import net.minecraft.world.entity.monster.Enemy;
+import net.minecraft.world.entity.monster.Pillager;
import net.minecraft.world.entity.npc.Villager;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.AbstractArrow;
import net.minecraft.world.entity.projectile.AbstractHurtingProjectile;
+import net.minecraft.world.entity.projectile.EyeOfEnder;
import net.minecraft.world.entity.projectile.FireworkRocketEntity;
import net.minecraft.world.entity.projectile.ThrowableProjectile;
import net.minecraft.world.entity.projectile.ThrownTrident;
@@ -0,0 +0,0 @@ public class ActivationRange
AABB boundingBox = new AABB( 0, 0, 0, 0, 0, 0 );
}
+ // Paper start
+
+ static net.minecraft.world.entity.schedule.Activity[] VILLAGER_PANIC_IMMUNITIES = {
+ net.minecraft.world.entity.schedule.Activity.HIDE,
+ net.minecraft.world.entity.schedule.Activity.PRE_RAID,
+ net.minecraft.world.entity.schedule.Activity.RAID,
+ net.minecraft.world.entity.schedule.Activity.PANIC
+ };
+
+ private static int checkInactiveWakeup(Entity entity) {
+ Level world = entity.level();
+ SpigotWorldConfig config = world.spigotConfig;
+ long inactiveFor = MinecraftServer.currentTick - entity.activatedTick;
+ if (entity.activationType == ActivationType.VILLAGER) {
+ if (inactiveFor > config.wakeUpInactiveVillagersEvery && world.wakeupInactiveRemainingVillagers > 0) {
+ world.wakeupInactiveRemainingVillagers--;
+ return config.wakeUpInactiveVillagersFor;
+ }
+ } else if (entity.activationType == ActivationType.ANIMAL) {
+ if (inactiveFor > config.wakeUpInactiveAnimalsEvery && world.wakeupInactiveRemainingAnimals > 0) {
+ world.wakeupInactiveRemainingAnimals--;
+ return config.wakeUpInactiveAnimalsFor;
+ }
+ } else if (entity.activationType == ActivationType.FLYING_MONSTER) {
+ if (inactiveFor > config.wakeUpInactiveFlyingEvery && world.wakeupInactiveRemainingFlying > 0) {
+ world.wakeupInactiveRemainingFlying--;
+ return config.wakeUpInactiveFlyingFor;
+ }
+ } else if (entity.activationType == ActivationType.MONSTER || entity.activationType == ActivationType.RAIDER) {
+ if (inactiveFor > config.wakeUpInactiveMonstersEvery && world.wakeupInactiveRemainingMonsters > 0) {
+ world.wakeupInactiveRemainingMonsters--;
+ return config.wakeUpInactiveMonstersFor;
+ }
+ }
+ return -1;
+ }
+ // Paper end
static AABB maxBB = new AABB( 0, 0, 0, 0, 0, 0 );
@@ -0,0 +0,0 @@ public class ActivationRange
*/
public static ActivationType initializeEntityActivationType(Entity entity)
{
+ if (entity instanceof WaterAnimal) { return ActivationType.WATER; } // Paper
+ else if (entity instanceof Villager) { return ActivationType.VILLAGER; } // Paper
+ else if (entity instanceof FlyingMob && entity instanceof Enemy) { return ActivationType.FLYING_MONSTER; } // Paper - doing & Monster incase Flying no longer includes monster in future
if ( entity instanceof Raider )
{
return ActivationType.RAIDER;
- } else if ( entity instanceof Monster || entity instanceof Slime )
+ } else if ( entity instanceof Enemy ) // Paper - correct monster check
{
return ActivationType.MONSTER;
} else if ( entity instanceof PathfinderMob || entity instanceof AmbientCreature )
@@ -0,0 +0,0 @@ public class ActivationRange
*/
public static boolean initializeEntityActivationState(Entity entity, SpigotWorldConfig config)
{
- if ( ( entity.activationType == ActivationType.MISC && config.miscActivationRange == 0 )
- || ( entity.activationType == ActivationType.RAIDER && config.raiderActivationRange == 0 )
- || ( entity.activationType == ActivationType.ANIMAL && config.animalActivationRange == 0 )
- || ( entity.activationType == ActivationType.MONSTER && config.monsterActivationRange == 0 )
+ if ( ( entity.activationType == ActivationType.MISC && config.miscActivationRange <= 0 )
+ || ( entity.activationType == ActivationType.RAIDER && config.raiderActivationRange <= 0 )
+ || ( entity.activationType == ActivationType.ANIMAL && config.animalActivationRange <= 0 )
+ || ( entity.activationType == ActivationType.MONSTER && config.monsterActivationRange <= 0 )
+ || ( entity.activationType == ActivationType.VILLAGER && config.villagerActivationRange <= 0 ) // Paper
+ || ( entity.activationType == ActivationType.WATER && config.waterActivationRange <= 0 ) // Paper
+ || ( entity.activationType == ActivationType.FLYING_MONSTER && config.flyingMonsterActivationRange <= 0 ) // Paper
+ || entity instanceof EyeOfEnder // Paper
|| entity instanceof Player
|| entity instanceof ThrowableProjectile
|| entity instanceof EnderDragon
@@ -0,0 +0,0 @@ public class ActivationRange
final int raiderActivationRange = world.spigotConfig.raiderActivationRange;
final int animalActivationRange = world.spigotConfig.animalActivationRange;
final int monsterActivationRange = world.spigotConfig.monsterActivationRange;
+ // Paper start
+ final int waterActivationRange = world.spigotConfig.waterActivationRange;
+ final int flyingActivationRange = world.spigotConfig.flyingMonsterActivationRange;
+ final int villagerActivationRange = world.spigotConfig.villagerActivationRange;
+ world.wakeupInactiveRemainingAnimals = Math.min(world.wakeupInactiveRemainingAnimals + 1, world.spigotConfig.wakeUpInactiveAnimals);
+ world.wakeupInactiveRemainingVillagers = Math.min(world.wakeupInactiveRemainingVillagers + 1, world.spigotConfig.wakeUpInactiveVillagers);
+ world.wakeupInactiveRemainingMonsters = Math.min(world.wakeupInactiveRemainingMonsters + 1, world.spigotConfig.wakeUpInactiveMonsters);
+ world.wakeupInactiveRemainingFlying = Math.min(world.wakeupInactiveRemainingFlying + 1, world.spigotConfig.wakeUpInactiveFlying);
+ final ServerChunkCache chunkProvider = (ServerChunkCache) world.getChunkSource();
+ // Paper end
int maxRange = Math.max( monsterActivationRange, animalActivationRange );
maxRange = Math.max( maxRange, raiderActivationRange );
maxRange = Math.max( maxRange, miscActivationRange );
+ // Paper start
+ maxRange = Math.max( maxRange, flyingActivationRange );
+ maxRange = Math.max( maxRange, waterActivationRange );
+ maxRange = Math.max( maxRange, villagerActivationRange );
+ // Paper end
maxRange = Math.min( ( world.spigotConfig.simulationDistance << 4 ) - 8, maxRange );
for ( Player player : world.players() )
@@ -0,0 +0,0 @@ public class ActivationRange
continue;
}
- ActivationRange.maxBB = player.getBoundingBox().inflate( maxRange, 256, maxRange );
- ActivationType.MISC.boundingBox = player.getBoundingBox().inflate( miscActivationRange, 256, miscActivationRange );
- ActivationType.RAIDER.boundingBox = player.getBoundingBox().inflate( raiderActivationRange, 256, raiderActivationRange );
- ActivationType.ANIMAL.boundingBox = player.getBoundingBox().inflate( animalActivationRange, 256, animalActivationRange );
- ActivationType.MONSTER.boundingBox = player.getBoundingBox().inflate( monsterActivationRange, 256, monsterActivationRange );
+ // Paper start
+ int worldHeight = world.getHeight();
+ ActivationRange.maxBB = player.getBoundingBox().inflate( maxRange, worldHeight, maxRange );
+ ActivationType.MISC.boundingBox = player.getBoundingBox().inflate( miscActivationRange, worldHeight, miscActivationRange );
+ ActivationType.RAIDER.boundingBox = player.getBoundingBox().inflate( raiderActivationRange, worldHeight, raiderActivationRange );
+ ActivationType.ANIMAL.boundingBox = player.getBoundingBox().inflate( animalActivationRange, worldHeight, animalActivationRange );
+ ActivationType.MONSTER.boundingBox = player.getBoundingBox().inflate( monsterActivationRange, worldHeight, monsterActivationRange );
+ ActivationType.WATER.boundingBox = player.getBoundingBox().inflate( waterActivationRange, worldHeight, waterActivationRange );
+ ActivationType.FLYING_MONSTER.boundingBox = player.getBoundingBox().inflate( flyingActivationRange, worldHeight, flyingActivationRange );
+ ActivationType.VILLAGER.boundingBox = player.getBoundingBox().inflate( villagerActivationRange, worldHeight, villagerActivationRange );
+ // Paper end
- world.getEntities().get(ActivationRange.maxBB, ActivationRange::activateEntity);
+ // Paper start
+ java.util.List<Entity> entities = world.getEntities((Entity)null, ActivationRange.maxBB, null);
+ boolean tickMarkers = world.paperConfig().entities.markers.tick; // Paper - Configurable marker ticking
+ for (Entity entity : entities) {
+ // Paper start - Configurable marker ticking
+ if (!tickMarkers && entity instanceof net.minecraft.world.entity.Marker) {
+ continue;
+ }
+ // Paper end - Configurable marker ticking
+ ActivationRange.activateEntity(entity);
+ }
+ // Paper end
}
}
@@ -0,0 +0,0 @@ public class ActivationRange
* @param entity
* @return
*/
- public static boolean checkEntityImmunities(Entity entity)
+ public static int checkEntityImmunities(Entity entity) // Paper - return # of ticks to get immunity
{
+ // Paper start
+ SpigotWorldConfig config = entity.level().spigotConfig;
+ int inactiveWakeUpImmunity = checkInactiveWakeup(entity);
+ if (inactiveWakeUpImmunity > -1) {
+ return inactiveWakeUpImmunity;
+ }
+ if (entity.getRemainingFireTicks() > 0) {
+ return 2;
+ }
+ if (entity.activatedImmunityTick >= MinecraftServer.currentTick) {
+ return 1;
+ }
+ long inactiveFor = MinecraftServer.currentTick - entity.activatedTick;
+ // Paper end
// quick checks.
- if ( entity.wasTouchingWater || entity.getRemainingFireTicks() > 0 )
+ if ( (entity.activationType != ActivationType.WATER && entity.wasTouchingWater && entity.isPushedByFluid()) ) // Paper
{
- return true;
+ return 100; // Paper
+ }
+ // Paper start
+ if ( !entity.onGround() || entity.getDeltaMovement().horizontalDistanceSqr() > 9.999999747378752E-6D )
+ {
+ return 100;
}
+ // Paper end
if ( !( entity instanceof AbstractArrow ) )
{
- if ( !entity.onGround() || !entity.passengers.isEmpty() || entity.isPassenger() )
+ if ( (!entity.onGround() && !(entity instanceof FlyingMob)) ) // Paper - remove passengers logic
{
- return true;
+ return 10; // Paper
}
} else if ( !( (AbstractArrow) entity ).isInGround() )
{
- return true;
+ return 1; // Paper
}
// special cases.
if ( entity instanceof LivingEntity )
{
LivingEntity living = (LivingEntity) entity;
- if ( /*TODO: Missed mapping? living.attackTicks > 0 || */ living.hurtTime > 0 || living.activeEffects.size() > 0 )
+ if ( living.onClimbable() || living.jumping || living.hurtTime > 0 || living.activeEffects.size() > 0 || living.isFreezing()) // Paper
{
- return true;
+ return 1; // Paper
}
- if ( entity instanceof PathfinderMob && ( (PathfinderMob) entity ).getTarget() != null )
+ if ( entity instanceof Mob && ((Mob) entity ).getTarget() != null) // Paper
{
- return true;
+ return 20; // Paper
}
- if ( entity instanceof Villager && ( (Villager) entity ).canBreed() )
+ // Paper start
+ if (entity instanceof Bee) {
+ Bee bee = (Bee)entity;
+ BlockPos movingTarget = bee.getMovingTarget();
+ if (bee.isAngry() ||
+ (bee.getHivePos() != null && bee.getHivePos().equals(movingTarget)) ||
+ (bee.getSavedFlowerPos() != null && bee.getSavedFlowerPos().equals(movingTarget))
+ ) {
+ return 20;
+ }
+ }
+ if ( entity instanceof Villager ) {
+ Brain<Villager> behaviorController = ((Villager) entity).getBrain();
+
+ if (config.villagersActiveForPanic) {
+ for (net.minecraft.world.entity.schedule.Activity activity : VILLAGER_PANIC_IMMUNITIES) {
+ if (behaviorController.isActive(activity)) {
+ return 20*5;
+ }
+ }
+ }
+
+ if (config.villagersWorkImmunityAfter > 0 && inactiveFor >= config.villagersWorkImmunityAfter) {
+ if (behaviorController.isActive(net.minecraft.world.entity.schedule.Activity.WORK)) {
+ return config.villagersWorkImmunityFor;
+ }
+ }
+ }
+ if ( entity instanceof Llama && ( (Llama) entity ).inCaravan() )
{
- return true;
+ return 1;
}
+ // Paper end
if ( entity instanceof Animal )
{
Animal animal = (Animal) entity;
if ( animal.isBaby() || animal.isInLove() )
{
- return true;
+ return 5; // Paper
}
if ( entity instanceof Sheep && ( (Sheep) entity ).isSheared() )
{
- return true;
+ return 1; // Paper
}
}
if (entity instanceof Creeper && ((Creeper) entity).isIgnited()) { // isExplosive
- return true;
+ return 20; // Paper
+ }
+ // Paper start
+ if (entity instanceof Mob && ((Mob) entity).targetSelector.hasTasks() ) {
+ return 0;
}
+ if (entity instanceof Pillager) {
+ Pillager pillager = (Pillager) entity;
+ // TODO:?
+ }
+ // Paper end
}
// SPIGOT-6644: Otherwise the target refresh tick will be missed
if (entity instanceof ExperienceOrb) {
- return true;
+ return 20; // Paper
}
- return false;
+ return -1; // Paper
}
/**
@@ -0,0 +0,0 @@ public class ActivationRange
if (entity instanceof FireworkRocketEntity || (entity instanceof ItemEntity && (entity.tickCount + entity.getId()) % 4 == 0)) { // Paper - Needed for item gravity, see ItemEntity tick
return true;
}
+ // Paper start - special case always immunities
+ // immunize brand new entities, dead entities, and portal scenarios
+ if (entity.defaultActivationState || entity.tickCount < 20*10 || !entity.isAlive() || (entity.portalProcess != null && !entity.portalProcess.hasExpired()) || entity.portalCooldown > 0) {
+ return true;
+ }
+ // immunize leashed entities
+ if (entity instanceof Mob && ((Mob)entity).getLeashHolder() instanceof Player) {
+ return true;
+ }
+ // Paper end
- boolean isActive = entity.activatedTick >= MinecraftServer.currentTick || entity.defaultActivationState;
+ boolean isActive = entity.activatedTick >= MinecraftServer.currentTick;
+ entity.isTemporarilyActive = false; // Paper
// Should this entity tick?
if ( !isActive )
@@ -0,0 +0,0 @@ public class ActivationRange
if ( ( MinecraftServer.currentTick - entity.activatedTick - 1 ) % 20 == 0 )
{
// Check immunities every 20 ticks.
- if ( ActivationRange.checkEntityImmunities( entity ) )
- {
- // Triggered some sort of immunity, give 20 full ticks before we check again.
- entity.activatedTick = MinecraftServer.currentTick + 20;
+ // Paper start
+ int immunity = checkEntityImmunities(entity);
+ if (immunity >= 0) {
+ entity.activatedTick = MinecraftServer.currentTick + immunity;
+ } else {
+ entity.isTemporarilyActive = true;
}
+ // Paper end
isActive = true;
}
}
diff --git a/src/main/java/org/spigotmc/SpigotWorldConfig.java b/src/main/java/org/spigotmc/SpigotWorldConfig.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/spigotmc/SpigotWorldConfig.java
+++ b/src/main/java/org/spigotmc/SpigotWorldConfig.java
@@ -0,0 +0,0 @@ public class SpigotWorldConfig
public int monsterActivationRange = 32;
public int raiderActivationRange = 64;
public int miscActivationRange = 16;
+ // Paper start
+ public int flyingMonsterActivationRange = 32;
+ public int waterActivationRange = 16;
+ public int villagerActivationRange = 32;
+ public int wakeUpInactiveAnimals = 4;
+ public int wakeUpInactiveAnimalsEvery = 60*20;
+ public int wakeUpInactiveAnimalsFor = 5*20;
+ public int wakeUpInactiveMonsters = 8;
+ public int wakeUpInactiveMonstersEvery = 20*20;
+ public int wakeUpInactiveMonstersFor = 5*20;
+ public int wakeUpInactiveVillagers = 4;
+ public int wakeUpInactiveVillagersEvery = 30*20;
+ public int wakeUpInactiveVillagersFor = 5*20;
+ public int wakeUpInactiveFlying = 8;
+ public int wakeUpInactiveFlyingEvery = 10*20;
+ public int wakeUpInactiveFlyingFor = 5*20;
+ public int villagersWorkImmunityAfter = 5*20;
+ public int villagersWorkImmunityFor = 20;
+ public boolean villagersActiveForPanic = true;
+ // Paper end
public boolean tickInactiveVillagers = true;
public boolean ignoreSpectatorActivation = false;
private void activationRange()
{
+ boolean hasAnimalsConfig = config.getInt("entity-activation-range.animals", this.animalActivationRange) != this.animalActivationRange; // Paper
this.animalActivationRange = this.getInt( "entity-activation-range.animals", this.animalActivationRange );
this.monsterActivationRange = this.getInt( "entity-activation-range.monsters", this.monsterActivationRange );
this.raiderActivationRange = this.getInt( "entity-activation-range.raiders", this.raiderActivationRange );
this.miscActivationRange = this.getInt( "entity-activation-range.misc", this.miscActivationRange );
+ // Paper start
+ this.waterActivationRange = this.getInt( "entity-activation-range.water", this.waterActivationRange );
+ this.villagerActivationRange = this.getInt( "entity-activation-range.villagers", hasAnimalsConfig ? this.animalActivationRange : this.villagerActivationRange );
+ this.flyingMonsterActivationRange = this.getInt( "entity-activation-range.flying-monsters", this.flyingMonsterActivationRange );
+
+ this.wakeUpInactiveAnimals = this.getInt("entity-activation-range.wake-up-inactive.animals-max-per-tick", this.wakeUpInactiveAnimals);
+ this.wakeUpInactiveAnimalsEvery = this.getInt("entity-activation-range.wake-up-inactive.animals-every", this.wakeUpInactiveAnimalsEvery);
+ this.wakeUpInactiveAnimalsFor = this.getInt("entity-activation-range.wake-up-inactive.animals-for", this.wakeUpInactiveAnimalsFor);
+
+ this.wakeUpInactiveMonsters = this.getInt("entity-activation-range.wake-up-inactive.monsters-max-per-tick", this.wakeUpInactiveMonsters);
+ this.wakeUpInactiveMonstersEvery = this.getInt("entity-activation-range.wake-up-inactive.monsters-every", this.wakeUpInactiveMonstersEvery);
+ this.wakeUpInactiveMonstersFor = this.getInt("entity-activation-range.wake-up-inactive.monsters-for", this.wakeUpInactiveMonstersFor);
+
+ this.wakeUpInactiveVillagers = this.getInt("entity-activation-range.wake-up-inactive.villagers-max-per-tick", this.wakeUpInactiveVillagers);
+ this.wakeUpInactiveVillagersEvery = this.getInt("entity-activation-range.wake-up-inactive.villagers-every", this.wakeUpInactiveVillagersEvery);
+ this.wakeUpInactiveVillagersFor = this.getInt("entity-activation-range.wake-up-inactive.villagers-for", this.wakeUpInactiveVillagersFor);
+
+ this.wakeUpInactiveFlying = this.getInt("entity-activation-range.wake-up-inactive.flying-monsters-max-per-tick", this.wakeUpInactiveFlying);
+ this.wakeUpInactiveFlyingEvery = this.getInt("entity-activation-range.wake-up-inactive.flying-monsters-every", this.wakeUpInactiveFlyingEvery);
+ this.wakeUpInactiveFlyingFor = this.getInt("entity-activation-range.wake-up-inactive.flying-monsters-for", this.wakeUpInactiveFlyingFor);
+
+ this.villagersWorkImmunityAfter = this.getInt( "entity-activation-range.villagers-work-immunity-after", this.villagersWorkImmunityAfter );
+ this.villagersWorkImmunityFor = this.getInt( "entity-activation-range.villagers-work-immunity-for", this.villagersWorkImmunityFor );
+ this.villagersActiveForPanic = this.getBoolean( "entity-activation-range.villagers-active-for-panic", this.villagersActiveForPanic );
+ // Paper end
this.tickInactiveVillagers = this.getBoolean( "entity-activation-range.tick-inactive-villagers", this.tickInactiveVillagers );
this.ignoreSpectatorActivation = this.getBoolean( "entity-activation-range.ignore-spectators", this.ignoreSpectatorActivation );
this.log( "Entity Activation Range: An " + this.animalActivationRange + " / Mo " + this.monsterActivationRange + " / Ra " + this.raiderActivationRange + " / Mi " + this.miscActivationRange + " / Tiv " + this.tickInactiveVillagers + " / Isa " + this.ignoreSpectatorActivation );

File diff suppressed because it is too large Load diff

View file

@ -1,387 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Andrew Steinborn <git@steinborn.me>
Date: Mon, 26 Jul 2021 02:15:17 -0400
Subject: [PATCH] Use Velocity compression and cipher natives
== AT ==
private-f net.minecraft.network.CompressionDecoder inflater
diff --git a/build.gradle.kts b/build.gradle.kts
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -0,0 +0,0 @@ dependencies {
runtimeOnly("org.xerial:sqlite-jdbc:3.47.0.0")
runtimeOnly("com.mysql:mysql-connector-j:9.1.0")
runtimeOnly("com.lmax:disruptor:3.4.4") // Paper
+ // Paper start - Use Velocity cipher
+ implementation("com.velocitypowered:velocity-native:3.3.0-SNAPSHOT") {
+ isTransitive = false
+ }
+ // Paper end - Use Velocity cipher
runtimeOnly("org.apache.maven:maven-resolver-provider:3.9.6")
runtimeOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.9.18")
diff --git a/src/main/java/net/minecraft/network/CipherDecoder.java b/src/main/java/net/minecraft/network/CipherDecoder.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/network/CipherDecoder.java
+++ b/src/main/java/net/minecraft/network/CipherDecoder.java
@@ -0,0 +0,0 @@ import java.util.List;
import javax.crypto.Cipher;
public class CipherDecoder extends MessageToMessageDecoder<ByteBuf> {
- private final CipherBase cipher;
+ private final com.velocitypowered.natives.encryption.VelocityCipher cipher; // Paper - Use Velocity cipher
- public CipherDecoder(Cipher cipher) {
- this.cipher = new CipherBase(cipher);
+ public CipherDecoder(com.velocitypowered.natives.encryption.VelocityCipher cipher) { // Paper - Use Velocity cipher
+ this.cipher = cipher; // Paper - Use Velocity cipher
}
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
- list.add(this.cipher.decipher(channelHandlerContext, byteBuf));
+ // Paper start - Use Velocity cipher
+ ByteBuf compatible = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(channelHandlerContext.alloc(), cipher, byteBuf);
+ try {
+ cipher.process(compatible);
+ list.add(compatible);
+ } catch (Exception e) {
+ compatible.release(); // compatible will never be used if we throw an exception
+ throw e;
+ }
+ // Paper end - Use Velocity cipher
}
+
+ // Paper start - Use Velocity cipher
+ @Override
+ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
+ cipher.close();
+ }
+ // Paper end - Use Velocity cipher
}
diff --git a/src/main/java/net/minecraft/network/CipherEncoder.java b/src/main/java/net/minecraft/network/CipherEncoder.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/network/CipherEncoder.java
+++ b/src/main/java/net/minecraft/network/CipherEncoder.java
@@ -0,0 +0,0 @@ import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import javax.crypto.Cipher;
+import java.util.List;
-public class CipherEncoder extends MessageToByteEncoder<ByteBuf> {
- private final CipherBase cipher;
+public class CipherEncoder extends io.netty.handler.codec.MessageToMessageEncoder<ByteBuf> { // Paper - Use Velocity cipher; change superclass
+ private final com.velocitypowered.natives.encryption.VelocityCipher cipher; // Paper - Use Velocity cipher
- public CipherEncoder(Cipher cipher) {
- this.cipher = new CipherBase(cipher);
+ public CipherEncoder(com.velocitypowered.natives.encryption.VelocityCipher cipher) { // Paper - Use Velocity cipher
+ this.cipher = cipher; // Paper - Use Velocity cipher
}
- protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, ByteBuf byteBuf2) throws Exception {
- this.cipher.encipher(byteBuf, byteBuf2);
+ protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
+ // Paper start - Use Velocity cipher
+ ByteBuf compatible = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(channelHandlerContext.alloc(), cipher, byteBuf);
+ try {
+ cipher.process(compatible);
+ list.add(compatible);
+ } catch (Exception e) {
+ compatible.release(); // compatible will never be used if we throw an exception
+ throw e;
+ }
+ // Paper end - Use Velocity cipher
}
+
+ // Paper start - Use Velocity cipher
+ @Override
+ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
+ cipher.close();
+ }
+ // Paper end - Use Velocity cipher
}
diff --git a/src/main/java/net/minecraft/network/CompressionDecoder.java b/src/main/java/net/minecraft/network/CompressionDecoder.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/network/CompressionDecoder.java
+++ b/src/main/java/net/minecraft/network/CompressionDecoder.java
@@ -0,0 +0,0 @@ public class CompressionDecoder extends ByteToMessageDecoder {
public static final int MAXIMUM_COMPRESSED_LENGTH = 2097152;
public static final int MAXIMUM_UNCOMPRESSED_LENGTH = 8388608;
private Inflater inflater;
+ private com.velocitypowered.natives.compression.VelocityCompressor compressor; // Paper - Use Velocity cipher
private int threshold;
private boolean validateDecompressed;
+ // Paper start - Use Velocity cipher
+ @io.papermc.paper.annotation.DoNotUse
public CompressionDecoder(int compressionThreshold, boolean rejectsBadPackets) {
+ this(null, compressionThreshold, rejectsBadPackets);
+ }
+ public CompressionDecoder(com.velocitypowered.natives.compression.VelocityCompressor compressor, int compressionThreshold, boolean rejectsBadPackets) {
this.threshold = compressionThreshold;
this.validateDecompressed = rejectsBadPackets;
- this.inflater = new Inflater();
+ this.inflater = compressor == null ? new Inflater() : null;
+ this.compressor = compressor;
+ // Paper end - Use Velocity cipher
}
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
@@ -0,0 +0,0 @@ public class CompressionDecoder extends ByteToMessageDecoder {
}
}
+ if (inflater != null) { // Paper - Use Velocity cipher; fallback to vanilla inflater
this.setupInflaterInput(byteBuf);
ByteBuf byteBuf2 = this.inflate(channelHandlerContext, i);
this.inflater.reset();
list.add(byteBuf2);
+ 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(channelHandlerContext.alloc(), this.compressor, byteBuf);
+ ByteBuf uncompressed = com.velocitypowered.natives.util.MoreByteBufUtils.preferredBuffer(channelHandlerContext.alloc(), this.compressor, claimedUncompressedSize);
+ try {
+ this.compressor.inflate(compatibleIn, uncompressed, claimedUncompressedSize);
+ list.add(uncompressed);
+ byteBuf.clear();
+ } catch (Exception e) {
+ uncompressed.release();
+ throw e;
+ } finally {
+ compatibleIn.release();
+ }
+ // Paper end - Use Velocity cipher
}
}
}
+ // Paper start - Use Velocity cipher
+ @Override
+ public void handlerRemoved0(ChannelHandlerContext ctx) throws Exception {
+ if (this.compressor != null) {
+ this.compressor.close();
+ }
+ }
+ // Paper end - Use Velocity cipher
+
private void setupInflaterInput(ByteBuf buf) {
ByteBuffer byteBuffer;
if (buf.nioBufferCount() > 0) {
@@ -0,0 +0,0 @@ public class CompressionDecoder extends ByteToMessageDecoder {
}
}
- public void setThreshold(int compressionThreshold, boolean rejectsBadPackets) {
+ // Paper start - Use Velocity cipher
+ public void setThreshold(com.velocitypowered.natives.compression.VelocityCompressor compressor, int compressionThreshold, boolean rejectsBadPackets) {
+ if (this.compressor == null && compressor != null) { // Only re-configure once. Re-reconfiguring would require closing the native compressor.
+ this.compressor = compressor;
+ this.inflater = null;
+ }
+ // Paper end - Use Velocity cipher
this.threshold = compressionThreshold;
this.validateDecompressed = rejectsBadPackets;
}
diff --git a/src/main/java/net/minecraft/network/CompressionEncoder.java b/src/main/java/net/minecraft/network/CompressionEncoder.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/network/CompressionEncoder.java
+++ b/src/main/java/net/minecraft/network/CompressionEncoder.java
@@ -0,0 +0,0 @@ import io.netty.handler.codec.MessageToByteEncoder;
import java.util.zip.Deflater;
public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
- private final byte[] encodeBuf = new byte[8192];
+ @javax.annotation.Nullable private final byte[] encodeBuf; // Paper - Use Velocity cipher
+ @javax.annotation.Nullable // Paper - Use Velocity cipher
private final Deflater deflater;
+ @javax.annotation.Nullable // Paper - Use Velocity cipher
+ private final com.velocitypowered.natives.compression.VelocityCompressor compressor; // Paper - Use Velocity cipher
private int threshold;
+ // Paper start - Use Velocity cipher
public CompressionEncoder(int compressionThreshold) {
+ this(null, compressionThreshold);
+ }
+ public CompressionEncoder(@javax.annotation.Nullable com.velocitypowered.natives.compression.VelocityCompressor compressor, int compressionThreshold) {
this.threshold = compressionThreshold;
- this.deflater = new Deflater();
+ if (compressor == null) {
+ this.encodeBuf = new byte[8192];
+ this.deflater = new Deflater();
+ } else {
+ this.encodeBuf = null;
+ this.deflater = null;
+ }
+ this.compressor = compressor;
+ // Paper end - Use Velocity cipher
}
- protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, ByteBuf byteBuf2) {
+ protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, ByteBuf byteBuf2) throws Exception { // Paper - Use Velocity cipher
int i = byteBuf.readableBytes();
if (i > 8388608) {
throw new IllegalArgumentException("Packet too big (is " + i + ", should be less than 8388608)");
@@ -0,0 +0,0 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
VarInt.write(byteBuf2, 0);
byteBuf2.writeBytes(byteBuf);
} else {
+ if (this.deflater != null) { // Paper - Use Velocity cipher
byte[] bs = new byte[i];
byteBuf.readBytes(bs);
VarInt.write(byteBuf2, bs.length);
@@ -0,0 +0,0 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
}
this.deflater.reset();
+ // Paper start - Use Velocity cipher
+ return;
+ }
+
+ VarInt.write(byteBuf2, i);
+ final ByteBuf compatibleIn = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(channelHandlerContext.alloc(), this.compressor, byteBuf);
+ try {
+ this.compressor.deflate(compatibleIn, byteBuf2);
+ } finally {
+ compatibleIn.release();
+ }
}
}
}
+ @Override
+ protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, ByteBuf msg, boolean preferDirect) throws Exception{
+ if (this.compressor != null) {
+ // We allocate bytes to be compressed plus 1 byte. This covers two cases:
+ //
+ // - Compression
+ // According to https://github.com/ebiggers/libdeflate/blob/master/libdeflate.h#L103,
+ // if the data compresses well (and we do not have some pathological case) then the maximum
+ // size the compressed size will ever be is the input size minus one.
+ // - Uncompressed
+ // This is fairly obvious - we will then have one more than the uncompressed size.
+ final int initialBufferSize = msg.readableBytes() + 1;
+ return com.velocitypowered.natives.util.MoreByteBufUtils.preferredBuffer(ctx.alloc(), this.compressor, initialBufferSize);
+ }
+
+ return super.allocateBuffer(ctx, msg, preferDirect);
+ }
+
+ @Override
+ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
+ if (this.compressor != null) {
+ this.compressor.close();
+ // Paper end - Use Velocity cipher
+ }
+ }
+
public int getThreshold() {
return this.threshold;
}
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
return networkmanager;
}
- public void setEncryptionKey(Cipher decryptionCipher, Cipher encryptionCipher) {
- this.encrypted = true;
- this.channel.pipeline().addBefore("splitter", "decrypt", new CipherDecoder(decryptionCipher));
- this.channel.pipeline().addBefore("prepender", "encrypt", new CipherEncoder(encryptionCipher));
+ // Paper start - Use Velocity cipher
+// public void setEncryptionKey(Cipher decryptionCipher, Cipher encryptionCipher) {
+// this.encrypted = true;
+// this.channel.pipeline().addBefore("splitter", "decrypt", new CipherDecoder(decryptionCipher));
+// this.channel.pipeline().addBefore("prepender", "encrypt", new CipherEncoder(encryptionCipher));
+// }
+
+ public void setupEncryption(javax.crypto.SecretKey key) throws net.minecraft.util.CryptException {
+ if (!this.encrypted) {
+ try {
+ com.velocitypowered.natives.encryption.VelocityCipher decryption = com.velocitypowered.natives.util.Natives.cipher.get().forDecryption(key);
+ com.velocitypowered.natives.encryption.VelocityCipher encryption = com.velocitypowered.natives.util.Natives.cipher.get().forEncryption(key);
+
+ this.encrypted = true;
+ this.channel.pipeline().addBefore("splitter", "decrypt", new CipherDecoder(decryption));
+ this.channel.pipeline().addBefore("prepender", "encrypt", new CipherEncoder(encryption));
+ } catch (java.security.GeneralSecurityException e) {
+ throw new net.minecraft.util.CryptException(e);
+ }
+ }
}
+ // Paper end - Use Velocity cipher
public boolean isEncrypted() {
return this.encrypted;
@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
public void setupCompression(int compressionThreshold, boolean rejectsBadPackets) {
if (compressionThreshold >= 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
ChannelHandler channelhandler = this.channel.pipeline().get("decompress");
if (channelhandler instanceof CompressionDecoder) {
CompressionDecoder packetdecompressor = (CompressionDecoder) channelhandler;
- packetdecompressor.setThreshold(compressionThreshold, rejectsBadPackets);
+ packetdecompressor.setThreshold(compressor, compressionThreshold, rejectsBadPackets); // Paper - Use Velocity cipher
} else {
- this.channel.pipeline().addAfter("splitter", "decompress", new CompressionDecoder(compressionThreshold, rejectsBadPackets));
+ this.channel.pipeline().addAfter("splitter", "decompress", new CompressionDecoder(compressor, compressionThreshold, rejectsBadPackets)); // Paper - Use Velocity cipher
}
channelhandler = this.channel.pipeline().get("compress");
@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
packetcompressor.setThreshold(compressionThreshold);
} else {
- this.channel.pipeline().addAfter("prepender", "compress", new CompressionEncoder(compressionThreshold));
+ this.channel.pipeline().addAfter("prepender", "compress", new CompressionEncoder(compressor, compressionThreshold)); // 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/src/main/java/net/minecraft/server/network/ServerConnectionListener.java b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
+++ b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
@@ -0,0 +0,0 @@ public class ServerConnectionListener {
}
// Paper end - Warn people with console access that HAProxy is in use.
+ // Paper start - Use Velocity cipher
+ 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(((ServerBootstrap) ((ServerBootstrap) (new ServerBootstrap()).channel(oclass)).childHandler(new ChannelInitializer<Channel>() {
protected void initChannel(Channel channel) {
try {
diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
@@ -0,0 +0,0 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
}
SecretKey secretkey = packet.getSecretKey(privatekey);
- Cipher cipher = Crypt.getCipher(2, secretkey);
- Cipher cipher1 = Crypt.getCipher(1, secretkey);
+ // Paper start - Use Velocity cipher
+// Cipher cipher = Crypt.getCipher(2, secretkey);
+// Cipher cipher1 = Crypt.getCipher(1, secretkey);
+ // Paper end - Use Velocity cipher
s = (new BigInteger(Crypt.digestData("", this.server.getKeyPair().getPublic(), secretkey))).toString(16);
this.state = ServerLoginPacketListenerImpl.State.AUTHENTICATING;
- this.connection.setEncryptionKey(cipher, cipher1);
+ this.connection.setupEncryption(secretkey); // Paper - Use Velocity cipher
} catch (CryptException cryptographyexception) {
throw new IllegalStateException("Protocol error", cryptographyexception);
}

View file

@ -1,171 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Mon, 6 Apr 2020 17:53:29 -0700
Subject: [PATCH] Optimize GoalSelector Goal.Flag Set operations
Optimise the stream.anyMatch statement to move to a bitset
where we can replace the call with a single bitwise operation.
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java b/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java
@@ -0,0 +0,0 @@ import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
public abstract class Goal {
- private final EnumSet<Goal.Flag> flags = EnumSet.noneOf(Goal.Flag.class);
+ private final EnumSet<Goal.Flag> flags = EnumSet.noneOf(Goal.Flag.class); // Paper unused, but dummy to prevent plugins from crashing as hard. Theyll need to support paper in a special case if this is super important, but really doesn't seem like it would be.
+ private final ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<net.minecraft.world.entity.ai.goal.Goal.Flag> goalTypes = new ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from pathfindergoalselector
+
+ // Paper start - remove streams from pathfindergoalselector; make sure types are not empty
+ public Goal() {
+ if (this.goalTypes.size() == 0) {
+ this.goalTypes.addUnchecked(Flag.UNKNOWN_BEHAVIOR);
+ }
+ }
+ // Paper end - remove streams from pathfindergoalselector
public abstract boolean canUse();
@@ -0,0 +0,0 @@ public abstract class Goal {
}
public void setFlags(EnumSet<Goal.Flag> controls) {
- this.flags.clear();
- this.flags.addAll(controls);
+ // Paper start - remove streams from pathfindergoalselector
+ this.goalTypes.clear();
+ this.goalTypes.addAllUnchecked(controls);
+ if (this.goalTypes.size() == 0) {
+ this.goalTypes.addUnchecked(Flag.UNKNOWN_BEHAVIOR);
+ }
+ // Paper end - remove streams from pathfindergoalselector
}
@Override
@@ -0,0 +0,0 @@ public abstract class Goal {
return this.getClass().getSimpleName();
}
- public EnumSet<Goal.Flag> getFlags() {
- return this.flags;
+ // Paper start - remove streams from pathfindergoalselector
+ public ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<Goal.Flag> getFlags() {
+ return this.goalTypes;
+ // Paper end - remove streams from pathfindergoalselector
}
// Paper start - Mob Goal API
public boolean hasFlag(final Goal.Flag flag) {
- return this.flags.contains(flag);
+ return this.goalTypes.hasElement(flag);
}
public void addFlag(final Goal.Flag flag) {
- this.flags.add(flag);
+ this.goalTypes.addUnchecked(flag);
}
// Paper end - Mob Goal API
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
@@ -0,0 +0,0 @@ public class GoalSelector {
};
private final Map<Goal.Flag, WrappedGoal> lockedFlags = new EnumMap<>(Goal.Flag.class);
private final Set<WrappedGoal> availableGoals = new ObjectLinkedOpenHashSet<>();
- private final EnumSet<Goal.Flag> disabledFlags = EnumSet.noneOf(Goal.Flag.class);
+ private static final Goal.Flag[] GOAL_FLAG_VALUES = Goal.Flag.values(); // Paper - remove streams from pathfindergoalselector
+ private final ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<net.minecraft.world.entity.ai.goal.Goal.Flag> goalTypes = new ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from pathfindergoalselector
private int curRate; // Paper - EAR 2
public void addGoal(int priority, Goal goal) {
@@ -0,0 +0,0 @@ public class GoalSelector {
this.availableGoals.removeIf(wrappedGoalx -> wrappedGoalx.getGoal() == goal);
}
- private static boolean goalContainsAnyFlags(WrappedGoal goal, EnumSet<Goal.Flag> controls) {
- for (Goal.Flag flag : goal.getFlags()) {
- if (controls.contains(flag)) {
- return true;
- }
- }
-
- return false;
+ // Paper start
+ private static boolean goalContainsAnyFlags(WrappedGoal goal, ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<Goal.Flag> controls) {
+ return goal.getFlags().hasCommonElements(controls);
}
private static boolean goalCanBeReplacedForAllFlags(WrappedGoal goal, Map<Goal.Flag, WrappedGoal> goalsByControl) {
- 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 flag = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)];
+ flagIterator ^= ca.spottedleaf.concurrentutil.util.IntegerUtil.getTrailingBit(flagIterator);
+ // Paper end
if (!goalsByControl.getOrDefault(flag, NO_GOAL).canBeReplacedBy(goal)) {
return false;
}
@@ -0,0 +0,0 @@ public class GoalSelector {
profilerFiller.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();
}
}
@@ -0,0 +0,0 @@ public class GoalSelector {
profilerFiller.push("goalUpdate");
for (WrappedGoal wrappedGoal2 : this.availableGoals) {
- if (!wrappedGoal2.isRunning()
- && !goalContainsAnyFlags(wrappedGoal2, this.disabledFlags)
- && goalCanBeReplacedForAllFlags(wrappedGoal2, this.lockedFlags)
- && wrappedGoal2.canUse()) {
- for (Goal.Flag flag : wrappedGoal2.getFlags()) {
+ // Paper start
+ if (!wrappedGoal2.isRunning() && !goalContainsAnyFlags(wrappedGoal2, this.goalTypes) && goalCanBeReplacedForAllFlags(wrappedGoal2, this.lockedFlags) && wrappedGoal2.canUse()) {
+ long flagIterator = wrappedGoal2.getFlags().getBackingSet();
+ int wrappedGoalSize = wrappedGoal2.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 wrappedGoal3 = this.lockedFlags.getOrDefault(flag, NO_GOAL);
wrappedGoal3.stop();
this.lockedFlags.put(flag, wrappedGoal2);
@@ -0,0 +0,0 @@ public class GoalSelector {
}
public void disableControlFlag(Goal.Flag control) {
- this.disabledFlags.add(control);
+ this.goalTypes.addUnchecked(control); // Paper - remove streams from pathfindergoalselector
}
public void enableControlFlag(Goal.Flag control) {
- this.disabledFlags.remove(control);
+ this.goalTypes.removeUnchecked(control); // Paper - remove streams from pathfindergoalselector
}
public void setControlFlag(Goal.Flag control, boolean enabled) {
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java
@@ -0,0 +0,0 @@ public class WrappedGoal extends Goal {
}
@Override
- public EnumSet<Goal.Flag> getFlags() {
+ // Paper start - remove streams from pathfindergoalselector
+ public ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<Goal.Flag> getFlags() {
return this.goal.getFlags();
+ // Paper end - remove streams from pathfindergoalselector
}
public boolean isRunning() {

View file

@ -1,664 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Wed, 27 Apr 2016 22:09:52 -0400
Subject: [PATCH] Optimize Hoppers
* Removes unnecessary extra calls to .update() that are very expensive
* Lots of itemstack cloning removed. Only clone if the item is actually moved
* Return true when a plugin cancels inventory move item event instead of false, as false causes pulls to cycle through all items.
However, pushes do not exhibit the same behavior, so this is not something plugins could of been relying on.
* Add option (Default on) to cooldown hoppers when they fail to move an item due to full inventory
* Skip subsequent InventoryMoveItemEvents if a plugin does not use the item after first event fire for an iteration by tracking changes to the event via an internal event implementation.
* Don't check for Entities with Inventories if the block above us is also occluding (not just Inventoried)
* Remove Streams from Item Suck In and restore restore 1.12 AABB checks which is simpler and no voxel allocations (was doing TWO Item Suck ins)
diff --git a/src/main/java/io/papermc/paper/event/inventory/PaperInventoryMoveItemEvent.java b/src/main/java/io/papermc/paper/event/inventory/PaperInventoryMoveItemEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/io/papermc/paper/event/inventory/PaperInventoryMoveItemEvent.java
@@ -0,0 +0,0 @@
+package io.papermc.paper.event.inventory;
+
+import org.bukkit.event.inventory.InventoryMoveItemEvent;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.ItemStack;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+import org.jetbrains.annotations.NotNull;
+
+@DefaultQualifier(NonNull.class)
+public class PaperInventoryMoveItemEvent extends InventoryMoveItemEvent {
+
+ public boolean calledSetItem;
+ public boolean calledGetItem;
+
+ public PaperInventoryMoveItemEvent(final @NotNull Inventory sourceInventory, final @NotNull ItemStack itemStack, final @NotNull Inventory destinationInventory, final boolean didSourceInitiate) {
+ super(sourceInventory, itemStack, destinationInventory, didSourceInitiate);
+ }
+
+ @Override
+ public ItemStack getItem() {
+ this.calledGetItem = true;
+ return super.getItem();
+ }
+
+ @Override
+ public void setItem(final ItemStack itemStack) {
+ super.setItem(itemStack);
+ this.calledSetItem = true;
+ }
+}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
ServerLevel worldserver = (ServerLevel) iterator.next();
worldserver.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - BlockPhysicsEvent
worldserver.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent
+ net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = worldserver.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - Perf: Optimize Hoppers
gameprofilerfiller.push(() -> {
String s = String.valueOf(worldserver);
diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/item/ItemStack.java
+++ b/src/main/java/net/minecraft/world/item/ItemStack.java
@@ -0,0 +0,0 @@ public final class ItemStack implements DataComponentHolder {
}
public ItemStack copy() {
- if (this.isEmpty()) {
+ // Paper start - Perf: Optimize Hoppers
+ return this.copy(false);
+ }
+
+ public ItemStack copy(boolean originalItem) {
+ if (!originalItem && this.isEmpty()) {
+ // Paper end - Perf: Optimize Hoppers
return ItemStack.EMPTY;
} else {
- ItemStack itemstack = new ItemStack(this.getItem(), this.count, this.components.copy());
+ ItemStack itemstack = new ItemStack(originalItem ? this.item : this.getItem(), this.count, this.components.copy()); // Paper - Perf: Optimize Hoppers
itemstack.setPopTime(this.getPopTime());
return itemstack;
diff --git a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
@@ -0,0 +0,0 @@ import org.bukkit.inventory.InventoryHolder;
// CraftBukkit end
public abstract class BlockEntity {
+ static boolean ignoreTileUpdates; // Paper - Perf: Optimize Hoppers
// CraftBukkit start - data containers
private static final CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new CraftPersistentDataTypeRegistry();
@@ -0,0 +0,0 @@ public abstract class BlockEntity {
public void setChanged() {
if (this.level != null) {
+ if (ignoreTileUpdates) return; // Paper - Perf: Optimize Hoppers
BlockEntity.setChanged(this.level, this.worldPosition, this.blockState);
}
diff --git a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java
@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
+ // Paper start - Perf: Optimize Hoppers
+ private static final int HOPPER_EMPTY = 0;
+ private static final int HOPPER_HAS_ITEMS = 1;
+ private static final int HOPPER_IS_FULL = 2;
+
+ private static int getFullState(final HopperBlockEntity tileEntity) {
+ tileEntity.unpackLootTable(null);
+
+ final List<ItemStack> hopperItems = tileEntity.getItems();
+
+ boolean empty = true;
+ boolean full = true;
+
+ for (int i = 0, len = hopperItems.size(); i < len; ++i) {
+ final ItemStack stack = hopperItems.get(i);
+ if (stack.isEmpty()) {
+ full = false;
+ continue;
+ }
+
+ if (!full) {
+ // can't be full
+ return HOPPER_HAS_ITEMS;
+ }
+
+ empty = false;
+
+ if (stack.getCount() != stack.getMaxStackSize()) {
+ // can't be full or empty
+ return HOPPER_HAS_ITEMS;
+ }
+ }
+
+ return empty ? HOPPER_EMPTY : (full ? HOPPER_IS_FULL : HOPPER_HAS_ITEMS);
+ }
+ // Paper end - Perf: Optimize Hoppers
+
private static boolean tryMoveItems(Level world, BlockPos pos, BlockState state, HopperBlockEntity blockEntity, BooleanSupplier booleansupplier) {
if (world.isClientSide) {
return false;
@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
if (!blockEntity.isOnCooldown() && (Boolean) state.getValue(HopperBlock.ENABLED)) {
boolean flag = false;
- if (!blockEntity.isEmpty()) {
+ final int fullState = getFullState(blockEntity); // Paper - Perf: Optimize Hoppers
+ if (fullState != HOPPER_EMPTY) { // Paper - Perf: Optimize Hoppers
flag = HopperBlockEntity.ejectItems(world, pos, blockEntity);
}
- if (!blockEntity.inventoryFull()) {
+ if (fullState != HOPPER_IS_FULL || flag) { // Paper - Perf: Optimize Hoppers
flag |= booleansupplier.getAsBoolean();
}
@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
return false;
}
+ // Paper start - Perf: Optimize Hoppers
+ private static boolean skipPullModeEventFire;
+ private static boolean skipPushModeEventFire;
+ public static boolean skipHopperEvents;
+
+ private static boolean hopperPush(final Level level, final Container destination, final Direction direction, final HopperBlockEntity hopper) {
+ skipPushModeEventFire = skipHopperEvents;
+ boolean foundItem = false;
+ for (int i = 0; i < hopper.getContainerSize(); ++i) {
+ final ItemStack item = hopper.getItem(i);
+ if (!item.isEmpty()) {
+ foundItem = true;
+ ItemStack origItemStack = item;
+ ItemStack movedItem = origItemStack;
+
+ final int originalItemCount = origItemStack.getCount();
+ final int movedItemCount = Math.min(level.spigotConfig.hopperAmount, originalItemCount);
+ origItemStack.setCount(movedItemCount);
+
+ // We only need to fire the event once to give protection plugins a chance to cancel this event
+ // Because nothing uses getItem, every event call should end up the same result.
+ if (!skipPushModeEventFire) {
+ movedItem = callPushMoveEvent(destination, movedItem, hopper);
+ if (movedItem == null) { // cancelled
+ origItemStack.setCount(originalItemCount);
+ return false;
+ }
+ }
+
+ final ItemStack remainingItem = addItem(hopper, destination, movedItem, direction);
+ final int remainingItemCount = remainingItem.getCount();
+ if (remainingItemCount != movedItemCount) {
+ origItemStack = origItemStack.copy(true);
+ origItemStack.setCount(originalItemCount);
+ if (!origItemStack.isEmpty()) {
+ origItemStack.setCount(originalItemCount - movedItemCount + remainingItemCount);
+ }
+ hopper.setItem(i, origItemStack);
+ destination.setChanged();
+ return true;
+ }
+ origItemStack.setCount(originalItemCount);
+ }
+ }
+ if (foundItem && level.paperConfig().hopper.cooldownWhenFull) { // Inventory was full - cooldown
+ hopper.setCooldown(level.spigotConfig.hopperTransfer);
+ }
+ return false;
+ }
+
+ private static boolean hopperPull(final Level level, final Hopper hopper, final Container container, ItemStack origItemStack, final int i) {
+ ItemStack movedItem = origItemStack;
+ final int originalItemCount = origItemStack.getCount();
+ final int movedItemCount = Math.min(level.spigotConfig.hopperAmount, originalItemCount);
+ container.setChanged(); // original logic always marks source inv as changed even if no move happens.
+ movedItem.setCount(movedItemCount);
+
+ if (!skipPullModeEventFire) {
+ movedItem = callPullMoveEvent(hopper, container, movedItem);
+ if (movedItem == null) { // cancelled
+ origItemStack.setCount(originalItemCount);
+ // Drastically improve performance by returning true.
+ // No plugin could of relied on the behavior of false as the other call
+ // site for IMIE did not exhibit the same behavior
+ return true;
+ }
+ }
+
+ final ItemStack remainingItem = addItem(container, hopper, movedItem, null);
+ final int remainingItemCount = remainingItem.getCount();
+ if (remainingItemCount != movedItemCount) {
+ origItemStack = origItemStack.copy(true);
+ origItemStack.setCount(originalItemCount);
+ if (!origItemStack.isEmpty()) {
+ origItemStack.setCount(originalItemCount - movedItemCount + remainingItemCount);
+ }
+
+ ignoreTileUpdates = true;
+ container.setItem(i, origItemStack);
+ ignoreTileUpdates = false;
+ container.setChanged();
+ return true;
+ }
+ origItemStack.setCount(originalItemCount);
+
+ if (level.paperConfig().hopper.cooldownWhenFull) {
+ cooldownHopper(hopper);
+ }
+
+ return false;
+ }
+
+ @Nullable
+ private static ItemStack callPushMoveEvent(Container iinventory, ItemStack itemstack, HopperBlockEntity hopper) {
+ final Inventory destinationInventory = getInventory(iinventory);
+ final io.papermc.paper.event.inventory.PaperInventoryMoveItemEvent event = new io.papermc.paper.event.inventory.PaperInventoryMoveItemEvent(hopper.getOwner(false).getInventory(),
+ CraftItemStack.asCraftMirror(itemstack), destinationInventory, true);
+ final boolean result = event.callEvent();
+ if (!event.calledGetItem && !event.calledSetItem) {
+ skipPushModeEventFire = true;
+ }
+ if (!result) {
+ cooldownHopper(hopper);
+ return null;
+ }
+
+ if (event.calledSetItem) {
+ return CraftItemStack.asNMSCopy(event.getItem());
+ } else {
+ return itemstack;
+ }
+ }
+
+ @Nullable
+ private static ItemStack callPullMoveEvent(final Hopper hopper, final Container container, final ItemStack itemstack) {
+ final Inventory sourceInventory = getInventory(container);
+ final Inventory destination = getInventory(hopper);
+
+ // Mirror is safe as no plugins ever use this item
+ final io.papermc.paper.event.inventory.PaperInventoryMoveItemEvent event = new io.papermc.paper.event.inventory.PaperInventoryMoveItemEvent(sourceInventory, CraftItemStack.asCraftMirror(itemstack), destination, false);
+ final boolean result = event.callEvent();
+ if (!event.calledGetItem && !event.calledSetItem) {
+ skipPullModeEventFire = true;
+ }
+ if (!result) {
+ cooldownHopper(hopper);
+ return null;
+ }
+
+ if (event.calledSetItem) {
+ return CraftItemStack.asNMSCopy(event.getItem());
+ } else {
+ return itemstack;
+ }
+ }
+
+ private static Inventory getInventory(final Container container) {
+ final Inventory sourceInventory;
+ if (container instanceof CompoundContainer compoundContainer) {
+ // Have to special-case large chests as they work oddly
+ sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer);
+ } else if (container instanceof BlockEntity blockEntity) {
+ sourceInventory = blockEntity.getOwner(false).getInventory();
+ } else if (container.getOwner() != null) {
+ sourceInventory = container.getOwner().getInventory();
+ } else {
+ sourceInventory = new CraftInventory(container);
+ }
+ return sourceInventory;
+ }
+
+ private static void cooldownHopper(final Hopper hopper) {
+ if (hopper instanceof HopperBlockEntity blockEntity && blockEntity.getLevel() != null) {
+ blockEntity.setCooldown(blockEntity.getLevel().spigotConfig.hopperTransfer);
+ }
+ }
+
+ private static boolean allMatch(Container iinventory, Direction enumdirection, java.util.function.BiPredicate<ItemStack, Integer> test) {
+ if (iinventory instanceof WorldlyContainer) {
+ for (int i : ((WorldlyContainer) iinventory).getSlotsForFace(enumdirection)) {
+ if (!test.test(iinventory.getItem(i), i)) {
+ return false;
+ }
+ }
+ } else {
+ int size = iinventory.getContainerSize();
+ for (int i = 0; i < size; i++) {
+ if (!test.test(iinventory.getItem(i), i)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private static boolean anyMatch(Container iinventory, Direction enumdirection, java.util.function.BiPredicate<ItemStack, Integer> test) {
+ if (iinventory instanceof WorldlyContainer) {
+ for (int i : ((WorldlyContainer) iinventory).getSlotsForFace(enumdirection)) {
+ if (test.test(iinventory.getItem(i), i)) {
+ return true;
+ }
+ }
+ } else {
+ int size = iinventory.getContainerSize();
+ for (int i = 0; i < size; i++) {
+ if (test.test(iinventory.getItem(i), i)) {
+ return true;
+ }
+ }
+ }
+ return true;
+ }
+ private static final java.util.function.BiPredicate<ItemStack, Integer> STACK_SIZE_TEST = (itemstack, i) -> itemstack.getCount() >= itemstack.getMaxStackSize();
+ private static final java.util.function.BiPredicate<ItemStack, Integer> IS_EMPTY_TEST = (itemstack, i) -> itemstack.isEmpty();
+ // Paper end - Perf: Optimize Hoppers
+
private static boolean ejectItems(Level world, BlockPos pos, HopperBlockEntity blockEntity) {
Container iinventory = HopperBlockEntity.getAttachedContainer(world, pos, blockEntity);
@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
if (HopperBlockEntity.isFullContainer(iinventory, enumdirection)) {
return false;
} else {
- for (int i = 0; i < blockEntity.getContainerSize(); ++i) {
- ItemStack itemstack = blockEntity.getItem(i);
-
- if (!itemstack.isEmpty()) {
- int j = itemstack.getCount();
- // CraftBukkit start - Call event when pushing items into other inventories
- ItemStack original = itemstack.copy();
- CraftItemStack oitemstack = CraftItemStack.asCraftMirror(blockEntity.removeItem(i, world.spigotConfig.hopperAmount)); // Spigot
-
- Inventory destinationInventory;
- // Have to special case large chests as they work oddly
- if (iinventory instanceof CompoundContainer) {
- destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) iinventory);
- } else if (iinventory.getOwner() != null) {
- destinationInventory = iinventory.getOwner().getInventory();
- } else {
- destinationInventory = new CraftInventory(iinventory);
- }
-
- InventoryMoveItemEvent event = new InventoryMoveItemEvent(blockEntity.getOwner().getInventory(), oitemstack, destinationInventory, true);
- world.getCraftServer().getPluginManager().callEvent(event);
- if (event.isCancelled()) {
- blockEntity.setItem(i, original);
- blockEntity.setCooldown(world.spigotConfig.hopperTransfer); // Delay hopper checks // Spigot
- return false;
- }
- int origCount = event.getItem().getAmount(); // Spigot
- ItemStack itemstack1 = HopperBlockEntity.addItem(blockEntity, iinventory, CraftItemStack.asNMSCopy(event.getItem()), enumdirection);
- // CraftBukkit end
-
- if (itemstack1.isEmpty()) {
- iinventory.setChanged();
- return true;
- }
-
- itemstack.setCount(j);
- // Spigot start
- itemstack.shrink(origCount - itemstack1.getCount());
- if (j <= world.spigotConfig.hopperAmount) {
- // Spigot end
- blockEntity.setItem(i, itemstack);
- }
- }
- }
-
- return false;
+ // Paper start - Perf: Optimize Hoppers
+ return hopperPush(world, iinventory, enumdirection, blockEntity);
+ //for (int i = 0; i < blockEntity.getContainerSize(); ++i) {
+ // ItemStack itemstack = blockEntity.getItem(i);
+
+ // if (!itemstack.isEmpty()) {
+ // int j = itemstack.getCount();
+ // // CraftBukkit start - Call event when pushing items into other inventories
+ // ItemStack original = itemstack.copy();
+ // CraftItemStack oitemstack = CraftItemStack.asCraftMirror(blockEntity.removeItem(i, world.spigotConfig.hopperAmount)); // Spigot
+
+ // Inventory destinationInventory;
+ // // Have to special case large chests as they work oddly
+ // if (iinventory instanceof CompoundContainer) {
+ // destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) iinventory);
+ // } else if (iinventory.getOwner() != null) {
+ // destinationInventory = iinventory.getOwner().getInventory();
+ // } else {
+ // destinationInventory = new CraftInventory(iinventory);
+ // }
+
+ // InventoryMoveItemEvent event = new InventoryMoveItemEvent(tileentityhopper.getOwner().getInventory(), oitemstack, destinationInventory, true);
+ // world.getCraftServer().getPluginManager().callEvent(event);
+ // if (event.isCancelled()) {
+ // blockEntity.setItem(i, original);
+ // blockEntity.setCooldown(world.spigotConfig.hopperTransfer); // Delay hopper checks // Spigot
+ // return false;
+ // }
+ // int origCount = event.getItem().getAmount(); // Spigot
+ // ItemStack itemstack1 = HopperBlockEntity.addItem(blockEntity, iinventory, CraftItemStack.asNMSCopy(event.getItem()), enumdirection);
+ // // CraftBukkit end
+
+ // if (itemstack1.isEmpty()) {
+ // iinventory.setChanged();
+ // return true;
+ // }
+
+ // itemstack.setCount(j);
+ // // Spigot start
+ // itemstack.shrink(origCount - itemstack1.getCount());
+ // if (j <= world.spigotConfig.hopperAmount) {
+ // // Spigot end
+ // blockEntity.setItem(i, itemstack);
+ // }
+ // }
+ //}
+
+ // return false;
+ // Paper end - Perf: Optimize Hoppers
}
}
}
@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
return false;
}
}
-
return true;
}
@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
if (iinventory != null) {
Direction enumdirection = Direction.DOWN;
+ skipPullModeEventFire = skipHopperEvents; // Paper - Perf: Optimize Hoppers
int[] aint = HopperBlockEntity.getSlots(iinventory, enumdirection);
int i = aint.length;
@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
ItemStack itemstack = iinventory.getItem(i);
if (!itemstack.isEmpty() && HopperBlockEntity.canTakeItemFromContainer(ihopper, iinventory, itemstack, i, enumdirection)) {
- int j = itemstack.getCount();
- // CraftBukkit start - Call event on collection of items from inventories into the hopper
- ItemStack original = itemstack.copy();
- CraftItemStack oitemstack = CraftItemStack.asCraftMirror(iinventory.removeItem(i, world.spigotConfig.hopperAmount)); // Spigot
-
- Inventory sourceInventory;
- // Have to special case large chests as they work oddly
- if (iinventory instanceof CompoundContainer) {
- sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) iinventory);
- } else if (iinventory.getOwner() != null) {
- sourceInventory = iinventory.getOwner().getInventory();
- } else {
- sourceInventory = new CraftInventory(iinventory);
- }
-
- InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory, oitemstack, ihopper.getOwner().getInventory(), false);
-
- Bukkit.getServer().getPluginManager().callEvent(event);
- if (event.isCancelled()) {
- iinventory.setItem(i, original);
-
- if (ihopper instanceof HopperBlockEntity) {
- ((HopperBlockEntity) ihopper).setCooldown(world.spigotConfig.hopperTransfer); // Spigot
- }
-
- return false;
- }
- int origCount = event.getItem().getAmount(); // Spigot
- ItemStack itemstack1 = HopperBlockEntity.addItem(iinventory, ihopper, CraftItemStack.asNMSCopy(event.getItem()), null);
- // CraftBukkit end
-
- if (itemstack1.isEmpty()) {
- iinventory.setChanged();
- return true;
- }
-
- itemstack.setCount(j);
- // Spigot start
- itemstack.shrink(origCount - itemstack1.getCount());
- if (j <= world.spigotConfig.hopperAmount) {
- // Spigot end
- iinventory.setItem(i, itemstack);
- }
+ // Paper start - Perf: Optimize Hoppers
+ return hopperPull(world, ihopper, iinventory, itemstack, i);
+ // int j = itemstack.getCount();
+ // // CraftBukkit start - Call event on collection of items from inventories into the hopper
+ // ItemStack original = itemstack.copy();
+ // CraftItemStack oitemstack = CraftItemStack.asCraftMirror(iinventory.removeItem(i, world.spigotConfig.hopperAmount)); // Spigot
+
+ // Inventory sourceInventory;
+ // // Have to special case large chests as they work oddly
+ // if (iinventory instanceof CompoundContainer) {
+ // sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) iinventory);
+ // } else if (iinventory.getOwner() != null) {
+ // sourceInventory = iinventory.getOwner().getInventory();
+ // } else {
+ // sourceInventory = new CraftInventory(iinventory);
+ // }
+
+ // InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory, oitemstack, ihopper.getOwner().getInventory(), false);
+
+ // Bukkit.getServer().getPluginManager().callEvent(event);
+ // if (event.isCancelled()) {
+ // iinventory.setItem(i, original);
+
+ // if (ihopper instanceof HopperBlockEntity) {
+ // ((HopperBlockEntity) ihopper).setCooldown(world.spigotConfig.hopperTransfer); // Spigot
+ // }
+
+ // return false;
+ // }
+ // int origCount = event.getItem().getAmount(); // Spigot
+ // ItemStack itemstack1 = HopperBlockEntity.addItem(iinventory, ihopper, CraftItemStack.asNMSCopy(event.getItem()), null);
+ // // CraftBukkit end
+
+ // if (itemstack1.isEmpty()) {
+ // iinventory.setChanged();
+ // return true;
+ // }
+
+ // itemstack.setCount(j);
+ // // Spigot start
+ // itemstack.shrink(origCount - itemstack1.getCount());
+ // if (j <= world.spigotConfig.hopperAmount) {
+ // // Spigot end
+ // iinventory.setItem(i, itemstack);
+ // }
+ // Paper end - Perf: Optimize Hoppers
}
return false;
@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
public static boolean addItem(Container inventory, ItemEntity itemEntity) {
boolean flag = false;
// CraftBukkit start
- InventoryPickupItemEvent event = new InventoryPickupItemEvent(inventory.getOwner().getInventory(), (org.bukkit.entity.Item) itemEntity.getBukkitEntity());
+ if (InventoryPickupItemEvent.getHandlerList().getRegisteredListeners().length > 0) { // Paper - optimize hoppers
+ InventoryPickupItemEvent event = new InventoryPickupItemEvent(getInventory(inventory), (org.bukkit.entity.Item) itemEntity.getBukkitEntity()); // Paper - Perf: Optimize Hoppers; use getInventory() to avoid snapshot creation
itemEntity.level().getCraftServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return false;
}
// CraftBukkit end
+ } // Paper - Perf: Optimize Hoppers
ItemStack itemstack = itemEntity.getItem().copy();
ItemStack itemstack1 = HopperBlockEntity.addItem((Container) null, inventory, itemstack, (Direction) null);
@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
stack = stack.split(to.getMaxStackSize());
}
// Spigot end
+ ignoreTileUpdates = true; // Paper - Perf: Optimize Hoppers
to.setItem(slot, stack);
+ ignoreTileUpdates = false; // Paper - Perf: Optimize Hoppers
stack = leftover; // Paper - Make hoppers respect inventory max stack size
flag = true;
} else if (HopperBlockEntity.canMergeItems(itemstack1, stack)) {
@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@Nullable
public static Container getContainerAt(Level world, BlockPos pos) {
- return HopperBlockEntity.getContainerAt(world, pos, world.getBlockState(pos), (double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D);
+ return HopperBlockEntity.getContainerAt(world, pos, world.getBlockState(pos), (double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D, true);
}
@Nullable
private static Container getContainerAt(Level world, BlockPos pos, BlockState state, double x, double y, double z) {
+ // Paper start - Perf: Optimize Hoppers
+ return HopperBlockEntity.getContainerAt(world, pos, state, x, y, z, false);
+ }
+ @Nullable
+ private static Container getContainerAt(Level world, BlockPos pos, BlockState state, double x, double y, double z, boolean optimizeEntities) {
+ // Paper end - Perf: Optimize Hoppers
Container iinventory = HopperBlockEntity.getBlockContainer(world, pos, state);
- if (iinventory == null) {
+ if (iinventory == null && (!optimizeEntities || !world.paperConfig().hopper.ignoreOccludingBlocks || !state.getBukkitMaterial().isOccluding())) { // Paper - Perf: Optimize Hoppers
iinventory = HopperBlockEntity.getEntityContainer(world, x, y, z);
}
@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@Nullable
private static Container getEntityContainer(Level world, double x, double y, double z) {
- List<Entity> list = world.getEntities((Entity) null, new AABB(x - 0.5D, y - 0.5D, z - 0.5D, x + 0.5D, y + 0.5D, z + 0.5D), EntitySelector.CONTAINER_ENTITY_SELECTOR);
+ List<Entity> list = world.getEntitiesOfClass((Class) Container.class, new AABB(x - 0.5D, y - 0.5D, z - 0.5D, x + 0.5D, y + 0.5D, z + 0.5D), EntitySelector.CONTAINER_ENTITY_SELECTOR); // Paper - Perf: Optimize hoppers
return !list.isEmpty() ? (Container) list.get(world.random.nextInt(list.size())) : null;
}
private static boolean canMergeItems(ItemStack first, ItemStack second) {
- return first.getCount() <= first.getMaxStackSize() && ItemStack.isSameItemSameComponents(first, second);
+ return first.getCount() < first.getMaxStackSize() && ItemStack.isSameItemSameComponents(first, second); // Paper - Perf: Optimize Hoppers; used to return true for full itemstacks?!
}
@Override
diff --git a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
@@ -0,0 +0,0 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc
@Override
public ItemStack getItem(int slot) {
- this.unpackLootTable(null);
+ if (slot == 0) this.unpackLootTable(null); // Paper - Perf: Optimize Hoppers
return super.getItem(slot);
}

View file

@ -1,123 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
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/src/main/java/net/minecraft/world/phys/shapes/IndirectMerger.java b/src/main/java/net/minecraft/world/phys/shapes/IndirectMerger.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/phys/shapes/IndirectMerger.java
+++ b/src/main/java/net/minecraft/world/phys/shapes/IndirectMerger.java
@@ -0,0 +0,0 @@ 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 = new int[]{1, 1};
+ private static final int[] INFINITE_B_0 = new int[]{0, 0};
+ private static final int[] INFINITE_C = new int[]{0, 1};
+ // Paper end
public IndirectMerger(DoubleList first, DoubleList second, boolean includeFirstOnly, boolean includeSecondOnly) {
double d = Double.NaN;
int i = first.size();
int j = second.size();
int k = i + j;
+ // Paper start - optimize common path of infinity doublelist
+ int size = first.size();
+ double tail = first.getDouble(size - 1);
+ double head = first.getDouble(0);
+ if (head == Double.NEGATIVE_INFINITY && tail == Double.POSITIVE_INFINITY && !includeFirstOnly && !includeSecondOnly && (size == 2 || size == 4)) {
+ this.result = second.toDoubleArray();
+ this.resultLength = second.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[k];
this.firstIndices = new int[k];
this.secondIndices = new int[k];
diff --git a/src/main/java/net/minecraft/world/phys/shapes/Shapes.java b/src/main/java/net/minecraft/world/phys/shapes/Shapes.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/phys/shapes/Shapes.java
+++ b/src/main/java/net/minecraft/world/phys/shapes/Shapes.java
@@ -0,0 +0,0 @@ public final class Shapes {
}
@VisibleForTesting
- protected static IndexMerger createIndexMerger(int size, DoubleList first, DoubleList second, boolean includeFirst, boolean includeSecond) {
+ private static IndexMerger createIndexMerger(int size, DoubleList first, DoubleList second, boolean includeFirst, boolean includeSecond) { // 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 (first.getDouble(0) == Double.NEGATIVE_INFINITY && first.getDouble(first.size() - 1) == Double.POSITIVE_INFINITY) {
+ return new IndirectMerger(first, second, includeFirst, includeSecond);
+ }
+ // Split out rest to hopefully inline the above
+ return lessCommonMerge(size, first, second, includeFirst, includeSecond);
+ }
+
+ private static IndexMerger lessCommonMerge(int size, DoubleList first, DoubleList second, boolean includeFirst, boolean includeSecond) {
int i = first.size() - 1;
int j = second.size() - 1;
+ // Paper note - Rewrite below as optimized order if instead of nasty ternary
if (first instanceof CubePointRange && second instanceof CubePointRange) {
long l = lcm(i, j);
if ((long)size * l <= 256L) {
@@ -0,0 +0,0 @@ public final class Shapes {
}
}
- if (first.getDouble(i) < second.getDouble(0) - 1.0E-7) {
+ // Paper start - Identical happens more often than Disjoint
+ if (i == j && Objects.equals(first, second)) {
+ if (first instanceof IdenticalMerger) {
+ return (IndexMerger) first;
+ } else if (second instanceof IdenticalMerger) {
+ return (IndexMerger) second;
+ }
+ return new IdenticalMerger(first);
+ } else if (first.getDouble(i) < second.getDouble(0) - 1.0E-7) {
return new NonOverlappingMerger(first, second, false);
} else if (second.getDouble(j) < first.getDouble(0) - 1.0E-7) {
return new NonOverlappingMerger(second, first, true);
} else {
- return (IndexMerger)(i == j && Objects.equals(first, second)
- ? new IdenticalMerger(first)
- : new IndirectMerger(first, second, includeFirst, includeSecond));
+ return new IndirectMerger(first, second, includeFirst, includeSecond);
}
+ // Paper end
}
public interface DoubleLineConsumer {

View file

@ -1,213 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Thu, 4 Jun 2020 02:24:49 -0400
Subject: [PATCH] Optimize Bit Operations by inlining
Inline bit operations and reduce instruction count to make these hot
operations faster
diff --git a/src/main/java/net/minecraft/core/BlockPos.java b/src/main/java/net/minecraft/core/BlockPos.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/core/BlockPos.java
+++ b/src/main/java/net/minecraft/core/BlockPos.java
@@ -0,0 +0,0 @@ public class BlockPos extends Vec3i {
};
private static final Logger LOGGER = LogUtils.getLogger();
public static final BlockPos ZERO = new BlockPos(0, 0, 0);
- public static final int PACKED_HORIZONTAL_LENGTH = 1 + Mth.log2(Mth.smallestEncompassingPowerOfTwo(30000000));
- public static final int PACKED_Y_LENGTH = 64 - 2 * PACKED_HORIZONTAL_LENGTH;
- private static final long PACKED_X_MASK = (1L << PACKED_HORIZONTAL_LENGTH) - 1L;
- private static final long PACKED_Y_MASK = (1L << PACKED_Y_LENGTH) - 1L;
- private static final long PACKED_Z_MASK = (1L << PACKED_HORIZONTAL_LENGTH) - 1L;
+ // Paper start - Optimize Bit Operations by inlining
+ public static final int PACKED_HORIZONTAL_LENGTH = 26;
+ public static final int PACKED_Y_LENGTH = 12;
+ private static final long PACKED_X_MASK = 67108863;
+ private static final long PACKED_Y_MASK = 4095;
+ private static final long PACKED_Z_MASK = 67108863;
private static final int Y_OFFSET = 0;
- private static final int Z_OFFSET = PACKED_Y_LENGTH;
- private static final int X_OFFSET = PACKED_Y_LENGTH + PACKED_HORIZONTAL_LENGTH;
- public static final int MAX_HORIZONTAL_COORDINATE = (1 << PACKED_HORIZONTAL_LENGTH) / 2 - 1;
+ private static final int Z_OFFSET = 12;
+ private static final int X_OFFSET = 38;
+ public static final int MAX_HORIZONTAL_COORDINATE = 33554431;
+ // Paper end - Optimize Bit Operations by inlining
public BlockPos(int x, int y, int z) {
super(x, y, z);
@@ -0,0 +0,0 @@ public class BlockPos extends Vec3i {
this(pos.getX(), pos.getY(), pos.getZ());
}
+ public static long getAdjacent(int baseX, int baseY, int baseZ, Direction enumdirection) { return asLong(baseX + enumdirection.getStepX(), baseY + enumdirection.getStepY(), baseZ + enumdirection.getStepZ()); } // Paper
public static long offset(long value, Direction direction) {
return offset(value, direction.getStepX(), direction.getStepY(), direction.getStepZ());
}
public static long offset(long value, int x, int y, int z) {
- return asLong(getX(value) + x, getY(value) + y, getZ(value) + z);
+ return asLong((int) (value >> 38) + x, (int) ((value << 52) >> 52) + y, (int) ((value << 26) >> 38) + z); // Paper - simplify/inline
}
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 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 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 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 containing(double x, double y, double z) {
@@ -0,0 +0,0 @@ public class BlockPos extends Vec3i {
}
public static long asLong(int x, int y, int z) {
- long l = 0L;
- l |= ((long)x & PACKED_X_MASK) << X_OFFSET;
- l |= ((long)y & PACKED_Y_MASK) << 0;
- return l | ((long)z & PACKED_Z_MASK) << Z_OFFSET;
+ return (((long) x & (long) 67108863) << 38) | (((long) y & (long) 4095)) | (((long) z & (long) 67108863) << 12); // Paper - inline constants and simplify
}
public static long getFlatIndex(long y) {
diff --git a/src/main/java/net/minecraft/core/SectionPos.java b/src/main/java/net/minecraft/core/SectionPos.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/core/SectionPos.java
+++ b/src/main/java/net/minecraft/core/SectionPos.java
@@ -0,0 +0,0 @@ public class SectionPos extends Vec3i {
}
public static SectionPos of(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) {
@@ -0,0 +0,0 @@ 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 bottomOf(ChunkAccess chunk) {
@@ -0,0 +0,0 @@ public class SectionPos extends Vec3i {
return offset(packed, direction.getStepX(), direction.getStepY(), direction.getStepZ());
}
+ // Paper start
+ public static long getAdjacentFromBlockPos(int x, int y, int z, Direction enumdirection) {
+ return (((long) ((x >> 4) + enumdirection.getStepX()) & 4194303L) << 42) | (((long) ((y >> 4) + enumdirection.getStepY()) & 1048575L)) | (((long) ((z >> 4) + enumdirection.getStepZ()) & 4194303L) << 20);
+ }
+ public static long getAdjacentFromSectionPos(int x, int y, int z, Direction enumdirection) {
+ return (((long) (x + enumdirection.getStepX()) & 4194303L) << 42) | (((long) ((y) + enumdirection.getStepY()) & 1048575L)) | (((long) (z + enumdirection.getStepZ()) & 4194303L) << 20);
+ }
+ // Paper end
public static long offset(long packed, int x, int y, int z) {
- return asLong(x(packed) + x, y(packed) + y, z(packed) + z);
+ return (((long) ((int) (packed >> 42) + x) & 4194303L) << 42) | (((long) ((int) (packed << 44 >> 44) + y) & 1048575L)) | (((long) ((int) (packed << 22 >> 42) + z) & 4194303L) << 20); // Simplify to reduce instruction count
}
public static int posToSectionCoord(double coord) {
@@ -0,0 +0,0 @@ public class SectionPos extends Vec3i {
}
public static short sectionRelativePos(BlockPos pos) {
- int i = sectionRelative(pos.getX());
- int j = sectionRelative(pos.getY());
- int k = sectionRelative(pos.getZ());
- return (short)(i << 8 | k << 4 | j << 0);
+ return (short) ((pos.getX() & 15) << 8 | (pos.getZ() & 15) << 4 | pos.getY() & 15); // Paper - simplify/inline
}
public static int sectionRelativeX(short packedLocalPos) {
@@ -0,0 +0,0 @@ public class SectionPos extends Vec3i {
return this.getZ();
}
- public int minBlockX() {
- return sectionToBlockCoord(this.x());
+ public final int minBlockX() { // Paper - make final
+ return this.getX() << 4; // Paper - inline
}
- public int minBlockY() {
- return sectionToBlockCoord(this.y());
+ public final int minBlockY() { // Paper - make final
+ return this.getY() << 4; // Paper - inline
}
- public int minBlockZ() {
- return sectionToBlockCoord(this.z());
+ public int minBlockZ() { // Paper - make final
+ return this.getZ() << 4; // Paper - inline
}
public int maxBlockX() {
@@ -0,0 +0,0 @@ public class SectionPos extends Vec3i {
}
public static long blockToSection(long blockPos) {
- return asLong(blockToSectionCoord(BlockPos.getX(blockPos)), blockToSectionCoord(BlockPos.getY(blockPos)), blockToSectionCoord(BlockPos.getZ(blockPos)));
+ // b(a(BlockPosition.b(i)), a(BlockPosition.c(i)), a(BlockPosition.d(i)));
+ return (((long) (int) (blockPos >> 42) & 4194303L) << 42) | (((long) (int) ((blockPos << 52) >> 56) & 1048575L)) | (((long) (int) ((blockPos << 26) >> 42) & 4194303L) << 20); // Simplify to reduce instruction count
}
public static long getZeroNode(int x, int z) {
@@ -0,0 +0,0 @@ public class SectionPos extends Vec3i {
return asLong(blockToSectionCoord(pos.getX()), blockToSectionCoord(pos.getY()), blockToSectionCoord(pos.getZ()));
}
+ // Paper start
+ public static long blockPosAsSectionLong(int i, int j, int k) {
+ return (((long) (i >> 4) & 4194303L) << 42) | (((long) (j >> 4) & 1048575L)) | (((long) (k >> 4) & 4194303L) << 20);
+ }
+ // Paper end
+
public static long asLong(int x, int y, int z) {
- long l = 0L;
- l |= ((long)x & 4194303L) << 42;
- l |= ((long)y & 1048575L) << 0;
- return l | ((long)z & 4194303L) << 20;
+ return (((long) x & 4194303L) << 42) | (((long) y & 1048575L)) | (((long) z & 4194303L) << 20); // Paper - Simplify to reduce instruction count
}
public long asLong() {
- return asLong(this.x(), this.y(), this.z());
+ return (((long) getX() & 4194303L) << 42) | (((long) getY() & 1048575L)) | (((long) getZ() & 4194303L) << 20); // Paper - Simplify to reduce instruction count
}
@Override
@@ -0,0 +0,0 @@ public class SectionPos extends Vec3i {
}
public static Stream<SectionPos> cube(SectionPos center, int radius) {
- int i = center.x();
- int j = center.y();
- int k = center.z();
- return betweenClosedStream(i - radius, j - radius, k - radius, i + radius, j + radius, k + 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<SectionPos> aroundChunk(ChunkPos center, int radius, int minY, int maxY) {
- int i = center.x;
- int j = center.z;
- return betweenClosedStream(i - radius, minY, j - radius, i + radius, maxY, j + radius);
+ return betweenClosedStream(center.x - radius, minY, center.z - radius, center.x + radius, maxY, center.z + radius); // Paper - simplify/inline
}
public static Stream<SectionPos> betweenClosedStream(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {

View file

@ -1,215 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Josh Roy <10731363+JRoy@users.noreply.github.com>
Date: Wed, 1 Jul 2020 18:01:49 -0400
Subject: [PATCH] Remove streams from hot code
Co-authored-by: Bjarne Koll <git@lynxplay.dev>
Co-authored-by: Spottedleaf <Spottedleaf@users.noreply.github.com>
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java b/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java
@@ -0,0 +0,0 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E>
if (this.hasRequiredMemories(entity)) {
this.status = Behavior.Status.RUNNING;
this.orderPolicy.apply(this.behaviors);
- this.runningPolicy.apply(this.behaviors.stream(), world, entity, time);
+ this.runningPolicy.apply(this.behaviors, world, entity, time); // Paper - Perf: Remove streams from hot code
return true;
} else {
return false;
@@ -0,0 +0,0 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E>
@Override
public final void tickOrStop(ServerLevel world, E entity, long time) {
- this.behaviors.stream().filter(task -> task.getStatus() == Behavior.Status.RUNNING).forEach(task -> task.tickOrStop(world, entity, time));
+ // Paper start - Perf: Remove streams from hot code
+ for (final BehaviorControl<? super E> task : this.behaviors) {
+ if (task.getStatus() == Behavior.Status.RUNNING) {
+ task.tickOrStop(world, entity, time);
+ }
+ }
+ // Paper end - Perf: Remove streams from hot code
if (this.behaviors.stream().noneMatch(task -> task.getStatus() == Behavior.Status.RUNNING)) {
this.doStop(world, entity, time);
}
@@ -0,0 +0,0 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E>
@Override
public final void doStop(ServerLevel world, E entity, long time) {
this.status = Behavior.Status.STOPPED;
- this.behaviors.stream().filter(task -> task.getStatus() == Behavior.Status.RUNNING).forEach(task -> task.doStop(world, entity, time));
- this.exitErasedMemories.forEach(entity.getBrain()::eraseMemory);
+ // Paper start - Perf: Remove streams from hot code
+ for (final BehaviorControl<? super E> task : this.behaviors) {
+ if (task.getStatus() == Behavior.Status.RUNNING) {
+ task.doStop(world, entity, time);
+ }
+ }
+ for (final MemoryModuleType<?> exitErasedMemory : this.exitErasedMemories) {
+ entity.getBrain().eraseMemory(exitErasedMemory);
+ }
+ // Paper end - Perf: Remove streams from hot code
}
@Override
@@ -0,0 +0,0 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E>
public static enum RunningPolicy {
RUN_ONE {
+ // Paper start - Perf: Remove streams from hot code
@Override
- public <E extends LivingEntity> void apply(Stream<BehaviorControl<? super E>> tasks, ServerLevel world, E entity, long time) {
- tasks.filter(task -> task.getStatus() == Behavior.Status.STOPPED).filter(task -> task.tryStart(world, entity, time)).findFirst();
+ public <E extends LivingEntity> void apply(ShufflingList<BehaviorControl<? super E>> tasks, ServerLevel world, E entity, long time) {
+ for (final BehaviorControl<? super E> task : tasks) {
+ if (task.getStatus() == Behavior.Status.STOPPED && task.tryStart(world, entity, time)) {
+ break;
+ }
+ }
+ // Paper end - Perf: Remove streams from hot code
}
},
TRY_ALL {
+ // Paper start - Perf: Remove streams from hot code
@Override
- public <E extends LivingEntity> void apply(Stream<BehaviorControl<? super E>> tasks, ServerLevel world, E entity, long time) {
- tasks.filter(task -> task.getStatus() == Behavior.Status.STOPPED).forEach(task -> task.tryStart(world, entity, time));
+ public <E extends LivingEntity> void apply(ShufflingList<BehaviorControl<? super E>> tasks, ServerLevel world, E entity, long time) {
+ for (final BehaviorControl<? super E> task : tasks) {
+ if (task.getStatus() == Behavior.Status.STOPPED) {
+ task.tryStart(world, entity, time);
+ }
+ }
+ // Paper end - Perf: Remove streams from hot code
}
};
- public abstract <E extends LivingEntity> void apply(Stream<BehaviorControl<? super E>> tasks, ServerLevel world, E entity, long time);
+ public abstract <E extends LivingEntity> void apply(ShufflingList<BehaviorControl<? super E>> tasks, ServerLevel world, E entity, long time); // Paper - Perf: Remove streams from hot code
}
}
diff --git a/src/main/java/net/minecraft/world/entity/ai/gossip/GossipContainer.java b/src/main/java/net/minecraft/world/entity/ai/gossip/GossipContainer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/ai/gossip/GossipContainer.java
+++ b/src/main/java/net/minecraft/world/entity/ai/gossip/GossipContainer.java
@@ -0,0 +0,0 @@ public class GossipContainer {
return this.gossips.entrySet().stream().flatMap(entry -> entry.getValue().unpack(entry.getKey()));
}
+ // Paper start - Perf: Remove streams from hot code
+ private List<GossipContainer.GossipEntry> decompress() {
+ List<GossipContainer.GossipEntry> list = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>();
+ for (Map.Entry<UUID, GossipContainer.EntityGossips> entry : this.gossips.entrySet()) {
+ for (GossipContainer.GossipEntry cur : entry.getValue().decompress(entry.getKey())) {
+ if (cur.weightedValue() != 0) {
+ list.add(cur);
+ }
+ }
+ }
+ return list;
+ }
+ // Paper end - Perf: Remove streams from hot code
+
private Collection<GossipContainer.GossipEntry> selectGossipsForTransfer(RandomSource random, int count) {
- List<GossipContainer.GossipEntry> list = this.unpack().toList();
+ List<GossipContainer.GossipEntry> list = this.decompress(); // Paper - Perf: Remove streams from hot code
if (list.isEmpty()) {
return Collections.emptyList();
} else {
@@ -0,0 +0,0 @@ public class GossipContainer {
public <T> T store(DynamicOps<T> ops) {
return GossipContainer.GossipEntry.LIST_CODEC
- .encodeStart(ops, this.unpack().toList())
+ .encodeStart(ops, this.decompress()) // Paper - Perf: Remove streams from hot code
.resultOrPartial(error -> LOGGER.warn("Failed to serialize gossips: {}", error))
.orElseGet(ops::emptyList);
}
@@ -0,0 +0,0 @@ public class GossipContainer {
final Object2IntMap<GossipType> entries = new Object2IntOpenHashMap<>();
public int weightedValue(Predicate<GossipType> gossipTypeFilter) {
- return this.entries
- .object2IntEntrySet()
- .stream()
- .filter(entry -> gossipTypeFilter.test(entry.getKey()))
- .mapToInt(entry -> entry.getIntValue() * entry.getKey().weight)
- .sum();
+ // Paper start - Perf: Remove streams from hot code
+ int weight = 0;
+ for (Object2IntMap.Entry<GossipType> entry : entries.object2IntEntrySet()) {
+ if (gossipTypeFilter.test(entry.getKey())) {
+ weight += entry.getIntValue() * entry.getKey().weight;
+ }
+ }
+ return weight;
+ }
+
+ public List<GossipContainer.GossipEntry> decompress(UUID uuid) {
+ List<GossipContainer.GossipEntry> list = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>();
+ for (Object2IntMap.Entry<GossipType> entry : entries.object2IntEntrySet()) {
+ list.add(new GossipContainer.GossipEntry(uuid, entry.getKey(), entry.getIntValue()));
+ }
+ return list;
+ // Paper end - Perf: Remove streams from hot code
}
public Stream<GossipContainer.GossipEntry> unpack(UUID target) {
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java
@@ -0,0 +0,0 @@ public class NearestItemSensor extends Sensor<Mob> {
@Override
protected void doTick(ServerLevel world, Mob entity) {
Brain<?> brain = entity.getBrain();
- List<ItemEntity> list = world.getEntitiesOfClass(ItemEntity.class, entity.getBoundingBox().inflate(32.0, 16.0, 32.0), itemEntity -> true);
+ List<ItemEntity> list = world.getEntitiesOfClass(ItemEntity.class, entity.getBoundingBox().inflate(32.0, 16.0, 32.0), itemEntity -> itemEntity.closerThan(entity, MAX_DISTANCE_TO_WANTED_ITEM) && entity.wantsToPickUp(world, itemEntity.getItem())); // Paper - Perf: Move predicate into getEntities
list.sort(Comparator.comparingDouble(entity::distanceToSqr));
- Optional<ItemEntity> optional = list.stream()
- .filter(itemEntity -> entity.wantsToPickUp(world, itemEntity.getItem()))
- .filter(itemEntityx -> itemEntityx.closerThan(entity, 32.0))
- .filter(entity::hasLineOfSight)
- .findFirst();
- brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM, optional);
+ // Paper start - Perf: remove streams from hot code
+ ItemEntity nearest = null;
+ for (ItemEntity entityItem : list) {
+ if (entity.hasLineOfSight(entityItem)) { // Paper - Perf: Move predicate into getEntities
+ nearest = entityItem;
+ break;
+ }
+ }
+ brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM, Optional.ofNullable(nearest));
+ // Paper end - Perf: remove streams from hot code
}
}
diff --git a/src/main/java/net/minecraft/world/level/levelgen/Beardifier.java b/src/main/java/net/minecraft/world/level/levelgen/Beardifier.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/levelgen/Beardifier.java
+++ b/src/main/java/net/minecraft/world/level/levelgen/Beardifier.java
@@ -0,0 +0,0 @@ public class Beardifier implements DensityFunctions.BeardifierOrMarker {
int j = pos.getMinBlockZ();
ObjectList<Beardifier.Rigid> objectList = new ObjectArrayList<>(10);
ObjectList<JigsawJunction> objectList2 = new ObjectArrayList<>(32);
- world.startsForStructure(pos, structure -> structure.terrainAdaptation() != TerrainAdjustment.NONE)
- .forEach(
- start -> {
+ // Paper start - Perf: Remove streams from hot code
+ for (net.minecraft.world.level.levelgen.structure.StructureStart start : world.startsForStructure(pos, (structure) -> {
+ return structure.terrainAdaptation() != TerrainAdjustment.NONE;
+ })) { // Paper end - Perf: Remove streams from hot code
TerrainAdjustment terrainAdjustment = start.getStructure().terrainAdaptation();
for (StructurePiece structurePiece : start.getPieces()) {
@@ -0,0 +0,0 @@ public class Beardifier implements DensityFunctions.BeardifierOrMarker {
}
}
}
- }
- );
+ } // Paper - Perf: Remove streams from hot code
return new Beardifier(objectList.iterator(), objectList2.iterator());
}

View file

@ -1,152 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Sun, 22 Aug 2021 15:21:57 -0700
Subject: [PATCH] Fix entity type tags suggestions in selectors
This would really be better as a client fix because just to fix it
all EntityArguments have been told to ask the server for completions
when if this was fixed on the client, that wouldn't be needed.
Mojira Issue: https://bugs.mojang.com/browse/MC-235045
diff --git a/src/main/java/net/minecraft/commands/CommandSourceStack.java b/src/main/java/net/minecraft/commands/CommandSourceStack.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/commands/CommandSourceStack.java
+++ b/src/main/java/net/minecraft/commands/CommandSourceStack.java
@@ -0,0 +0,0 @@ public class CommandSourceStack implements ExecutionCommandSource<CommandSourceS
return this.source.getBukkitSender(this);
}
// CraftBukkit end
+ // Paper start - tell clients to ask server for suggestions for EntityArguments
+ @Override
+ public Collection<String> getSelectedEntities() {
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().commands.fixTargetSelectorTagCompletion && this.source instanceof ServerPlayer player) {
+ final Entity cameraEntity = player.getCamera();
+ final double pickDistance = player.entityInteractionRange();
+ final Vec3 min = cameraEntity.getEyePosition(1.0F);
+ final Vec3 viewVector = cameraEntity.getViewVector(1.0F);
+ final Vec3 max = min.add(viewVector.x * pickDistance, viewVector.y * pickDistance, viewVector.z * pickDistance);
+ final net.minecraft.world.phys.AABB aabb = cameraEntity.getBoundingBox().expandTowards(viewVector.scale(pickDistance)).inflate(1.0D, 1.0D, 1.0D);
+ final net.minecraft.world.phys.EntityHitResult hitResult = net.minecraft.world.entity.projectile.ProjectileUtil.getEntityHitResult(cameraEntity, min, max, aabb, (e) -> !e.isSpectator() && e.isPickable(), pickDistance * pickDistance);
+ return hitResult != null ? java.util.Collections.singletonList(hitResult.getEntity().getStringUUID()) : SharedSuggestionProvider.super.getSelectedEntities();
+ }
+ return SharedSuggestionProvider.super.getSelectedEntities();
+ }
+ // Paper end - tell clients to ask server for suggestions for EntityArguments
}
diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/commands/Commands.java
+++ b/src/main/java/net/minecraft/commands/Commands.java
@@ -0,0 +0,0 @@ public class Commands {
Iterator iterator = children.iterator();
// Paper end - Perf: Async command map building
+ boolean registeredAskServerSuggestionsForTree = false; // Paper - tell clients to ask server for suggestions for EntityArguments
while (iterator.hasNext()) {
CommandNode<CommandSourceStack> commandnode2 = (CommandNode) iterator.next();
// Paper start - Brigadier API
@@ -0,0 +0,0 @@ public class Commands {
if (requiredargumentbuilder.getSuggestionsProvider() != null) {
requiredargumentbuilder.suggests(SuggestionProviders.safelySwap(requiredargumentbuilder.getSuggestionsProvider()));
+ // Paper start - tell clients to ask server for suggestions for EntityArguments
+ registeredAskServerSuggestionsForTree = requiredargumentbuilder.getSuggestionsProvider() == net.minecraft.commands.synchronization.SuggestionProviders.ASK_SERVER;
+ } else if (io.papermc.paper.configuration.GlobalConfiguration.get().commands.fixTargetSelectorTagCompletion && !registeredAskServerSuggestionsForTree && requiredargumentbuilder.getType() instanceof net.minecraft.commands.arguments.EntityArgument) {
+ requiredargumentbuilder.suggests(requiredargumentbuilder.getType()::listSuggestions);
+ registeredAskServerSuggestionsForTree = true; // You can only
+ // Paper end - tell clients to ask server for suggestions for EntityArguments
}
}
diff --git a/src/main/java/net/minecraft/commands/arguments/EntityArgument.java b/src/main/java/net/minecraft/commands/arguments/EntityArgument.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/commands/arguments/EntityArgument.java
+++ b/src/main/java/net/minecraft/commands/arguments/EntityArgument.java
@@ -0,0 +0,0 @@ public class EntityArgument implements ArgumentType<EntitySelector> {
final boolean permission = object instanceof CommandSourceStack stack
? stack.bypassSelectorPermissions || stack.hasPermission(2, "minecraft.command.selector")
: icompletionprovider.hasPermission(2);
- EntitySelectorParser argumentparserselector = new EntitySelectorParser(stringreader, permission);
+ EntitySelectorParser argumentparserselector = new EntitySelectorParser(stringreader, permission, true); // Paper - tell clients to ask server for suggestions for EntityArguments
// Paper end - Fix EntityArgument permissions
try {
@@ -0,0 +0,0 @@ public class EntityArgument implements ArgumentType<EntitySelector> {
}
return argumentparserselector.fillSuggestions(suggestionsbuilder, (suggestionsbuilder1) -> {
- Collection<String> collection = icompletionprovider.getOnlinePlayerNames();
+ // Paper start - tell clients to ask server for suggestions for EntityArguments
+ final Collection<String> collection;
+ if (icompletionprovider instanceof CommandSourceStack commandSourceStack && commandSourceStack.getEntity() instanceof ServerPlayer sourcePlayer) {
+ collection = new java.util.ArrayList<>();
+ for (final ServerPlayer player : commandSourceStack.getServer().getPlayerList().getPlayers()) {
+ if (sourcePlayer.getBukkitEntity().canSee(player.getBukkitEntity())) {
+ collection.add(player.getGameProfile().getName());
+ }
+ }
+ } else {
+ collection = icompletionprovider.getOnlinePlayerNames();
+ }
+ // Paper end - tell clients to ask server for suggestions for EntityArguments
Iterable<String> iterable = this.playersOnly ? collection : Iterables.concat(collection, icompletionprovider.getSelectedEntities());
SharedSuggestionProvider.suggest((Iterable) iterable, suggestionsbuilder1);
diff --git a/src/main/java/net/minecraft/commands/arguments/selector/EntitySelectorParser.java b/src/main/java/net/minecraft/commands/arguments/selector/EntitySelectorParser.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/commands/arguments/selector/EntitySelectorParser.java
+++ b/src/main/java/net/minecraft/commands/arguments/selector/EntitySelectorParser.java
@@ -0,0 +0,0 @@ public class EntitySelectorParser {
private boolean hasScores;
private boolean hasAdvancements;
private boolean usesSelectors;
+ public boolean parsingEntityArgumentSuggestions; // Paper - tell clients to ask server for suggestions for EntityArguments
public EntitySelectorParser(StringReader reader, boolean atAllowed) {
+ // Paper start - tell clients to ask server for suggestions for EntityArguments
+ this(reader, atAllowed, false);
+ }
+ public EntitySelectorParser(StringReader reader, boolean atAllowed, boolean parsingEntityArgumentSuggestions) {
+ this.parsingEntityArgumentSuggestions = parsingEntityArgumentSuggestions;
+ // Paper end - tell clients to ask server for suggestions for EntityArguments
this.distance = MinMaxBounds.Doubles.ANY;
this.level = MinMaxBounds.Ints.ANY;
this.rotX = WrappedMinMaxBounds.ANY;
diff --git a/src/main/java/net/minecraft/commands/arguments/selector/options/EntitySelectorOptions.java b/src/main/java/net/minecraft/commands/arguments/selector/options/EntitySelectorOptions.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/commands/arguments/selector/options/EntitySelectorOptions.java
+++ b/src/main/java/net/minecraft/commands/arguments/selector/options/EntitySelectorOptions.java
@@ -0,0 +0,0 @@ public class EntitySelectorOptions {
public static final DynamicCommandExceptionType ERROR_ENTITY_TYPE_INVALID = new DynamicCommandExceptionType(
entity -> Component.translatableEscape("argument.entity.options.type.invalid", entity)
);
+ // Paper start - tell clients to ask server for suggestions for EntityArguments
+ public static final DynamicCommandExceptionType ERROR_ENTITY_TAG_INVALID = new DynamicCommandExceptionType((object) -> {
+ return io.papermc.paper.adventure.PaperAdventure
+ .asVanilla(net.kyori.adventure.text.Component
+ .text("Invalid or unknown entity type tag '" + object + "'")
+ .hoverEvent(net.kyori.adventure.text.event.HoverEvent
+ .showText(net.kyori.adventure.text.Component
+ .text("You can disable this error in 'paper.yml'")
+ )
+ )
+ );
+ });
+ // Paper end - tell clients to ask server for suggestions for EntityArguments
private static void register(String id, EntitySelectorOptions.Modifier handler, Predicate<EntitySelectorParser> condition, Component description) {
OPTIONS.put(id, new EntitySelectorOptions.Option(handler, condition, description));
@@ -0,0 +0,0 @@ public class EntitySelectorOptions {
if (reader.isTag()) {
TagKey<EntityType<?>> tagKey = TagKey.create(Registries.ENTITY_TYPE, ResourceLocation.read(reader.getReader()));
+ // Paper start - tell clients to ask server for suggestions for EntityArguments; throw error if invalid entity tag (only on suggestions to keep cmd success behavior)
+ if (reader.parsingEntityArgumentSuggestions && io.papermc.paper.configuration.GlobalConfiguration.get().commands.fixTargetSelectorTagCompletion && net.minecraft.core.registries.BuiltInRegistries.ENTITY_TYPE.get(tagKey).isEmpty()) {
+ reader.getReader().setCursor(i);
+ throw ERROR_ENTITY_TAG_INVALID.createWithContext(reader.getReader(), tagKey);
+ }
+ // Paper end - tell clients to ask server for suggestions for EntityArguments
reader.addPredicate(entity -> entity.getType().is(tagKey) != bl);
} else {
ResourceLocation resourceLocation = ResourceLocation.read(reader.getReader());

View file

@ -1,68 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Mon, 2 Aug 2021 10:10:40 +0200
Subject: [PATCH] Check distance in entity interactions
diff --git a/src/main/java/net/minecraft/Util.java b/src/main/java/net/minecraft/Util.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/Util.java
+++ b/src/main/java/net/minecraft/Util.java
@@ -0,0 +0,0 @@ public class Util {
.filter(fileSystemProvider -> fileSystemProvider.getScheme().equalsIgnoreCase("jar"))
.findFirst()
.orElseThrow(() -> new IllegalStateException("No jar file system provider found"));
+ public static final double COLLISION_EPSILON = 1.0E-7; // Paper - Check distance in entity interactions
private static Consumer<String> thePauser = message -> {
};
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -0,0 +0,0 @@ public abstract class LivingEntity extends Entity implements Attackable {
if (!source.is(DamageTypeTags.IS_PROJECTILE)) {
Entity entity = source.getDirectEntity();
- if (entity instanceof LivingEntity) {
+ if (entity instanceof LivingEntity && entity.distanceToSqr(this) <= (200.0D * 200.0D)) { // Paper - Check distance in entity interactions
LivingEntity entityliving = (LivingEntity) entity;
this.blockUsingShield(entityliving);
@@ -0,0 +0,0 @@ public abstract class LivingEntity extends Entity implements Attackable {
d0 = source.getSourcePosition().x() - this.getX();
d1 = source.getSourcePosition().z() - this.getZ();
}
+ // Paper start - Check distance in entity interactions; see for loop in knockback method
+ if (Math.abs(d0) > 200) {
+ d0 = Math.random() - Math.random();
+ }
+ if (Math.abs(d1) > 200) {
+ d1 = Math.random() - Math.random();
+ }
+ // Paper end - Check distance in entity interactions
this.knockback(0.4000000059604645D, d0, d1, entity1, entity1 == null ? io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.DAMAGE : io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.ENTITY_ATTACK); // CraftBukkit // Paper - knockback events
if (!flag) {
@@ -0,0 +0,0 @@ public abstract class LivingEntity extends Entity implements Attackable {
this.hurtCurrentlyUsedShield((float) -event.getDamage(DamageModifier.BLOCKING));
Entity entity = damagesource.getDirectEntity();
- if (!damagesource.is(DamageTypeTags.IS_PROJECTILE) && entity instanceof LivingEntity) { // Paper - Fix shield disable inconsistency
+ if (!damagesource.is(DamageTypeTags.IS_PROJECTILE) && entity instanceof LivingEntity && entity.distanceToSqr(this) <= (200.0D * 200.0D)) { // Paper - Fix shield disable inconsistency & Check distance in entity interactions
this.blockUsingShield((LivingEntity) entity);
}
}
diff --git a/src/main/java/net/minecraft/world/entity/vehicle/AbstractBoat.java b/src/main/java/net/minecraft/world/entity/vehicle/AbstractBoat.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractBoat.java
+++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractBoat.java
@@ -0,0 +0,0 @@ public abstract class AbstractBoat extends VehicleEntity implements Leashable {
double d2 = (double) (this.getWaterLevelAbove() - this.getBbHeight()) + 0.101D;
if (this.level().noCollision(this, this.getBoundingBox().move(0.0D, d2 - this.getY(), 0.0D))) {
- this.setPos(this.getX(), d2, this.getZ());
+ this.move(MoverType.SELF, new Vec3(0.0D, d2 - this.getY(), 0.0D)); // Paper - Check distance in entity interactions // TODO Still needed??
this.setDeltaMovement(this.getDeltaMovement().multiply(1.0D, 0.0D, 1.0D));
this.lastYd = 0.0D;
}

View file

@ -1,78 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: lukas81298 <lukas81298@gommehd.net>
Date: Fri, 22 Jan 2021 21:50:18 +0100
Subject: [PATCH] optimize dirt and snow spreading
diff --git a/src/main/java/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java b/src/main/java/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java
@@ -0,0 +0,0 @@ public abstract class SpreadingSnowyDirtBlock extends SnowyDirtBlock {
}
private static boolean canBeGrass(BlockState state, LevelReader world, BlockPos pos) {
+ // Paper start - Perf: optimize dirt and snow spreading
+ return canBeGrass(world.getChunk(pos), state, world, pos);
+ }
+ private static boolean canBeGrass(net.minecraft.world.level.chunk.ChunkAccess chunk, BlockState state, LevelReader world, BlockPos pos) {
+ // Paper end - Perf: optimize dirt and snow spreading
BlockPos blockposition1 = pos.above();
- BlockState iblockdata1 = world.getBlockState(blockposition1);
+ BlockState iblockdata1 = chunk.getBlockState(blockposition1); // Paper - Perf: optimize dirt and snow spreading
if (iblockdata1.is(Blocks.SNOW) && (Integer) iblockdata1.getValue(SnowLayerBlock.LAYERS) == 1) {
return true;
@@ -0,0 +0,0 @@ public abstract class SpreadingSnowyDirtBlock extends SnowyDirtBlock {
protected abstract MapCodec<? extends SpreadingSnowyDirtBlock> codec();
private static boolean canPropagate(BlockState state, LevelReader world, BlockPos pos) {
+ // Paper start - Perf: optimize dirt and snow spreading
+ return canPropagate(world.getChunk(pos), state, world, pos);
+ }
+
+ private static boolean canPropagate(net.minecraft.world.level.chunk.ChunkAccess chunk, BlockState state, LevelReader world, BlockPos pos) {
+ // Paper end - Perf: optimize dirt and snow spreading
BlockPos blockposition1 = pos.above();
- return SpreadingSnowyDirtBlock.canBeGrass(state, world, pos) && !world.getFluidState(blockposition1).is(FluidTags.WATER);
+ return SpreadingSnowyDirtBlock.canBeGrass(chunk, state, world, pos) && !chunk.getFluidState(blockposition1).is(FluidTags.WATER); // Paper - Perf: optimize dirt and snow spreading
}
@Override
protected void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
if (this instanceof GrassBlock && world.paperConfig().tickRates.grassSpread != 1 && (world.paperConfig().tickRates.grassSpread < 1 || (net.minecraft.server.MinecraftServer.currentTick + pos.hashCode()) % world.paperConfig().tickRates.grassSpread != 0)) { return; } // Paper - Configurable random tick rates for blocks
- if (!SpreadingSnowyDirtBlock.canBeGrass(state, world, pos)) {
+ // Paper start - Perf: optimize dirt and snow spreading
+ final net.minecraft.world.level.chunk.ChunkAccess cachedBlockChunk = world.getChunkIfLoaded(pos);
+ if (cachedBlockChunk == null) { // Is this needed?
+ return;
+ }
+ if (!SpreadingSnowyDirtBlock.canBeGrass(cachedBlockChunk, state, world, pos)) {
+ // Paper end - Perf: optimize dirt and snow spreading
// CraftBukkit start
if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, pos, Blocks.DIRT.defaultBlockState()).isCancelled()) {
return;
@@ -0,0 +0,0 @@ public abstract class SpreadingSnowyDirtBlock extends SnowyDirtBlock {
for (int i = 0; i < 4; ++i) {
BlockPos blockposition1 = pos.offset(random.nextInt(3) - 1, random.nextInt(5) - 3, random.nextInt(3) - 1);
- if (world.getBlockState(blockposition1).is(Blocks.DIRT) && SpreadingSnowyDirtBlock.canPropagate(iblockdata1, world, blockposition1)) {
- org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(world, pos, blockposition1, (BlockState) iblockdata1.setValue(SpreadingSnowyDirtBlock.SNOWY, isSnowySetting(world.getBlockState(blockposition1.above())))); // CraftBukkit
+ // Paper start - Perf: optimize dirt and snow spreading
+ if (pos.getX() == blockposition1.getX() && pos.getY() == blockposition1.getY() && pos.getZ() == blockposition1.getZ()) {
+ continue;
+ }
+
+ final net.minecraft.world.level.chunk.ChunkAccess access;
+ if (cachedBlockChunk.locX == blockposition1.getX() >> 4 && cachedBlockChunk.locZ == blockposition1.getZ() >> 4) {
+ access = cachedBlockChunk;
+ } else {
+ access = world.getChunkAt(blockposition1);
+ }
+ if (access.getBlockState(blockposition1).is(Blocks.DIRT) && SpreadingSnowyDirtBlock.canPropagate(access, iblockdata1, world, blockposition1)) {
+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(world, pos, blockposition1, (BlockState) iblockdata1.setValue(SpreadingSnowyDirtBlock.SNOWY, isSnowySetting(access.getBlockState(blockposition1.above())))); // CraftBukkit
+ // Paper end - Perf: optimize dirt and snow spreading
}
}
}

View file

@ -1,60 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Sat, 25 Jan 2020 17:04:35 -0800
Subject: [PATCH] Optimise getChunkAt calls for loaded chunks
bypass the need to get a player chunk, then get the either,
then unwrap it...
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource {
return this.getChunk(x, z, leastStatus, create);
}, this.mainThreadProcessor).join();
} else {
+ // Paper start - Perf: Optimise getChunkAt calls for loaded chunks
+ LevelChunk ifLoaded = this.getChunkAtIfLoadedMainThread(x, z);
+ if (ifLoaded != null) {
+ return ifLoaded;
+ }
+ // Paper end - Perf: Optimise getChunkAt calls for loaded chunks
ProfilerFiller gameprofilerfiller = Profiler.get();
gameprofilerfiller.incrementCounter("getChunk");
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource {
if (Thread.currentThread() != this.mainThread) {
return null;
} else {
- Profiler.get().incrementCounter("getChunkNow");
- long k = ChunkPos.asLong(chunkX, chunkZ);
-
- ChunkAccess ichunkaccess;
-
- for (int l = 0; l < 4; ++l) {
- if (k == this.lastChunkPos[l] && this.lastChunkStatus[l] == ChunkStatus.FULL) {
- ichunkaccess = this.lastChunk[l];
- return ichunkaccess instanceof LevelChunk ? (LevelChunk) ichunkaccess : null;
- }
- }
-
- ChunkHolder playerchunk = this.getVisibleChunkIfPresent(k);
-
- if (playerchunk == null) {
- return null;
- } else {
- ichunkaccess = playerchunk.getChunkIfPresent(ChunkStatus.FULL);
- if (ichunkaccess != null) {
- this.storeInCache(k, ichunkaccess, ChunkStatus.FULL);
- if (ichunkaccess instanceof LevelChunk) {
- return (LevelChunk) ichunkaccess;
- }
- }
-
- return null;
- }
+ return this.getChunkAtIfLoadedMainThread(chunkX, chunkZ); // Paper - Perf: Optimise getChunkAt calls for loaded chunks
}
}

View file

@ -1,455 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sun, 12 Apr 2020 15:50:48 -0400
Subject: [PATCH] Improved Watchdog Support
Forced Watchdog Crash support and Improve Async Shutdown
If the request to shut down the server is received while we are in
a watchdog hang, immediately treat it as a crash and begin the shutdown
process. Shutdown process is now improved to also shutdown cleanly when
not using restart scripts either.
If a server is deadlocked, a server owner can send SIGUP (or any other signal
the JVM understands to shut down as it currently does) and the watchdog
will no longer need to wait until the full timeout, allowing you to trigger
a close process and try to shut the server down gracefully, saving player and
world data.
Previously there was no way to trigger this outside of waiting for a full watchdog
timeout, which may be set to a really long time...
Additionally, fix everything to do with shutting the server down asynchronously.
Previously, nearly everything about the process was fragile and unsafe. Main might
not have actually been frozen, and might still be manipulating state.
Or, some reuest might ask main to do something in the shutdown but main is dead.
Or worse, other things might start closing down items such as the Console or Thread Pool
before we are fully shutdown.
This change tries to resolve all of these issues by moving everything into the stop
method and guaranteeing only one thread is stopping the server.
We then issue Thread Death to the main thread of another thread initiates the stop process.
We have to ensure Thread Death propagates correctly though to stop main completely.
This is to ensure that if main isn't truely stuck, it's not manipulating state we are trying to save.
This also moves all plugins who register "delayed init" tasks to occur just before "Done" so they
are properly accounted for and wont trip watchdog on init.
diff --git a/src/main/java/io/papermc/paper/util/LogManagerShutdownThread.java b/src/main/java/io/papermc/paper/util/LogManagerShutdownThread.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/io/papermc/paper/util/LogManagerShutdownThread.java
@@ -0,0 +0,0 @@
+package io.papermc.paper.util;
+
+public class LogManagerShutdownThread extends Thread {
+
+ static LogManagerShutdownThread INSTANCE = new LogManagerShutdownThread();
+ public static final void hook() {
+ if (INSTANCE == null) {
+ throw new IllegalStateException("Cannot re-hook after being unhooked");
+ }
+ Runtime.getRuntime().addShutdownHook(INSTANCE);
+ }
+
+ public static final void unhook() {
+ Runtime.getRuntime().removeShutdownHook(INSTANCE);
+ INSTANCE = null;
+ }
+
+ private LogManagerShutdownThread() {
+ super("Log4j2 Shutdown Thread");
+ }
+
+ @Override
+ public void run() {
+ org.apache.logging.log4j.LogManager.shutdown();
+ }
+}
diff --git a/src/main/java/net/minecraft/CrashReport.java b/src/main/java/net/minecraft/CrashReport.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/CrashReport.java
+++ b/src/main/java/net/minecraft/CrashReport.java
@@ -0,0 +0,0 @@ public class CrashReport {
}
public static CrashReport forThrowable(Throwable cause, String title) {
+ if (cause instanceof ThreadDeath) com.destroystokyo.paper.util.SneakyThrow.sneaky(cause); // Paper
while (cause instanceof CompletionException && cause.getCause() != null) {
cause = cause.getCause();
}
diff --git a/src/main/java/net/minecraft/server/Main.java b/src/main/java/net/minecraft/server/Main.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/Main.java
+++ b/src/main/java/net/minecraft/server/Main.java
@@ -0,0 +0,0 @@ public class Main {
@SuppressForbidden(reason = "System.out needed before bootstrap") // CraftBukkit - decompile error
@DontObfuscate
public static void main(final OptionSet optionset) { // CraftBukkit - replaces main(String[] astring)
+ io.papermc.paper.util.LogManagerShutdownThread.hook(); // Paper
SharedConstants.tryDetectVersion();
/* CraftBukkit start - Replace everything
OptionParser optionparser = new OptionParser();
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
public java.util.Queue<Runnable> processQueue = new java.util.concurrent.ConcurrentLinkedQueue<Runnable>();
public int autosavePeriod;
// Paper - don't store the vanilla dispatcher
- private boolean forceTicks;
+ public boolean forceTicks; // Paper - Improved watchdog support
// CraftBukkit end
// Spigot start
public static final int TPS = 20;
@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
public boolean isIteratingOverLevels = false; // Paper - Throw exception on world create while being ticked
private final Set<String> pluginsBlockingSleep = new java.util.HashSet<>(); // Paper - API to allow/disallow tick sleeping
+ public volatile Thread shutdownThread; // Paper
+ public volatile boolean abnormalExit = false; // Paper
+
public static <S extends MinecraftServer> S spin(Function<Thread, S> serverFactory) {
ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system
AtomicReference<S> atomicreference = new AtomicReference();
@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
*/
// Paper end
+ io.papermc.paper.util.LogManagerShutdownThread.unhook(); // Paper
Runtime.getRuntime().addShutdownHook(new org.bukkit.craftbukkit.util.ServerShutdownThread(this));
// CraftBukkit end
this.paperConfigurations = services.paperConfigurations(); // Paper - add paper configuration files
@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
// CraftBukkit start
private boolean hasStopped = false;
private boolean hasLoggedStop = false; // Paper - Debugging
+ public volatile boolean hasFullyShutdown = false; // Paper
private final Object stopLock = new Object();
public final boolean hasStopped() {
synchronized (this.stopLock) {
@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.hasStopped = true;
}
if (!hasLoggedStop && isDebugging()) io.papermc.paper.util.TraceUtil.dumpTraceForThread("Server stopped"); // Paper - Debugging
+ // Paper start - kill main thread, and kill it hard
+ shutdownThread = Thread.currentThread();
+ org.spigotmc.WatchdogThread.doStop(); // Paper
+ // Paper end
// CraftBukkit end
if (this.metricsRecorder.isRecording()) {
this.cancelRecordingMetrics();
@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
ca.spottedleaf.moonrise.common.util.MoonriseCommon.haltExecutors();
}
// Paper end - rewrite chunk system
+ // Paper start - Improved watchdog support - move final shutdown items here
+ Util.shutdownExecutors();
+ try {
+ net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Use TerminalConsoleAppender
+ } catch (final Exception ignored) {
+ }
+ io.papermc.paper.log.CustomLogManager.forceReset(); // Paper - Reset loggers after shutdown
+ this.onServerExit();
+ // Paper end - Improved watchdog support - move final shutdown items here
}
public String getLocalIp() {
@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
protected void runServer() {
try {
+ long serverStartTime = Util.getNanos(); // Paper
if (!this.initServer()) {
throw new IllegalStateException("Failed to initialize server");
}
@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.server.spark.enableBeforePlugins(); // Paper - spark
// Spigot start
+ // Paper start - Improved Watchdog Support
+ LOGGER.info("Running delayed init tasks");
+ this.server.getScheduler().mainThreadHeartbeat(); // run all 1 tick delay tasks during init,
+ // this is going to be the first thing the tick process does anyways, so move done and run it after
+ // everything is init before watchdog tick.
+ // anything at 3+ won't be caught here but also will trip watchdog....
+ // tasks are default scheduled at -1 + delay, and first tick will tick at 1
+ final long actualDoneTimeMs = System.currentTimeMillis() - org.bukkit.craftbukkit.Main.BOOT_TIME.toEpochMilli(); // Paper - Add total time
+ LOGGER.info("Done ({})! For help, type \"help\"", String.format(java.util.Locale.ROOT, "%.3fs", actualDoneTimeMs / 1000.00D)); // Paper - Add total time
+ org.spigotmc.WatchdogThread.tick();
+ // Paper end - Improved Watchdog Support
org.spigotmc.WatchdogThread.hasStarted = true; // Paper
Arrays.fill( this.recentTps, 20 );
// Paper start - further improve server tick loop
@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
JvmProfiler.INSTANCE.onServerTick(this.smoothedTickTimeMillis);
}
} catch (Throwable throwable2) {
+ // Paper start
+ if (throwable2 instanceof ThreadDeath) {
+ MinecraftServer.LOGGER.error("Main thread terminated by WatchDog due to hard crash", throwable2);
+ return;
+ }
+ // Paper end
MinecraftServer.LOGGER.error("Encountered an unexpected exception", throwable2);
CrashReport crashreport = MinecraftServer.constructOrExtractCrashReport(throwable2);
@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.services.profileCache().clearExecutor();
}
- org.spigotmc.WatchdogThread.doStop(); // Spigot
+ //org.spigotmc.WatchdogThread.doStop(); // Spigot // Paper - move into stop
// CraftBukkit start - Restore terminal to original settings
try {
- net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Use TerminalConsoleAppender
+ //net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Move into stop
} catch (Exception ignored) {
}
// CraftBukkit end
- io.papermc.paper.log.CustomLogManager.forceReset(); // Paper - Reset loggers after shutdown
- this.onServerExit();
+ //io.papermc.paper.log.CustomLogManager.forceReset(); // Paper - Reset loggers after shutdown
+ //this.onServerExit(); // Paper - moved into stop
}
}
@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@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)) {
+ runnable.run();
+ runnable = () -> {};
+ }
+ // Paper end
return new TickTask(this.tickCount, runnable);
}
@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.resources.managers.updateStaticRegistryTags();
this.resources.managers.getRecipeManager().finalizeRecipeLoading(this.worldData.enabledFeatures());
this.potionBrewing = this.potionBrewing.reload(this.worldData.enabledFeatures()); // Paper - Custom Potion Mixes
- this.getPlayerList().saveAll();
+ // Paper start
+ if (Thread.currentThread() != this.serverThread) {
+ return;
+ }
+ // this.getPlayerList().saveAll(); // Paper - we don't need to save everything, just advancements // TODO Move this to a different patch
+ for (ServerPlayer player : this.getPlayerList().getPlayers()) {
+ player.getAdvancements().save();
+ }
+ // Paper end
this.getPlayerList().reloadResources();
this.functionManager.replaceLibrary(this.resources.managers.getFunctionLibrary());
this.structureTemplateManager.onResourceManagerReload(this.resources.resourceManager);
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
@@ -0,0 +0,0 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
long j = Util.getNanos() - i;
String s = String.format(Locale.ROOT, "%.3fs", (double) j / 1.0E9D);
- DedicatedServer.LOGGER.info("Done ({})! For help, type \"help\"", s);
+ DedicatedServer.LOGGER.info("Done preparing level \"{}\" ({})", this.getLevelIdName(), s); // Paper - clarify startup log messages & add total time
if (dedicatedserverproperties.announcePlayerAchievements != null) {
((GameRules.BooleanValue) this.getGameRules().getRule(GameRules.RULE_ANNOUNCE_ADVANCEMENTS)).set(dedicatedserverproperties.announcePlayerAchievements, this.overworld()); // CraftBukkit - per-world
}
@@ -0,0 +0,0 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
// this.remoteStatusListener.stop(); // Paper - don't wait for remote connections
}
- System.exit(0); // CraftBukkit
+ hasFullyShutdown = true; // Paper
+ System.exit(this.abnormalExit ? 70 : 0); // CraftBukkit // Paper
}
@Override
@@ -0,0 +0,0 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
@Override
public void stopServer() {
super.stopServer();
- Util.shutdownExecutors();
+ //Util.shutdownExecutors(); // Paper - moved into super
SkullBlockEntity.clear();
}
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -0,0 +0,0 @@ public abstract class PlayerList {
this.cserver.getPluginManager().callEvent(playerQuitEvent);
entityplayer.getBukkitEntity().disconnect(playerQuitEvent.getQuitMessage());
- entityplayer.doTick(); // SPIGOT-924
+ if (server.isSameThread()) entityplayer.doTick(); // SPIGOT-924 // Paper - don't tick during emergency shutdowns (Watchdog)
// CraftBukkit end
// Paper start - Configurable player collision; Remove from collideRule team if needed
diff --git a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java
+++ b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java
@@ -0,0 +0,0 @@ public abstract class BlockableEventLoop<R extends Runnable> implements Profiler
public static boolean isNonRecoverable(Throwable exception) {
return exception instanceof ReportedException reportedException
? isNonRecoverable(reportedException.getCause())
- : exception instanceof OutOfMemoryError || exception instanceof StackOverflowError;
+ : exception instanceof OutOfMemoryError || exception instanceof StackOverflowError || exception instanceof ThreadDeath; // Paper
}
}
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -0,0 +0,0 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
try {
tickConsumer.accept(entity);
} catch (Throwable throwable) {
+ if (throwable instanceof ThreadDeath) throw throwable; // Paper
// 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, throwable);
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
@@ -0,0 +0,0 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
gameprofilerfiller.pop();
} catch (Throwable throwable) {
+ if (throwable instanceof ThreadDeath) throw throwable; // Paper
// 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, throwable);
diff --git a/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java b/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java
@@ -0,0 +0,0 @@ public class ServerShutdownThread extends Thread {
@Override
public void run() {
try {
+ // Paper start - try to shutdown on main
+ server.safeShutdown(false, false);
+ for (int i = 1000; i > 0 && !server.hasStopped(); i -= 100) {
+ Thread.sleep(100);
+ }
+ if (server.hasStopped()) {
+ while (!server.hasFullyShutdown) Thread.sleep(1000);
+ return;
+ }
+ // Looks stalled, close async
org.spigotmc.AsyncCatcher.enabled = false; // Spigot
+ server.forceTicks = true;
this.server.close();
+ while (!server.hasFullyShutdown) Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ // Paper end
} finally {
+ org.apache.logging.log4j.LogManager.shutdown(); // Paper
try {
- net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Use TerminalConsoleAppender
+ //net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Move into stop
} catch (Exception e) {
}
}
diff --git a/src/main/java/org/spigotmc/RestartCommand.java b/src/main/java/org/spigotmc/RestartCommand.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/spigotmc/RestartCommand.java
+++ b/src/main/java/org/spigotmc/RestartCommand.java
@@ -0,0 +0,0 @@ public class RestartCommand extends Command
// Paper end
// Paper start - copied from above and modified to return if the hook registered
- private static boolean addShutdownHook(String restartScript)
+ public static boolean addShutdownHook(String restartScript) // Paper
{
String[] split = restartScript.split( " " );
if ( split.length > 0 && new File( split[0] ).isFile() )
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/spigotmc/WatchdogThread.java
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
@@ -0,0 +0,0 @@ import org.bukkit.Bukkit;
public class WatchdogThread extends ca.spottedleaf.moonrise.common.util.TickThread // Paper - rewrite chunk system
{
+ public static final boolean DISABLE_WATCHDOG = Boolean.getBoolean("disable.watchdog"); // Paper - Improved watchdog support
private static WatchdogThread instance;
private long timeoutTime;
private boolean restart;
@@ -0,0 +0,0 @@ public class WatchdogThread extends ca.spottedleaf.moonrise.common.util.TickThre
{
if ( WatchdogThread.instance == null )
{
+ if (timeoutTime <= 0) timeoutTime = 300; // Paper
WatchdogThread.instance = new WatchdogThread( timeoutTime * 1000L, restart );
WatchdogThread.instance.start();
} else
@@ -0,0 +0,0 @@ public class WatchdogThread extends ca.spottedleaf.moonrise.common.util.TickThre
// Paper start
Logger log = Bukkit.getServer().getLogger();
long currentTime = WatchdogThread.monotonicMillis();
- if ( this.lastTick != 0 && this.timeoutTime > 0 && currentTime > this.lastTick + this.earlyWarningEvery && !Boolean.getBoolean("disable.watchdog")) // Paper - Add property to disable
+ MinecraftServer server = MinecraftServer.getServer();
+ if ( this.lastTick != 0 && this.timeoutTime > 0 && WatchdogThread.hasStarted && (!server.isRunning() || (currentTime > this.lastTick + this.earlyWarningEvery && !DISABLE_WATCHDOG) )) // Paper - add property to disable
{
- boolean isLongTimeout = currentTime > lastTick + timeoutTime;
+ boolean isLongTimeout = currentTime > lastTick + timeoutTime || (!server.isRunning() && !server.hasStopped() && currentTime > lastTick + 1000);
// Don't spam early warning dumps
if ( !isLongTimeout && (earlyWarningEvery <= 0 || !hasStarted || currentTime < lastEarlyWarning + earlyWarningEvery || currentTime < lastTick + earlyWarningDelay)) continue;
- if ( !isLongTimeout && MinecraftServer.getServer().hasStopped()) continue; // Don't spam early watchdog warnings during shutdown, we'll come back to this...
+ if ( !isLongTimeout && server.hasStopped()) continue; // Don't spam early watchdog warnings during shutdown, we'll come back to this...
lastEarlyWarning = currentTime;
if (isLongTimeout) {
// Paper end
@@ -0,0 +0,0 @@ public class WatchdogThread extends ca.spottedleaf.moonrise.common.util.TickThre
if ( isLongTimeout )
{
- if ( this.restart && !MinecraftServer.getServer().hasStopped() )
+ if ( !server.hasStopped() )
{
- RestartCommand.restart();
+ AsyncCatcher.enabled = false; // Disable async catcher incase it interferes with us
+ server.forceTicks = true;
+ if (restart) {
+ RestartCommand.addShutdownHook( SpigotConfig.restartScript );
+ }
+ // try one last chance to safe shutdown on main incase it 'comes back'
+ server.abnormalExit = true;
+ server.safeShutdown(false, restart);
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ if (!server.hasStopped()) {
+ server.close();
+ }
}
break;
} // Paper end
diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/resources/log4j2.xml
+++ b/src/main/resources/log4j2.xml
@@ -0,0 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
-<Configuration status="WARN">
+<Configuration status="WARN" shutdownHook="disable">
<Appenders>
<Queue name="ServerGuiConsole">
<PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg{nolookups}%n" />

View file

@ -1,295 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Thu, 26 Mar 2020 21:59:32 -0700
Subject: [PATCH] Detail more information in watchdog dumps
- Dump position, world, velocity, and uuid for currently ticking entities
- Dump player name, player uuid, position, and world for packet handling
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
if (!(this.packetListener instanceof net.minecraft.server.network.ServerLoginPacketListenerImpl loginPacketListener)
|| loginPacketListener.state != net.minecraft.server.network.ServerLoginPacketListenerImpl.State.VERIFYING
|| Connection.joinAttemptsThisTick++ < MAX_PER_TICK) {
+ // Paper start - detailed watchdog information
+ net.minecraft.network.protocol.PacketUtils.packetProcessing.push(this.packetListener);
+ try {
tickablepacketlistener.tick();
+ } finally {
+ net.minecraft.network.protocol.PacketUtils.packetProcessing.pop();
+ } // Paper end - detailed watchdog information
} // Paper end - Buffer joins to world
}
diff --git a/src/main/java/net/minecraft/network/protocol/PacketUtils.java b/src/main/java/net/minecraft/network/protocol/PacketUtils.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/network/protocol/PacketUtils.java
+++ b/src/main/java/net/minecraft/network/protocol/PacketUtils.java
@@ -0,0 +0,0 @@ public class PacketUtils {
private static final Logger LOGGER = LogUtils.getLogger();
+ // Paper start - detailed watchdog information
+ public static final java.util.concurrent.ConcurrentLinkedDeque<PacketListener> packetProcessing = new java.util.concurrent.ConcurrentLinkedDeque<>();
+ static final java.util.concurrent.atomic.AtomicLong totalMainThreadPacketsProcessed = new java.util.concurrent.atomic.AtomicLong();
+
+ public static long getTotalProcessedPackets() {
+ return totalMainThreadPacketsProcessed.get();
+ }
+
+ public static java.util.List<PacketListener> getCurrentPacketProcessors() {
+ java.util.List<PacketListener> ret = new java.util.ArrayList<>(4);
+ for (PacketListener listener : packetProcessing) {
+ ret.add(listener);
+ }
+
+ return ret;
+ }
+ // Paper end - detailed watchdog information
+
public PacketUtils() {}
public static <T extends PacketListener> void ensureRunningOnSameThread(Packet<T> packet, T listener, ServerLevel world) throws RunningOnDifferentThreadException {
@@ -0,0 +0,0 @@ public class PacketUtils {
public static <T extends PacketListener> void ensureRunningOnSameThread(Packet<T> packet, T listener, BlockableEventLoop<?> engine) throws RunningOnDifferentThreadException {
if (!engine.isSameThread()) {
engine.executeIfPossible(() -> {
+ packetProcessing.push(listener); // Paper - detailed watchdog information
+ try { // Paper - detailed watchdog information
if (listener instanceof ServerCommonPacketListenerImpl serverCommonPacketListener && serverCommonPacketListener.processedDisconnect) return; // CraftBukkit - Don't handle sync packets for kicked players
if (listener.shouldHandleMessage(packet)) {
try {
@@ -0,0 +0,0 @@ public class PacketUtils {
} else {
PacketUtils.LOGGER.debug("Ignoring packet due to disconnection: {}", packet);
}
+ // Paper start - detailed watchdog information
+ } finally {
+ totalMainThreadPacketsProcessed.getAndIncrement();
+ packetProcessing.pop();
+ }
+ // Paper end - detailed watchdog information
});
throw RunningOnDifferentThreadException.RUNNING_ON_DIFFERENT_THREAD;
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -0,0 +0,0 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
}
+ // Paper start - log detailed entity tick information
+ // TODO replace with varhandle
+ static final java.util.concurrent.atomic.AtomicReference<Entity> currentlyTickingEntity = new java.util.concurrent.atomic.AtomicReference<>();
+
+ public static List<Entity> getCurrentlyTickingEntities() {
+ Entity ticking = currentlyTickingEntity.get();
+ List<Entity> ret = java.util.Arrays.asList(ticking == null ? new Entity[0] : new Entity[] { ticking });
+
+ return ret;
+ }
+ // Paper end - log detailed entity tick information
+
public void tickNonPassenger(Entity entity) {
+ // Paper start - log detailed entity tick information
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread("Cannot tick an entity off-main");
+ try {
+ if (currentlyTickingEntity.get() == null) {
+ currentlyTickingEntity.lazySet(entity);
+ }
+ // Paper end - log detailed entity tick information
// Spigot start
/*if (!org.spigotmc.ActivationRange.checkIfActive(entity)) { // Paper - comment out EAR 2
entity.tickCount++;
@@ -0,0 +0,0 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
this.tickPassenger(entity, entity1, isActive); // Paper - EAR 2
}
+ // Paper start - log detailed entity tick information
+ } finally {
+ if (currentlyTickingEntity.get() == entity) {
+ currentlyTickingEntity.lazySet(null);
+ }
+ }
+ // Paper end - log detailed entity tick information
}
private void tickPassenger(Entity vehicle, Entity passenger, boolean isActive) { // Paper - EAR 2
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -0,0 +0,0 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
return this.onGround;
}
+ // Paper start - detailed watchdog information
+ public final Object posLock = new Object(); // Paper - log detailed entity tick information
+
+ private Vec3 moveVector;
+ private double moveStartX;
+ private double moveStartY;
+ private double moveStartZ;
+
+ public final Vec3 getMoveVector() {
+ return this.moveVector;
+ }
+
+ public final double getMoveStartX() {
+ return this.moveStartX;
+ }
+
+ public final double getMoveStartY() {
+ return this.moveStartY;
+ }
+
+ public final double getMoveStartZ() {
+ return this.moveStartZ;
+ }
+ // Paper end - detailed watchdog information
+
public void move(MoverType type, Vec3 movement) {
final Vec3 originalMovement = movement; // 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;
+ }
+ try {
+ // Paper end - detailed watchdog information
if (this.noPhysics) {
this.setPos(this.getX() + movement.x, this.getY() + movement.y, this.getZ() + movement.z);
} else {
@@ -0,0 +0,0 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
gameprofilerfiller.pop();
}
}
+ // Paper start - detailed watchdog information
+ } finally {
+ synchronized (this.posLock) { // Paper
+ this.moveVector = null;
+ } // Paper
+ }
+ // Paper end - detailed watchdog information
}
private void applyMovementEmissionAndPlaySound(Entity.MovementEmission moveEffect, Vec3 movement, BlockPos landingPos, BlockState landingState) {
@@ -0,0 +0,0 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
}
public void setDeltaMovement(Vec3 velocity) {
+ synchronized (this.posLock) { // Paper
this.deltaMovement = velocity;
+ } // Paper
}
public void addDeltaMovement(Vec3 velocity) {
@@ -0,0 +0,0 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
}
// Paper end - Fix MC-4
if (this.position.x != x || this.position.y != y || this.position.z != z) {
+ synchronized (this.posLock) { // Paper
this.position = new Vec3(x, y, z);
+ } // Paper
int i = Mth.floor(x);
int j = Mth.floor(y);
int k = Mth.floor(z);
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/spigotmc/WatchdogThread.java
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
@@ -0,0 +0,0 @@ public class WatchdogThread extends ca.spottedleaf.moonrise.common.util.TickThre
private volatile long lastTick;
private volatile boolean stopping;
+ // Paper start - log detailed tick information
+ private void dumpEntity(net.minecraft.world.entity.Entity entity) {
+ Logger log = Bukkit.getServer().getLogger();
+ double posX, posY, posZ;
+ net.minecraft.world.phys.Vec3 mot;
+ double moveStartX, moveStartY, moveStartZ;
+ net.minecraft.world.phys.Vec3 moveVec;
+ synchronized (entity.posLock) {
+ posX = entity.getX();
+ posY = entity.getY();
+ posZ = entity.getZ();
+ mot = entity.getDeltaMovement();
+ moveStartX = entity.getMoveStartX();
+ moveStartY = entity.getMoveStartY();
+ moveStartZ = entity.getMoveStartZ();
+ moveVec = entity.getMoveVector();
+ }
+
+ String entityType = net.minecraft.world.entity.EntityType.getKey(entity.getType()).toString();
+ java.util.UUID entityUUID = entity.getUUID();
+ net.minecraft.world.level.Level world = entity.level();
+
+ log.log(Level.SEVERE, "Ticking entity: " + entityType + ", entity class: " + entity.getClass().getName());
+ log.log(Level.SEVERE, "Entity status: removed: " + entity.isRemoved() + ", valid: " + entity.valid + ", alive: " + entity.isAlive() + ", is passenger: " + entity.isPassenger());
+ log.log(Level.SEVERE, "Entity UUID: " + entityUUID);
+ log.log(Level.SEVERE, "Position: world: '" + (world == null ? "unknown world?" : world.getWorld().getName()) + "' at location (" + posX + ", " + posY + ", " + posZ + ")");
+ log.log(Level.SEVERE, "Velocity: " + (mot == null ? "unknown velocity" : mot.toString()) + " (in blocks per tick)");
+ log.log(Level.SEVERE, "Entity AABB: " + entity.getBoundingBox());
+ if (moveVec != null) {
+ log.log(Level.SEVERE, "Move call information: ");
+ log.log(Level.SEVERE, "Start position: (" + moveStartX + ", " + moveStartY + ", " + moveStartZ + ")");
+ log.log(Level.SEVERE, "Move vector: " + moveVec.toString());
+ }
+ }
+
+ private void dumpTickingInfo() {
+ Logger log = Bukkit.getServer().getLogger();
+
+ // ticking entities
+ for (net.minecraft.world.entity.Entity entity : net.minecraft.server.level.ServerLevel.getCurrentlyTickingEntities()) {
+ this.dumpEntity(entity);
+ net.minecraft.world.entity.Entity vehicle = entity.getVehicle();
+ if (vehicle != null) {
+ log.log(Level.SEVERE, "Detailing vehicle for above entity:");
+ this.dumpEntity(vehicle);
+ }
+ }
+
+ // packet processors
+ for (net.minecraft.network.PacketListener packetListener : net.minecraft.network.protocol.PacketUtils.getCurrentPacketProcessors()) {
+ if (packetListener instanceof net.minecraft.server.network.ServerGamePacketListenerImpl) {
+ net.minecraft.server.level.ServerPlayer player = ((net.minecraft.server.network.ServerGamePacketListenerImpl)packetListener).player;
+ long totalPackets = net.minecraft.network.protocol.PacketUtils.getTotalProcessedPackets();
+ if (player == null) {
+ log.log(Level.SEVERE, "Handling packet for player connection or ticking player connection (null player): " + packetListener);
+ log.log(Level.SEVERE, "Total packets processed on the main thread for all players: " + totalPackets);
+ } else {
+ this.dumpEntity(player);
+ net.minecraft.world.entity.Entity vehicle = player.getVehicle();
+ if (vehicle != null) {
+ log.log(Level.SEVERE, "Detailing vehicle for above entity:");
+ this.dumpEntity(vehicle);
+ }
+ log.log(Level.SEVERE, "Total packets processed on the main thread for all players: " + totalPackets);
+ }
+ } else {
+ log.log(Level.SEVERE, "Handling packet for connection: " + packetListener);
+ }
+ }
+ }
+ // Paper end - log detailed tick information
+
private WatchdogThread(long timeoutTime, boolean restart)
{
super( "Paper Watchdog Thread" );
@@ -0,0 +0,0 @@ public class WatchdogThread extends ca.spottedleaf.moonrise.common.util.TickThre
log.log( Level.SEVERE, "------------------------------" );
log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):" ); // Paper
ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler.dumpAllChunkLoadInfo(MinecraftServer.getServer(), isLongTimeout); // Paper - rewrite chunk system
+ this.dumpTickingInfo(); // Paper - log detailed tick information
WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log );
log.log( Level.SEVERE, "------------------------------" );
//

View file

@ -1,92 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Sun, 13 Aug 2023 15:41:52 -0700
Subject: [PATCH] Improve performance of mass crafts
When the server crafts all available items in CraftingMenu or InventoryMenu the game
checks either 4 or 9 times for each individual craft for a matching recipe for that container.
This check can be expensive if 64 total crafts are being performed with the recipe matching logic
being run 64 * 9 + 64 times. A breakdown of those times is below. This patch caches the last matching
recipe so that it is checked first and only if it doesn't match does the rest of the matching logic run.
Shift-click crafts are processed one at a time, so shift clicking on an item in the result of a iron block craft
where all the 9 inputs are full stacks of iron will run 64 iron block crafts. For each of those crafts, the
'remaining' blocks are calculated. This is due to recipes that have leftover items like buckets. This is done
for each craft, and done once to get the full 9 leftover items which are usually air. Then 1 item is removed
from each of the 9 inputs and each time that happens, logic is triggered to update the result itemstack. So
for each craft, that logic is run 9 times (hence the 64 * 9). The + 64 is from the 64 checks for remaining items.
After this patch, the full iteration over all recipes checking for a match should run once for a full craft to find the
initial recipe match. Then that recipe will be checked first for all future recipe match checks.
diff --git a/src/main/java/net/minecraft/world/inventory/CraftingContainer.java b/src/main/java/net/minecraft/world/inventory/CraftingContainer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/inventory/CraftingContainer.java
+++ b/src/main/java/net/minecraft/world/inventory/CraftingContainer.java
@@ -0,0 +0,0 @@ public interface CraftingContainer extends Container, StackedContentsCompatible
List<ItemStack> getItems();
// CraftBukkit start
- default RecipeHolder<?> getCurrentRecipe() {
+ default RecipeHolder<net.minecraft.world.item.crafting.CraftingRecipe> getCurrentRecipe() { // Paper - use correct generic
return null;
}
- default void setCurrentRecipe(RecipeHolder<?> recipe) {
+ default void setCurrentRecipe(RecipeHolder<net.minecraft.world.item.crafting.CraftingRecipe> recipe) { // Paper - use correct generic
}
// CraftBukkit end
diff --git a/src/main/java/net/minecraft/world/inventory/CraftingMenu.java b/src/main/java/net/minecraft/world/inventory/CraftingMenu.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/inventory/CraftingMenu.java
+++ b/src/main/java/net/minecraft/world/inventory/CraftingMenu.java
@@ -0,0 +0,0 @@ public class CraftingMenu extends AbstractCraftingMenu {
CraftingInput craftinginput = craftingInventory.asCraftInput();
ServerPlayer entityplayer = (ServerPlayer) player;
ItemStack itemstack = ItemStack.EMPTY;
+ if (recipe == null) recipe = craftingInventory.getCurrentRecipe(); // Paper - Perf: Improve mass crafting; check last recipe used first
Optional<RecipeHolder<CraftingRecipe>> optional = world.getServer().getRecipeManager().getRecipeFor(RecipeType.CRAFTING, craftinginput, world, recipe);
craftingInventory.setCurrentRecipe(optional.orElse(null)); // CraftBukkit
diff --git a/src/main/java/net/minecraft/world/inventory/ResultSlot.java b/src/main/java/net/minecraft/world/inventory/ResultSlot.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/inventory/ResultSlot.java
+++ b/src/main/java/net/minecraft/world/inventory/ResultSlot.java
@@ -0,0 +0,0 @@ public class ResultSlot extends Slot {
private NonNullList<ItemStack> getRemainingItems(CraftingInput input, Level world) {
return world instanceof ServerLevel serverLevel
? serverLevel.recipeAccess()
- .getRecipeFor(RecipeType.CRAFTING, input, serverLevel)
+ .getRecipeFor(RecipeType.CRAFTING, input, serverLevel, this.craftSlots.getCurrentRecipe()) // Paper - Perf: Improve mass crafting; check last recipe used first
.map(recipe -> recipe.value().getRemainingItems(input))
.orElseGet(() -> copyAllInputItems(input))
: CraftingRecipe.defaultCraftingReminder(input);
diff --git a/src/main/java/net/minecraft/world/inventory/TransientCraftingContainer.java b/src/main/java/net/minecraft/world/inventory/TransientCraftingContainer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/inventory/TransientCraftingContainer.java
+++ b/src/main/java/net/minecraft/world/inventory/TransientCraftingContainer.java
@@ -0,0 +0,0 @@ public class TransientCraftingContainer implements CraftingContainer {
// CraftBukkit start - add fields
public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>();
- private RecipeHolder<?> currentRecipe;
+ private RecipeHolder<net.minecraft.world.item.crafting.CraftingRecipe> currentRecipe; // Paper - use correct generic
public Container resultInventory;
private Player owner;
private int maxStack = MAX_STACK;
@@ -0,0 +0,0 @@ public class TransientCraftingContainer implements CraftingContainer {
}
@Override
- public RecipeHolder<?> getCurrentRecipe() {
+ public RecipeHolder<net.minecraft.world.item.crafting.CraftingRecipe> getCurrentRecipe() { // Paper - use correct generic
return this.currentRecipe;
}
@Override
- public void setCurrentRecipe(RecipeHolder<?> currentRecipe) {
+ public void setCurrentRecipe(RecipeHolder<net.minecraft.world.item.crafting.CraftingRecipe> currentRecipe) { // Paper - use correct generic
this.currentRecipe = currentRecipe;
}

View file

@ -1,129 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Sat, 23 Sep 2023 22:05:35 -0700
Subject: [PATCH] Lag compensation ticks
Areas affected by lag comepnsation:
- Block breaking and destroying
- Eating food items
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
public volatile Thread shutdownThread; // Paper
public volatile boolean abnormalExit = false; // Paper
+ public static final long SERVER_INIT = System.nanoTime(); // Paper - Lag compensation
public static <S extends MinecraftServer> S spin(Function<Thread, S> serverFactory) {
ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system
@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
worldserver.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - BlockPhysicsEvent
worldserver.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent
net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = worldserver.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - Perf: Optimize Hoppers
+ worldserver.updateLagCompensationTick(); // Paper - lag compensation
gameprofilerfiller.push(() -> {
String s = String.valueOf(worldserver);
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -0,0 +0,0 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
);
}
// Paper end - chunk tick iteration
+ // Paper start - lag compensation
+ private long lagCompensationTick = net.minecraft.server.MinecraftServer.SERVER_INIT;
+
+ public long getLagCompensationTick() {
+ return this.lagCompensationTick;
+ }
+
+ public void updateLagCompensationTick() {
+ this.lagCompensationTick = (System.nanoTime() - net.minecraft.server.MinecraftServer.SERVER_INIT) / (java.util.concurrent.TimeUnit.MILLISECONDS.toNanos(50L));
+ }
+ // Paper end - lag compensation
// Add env and gen to constructor, IWorldDataServer -> WorldDataServer
public ServerLevel(MinecraftServer minecraftserver, Executor executor, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PrimaryLevelData iworlddataserver, ResourceKey<Level> resourcekey, LevelStem worlddimension, ChunkProgressListener worldloadlistener, boolean flag, long i, List<CustomSpawner> list, boolean flag1, @Nullable RandomSequences randomsequences, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider) {
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
@@ -0,0 +0,0 @@ public class ServerPlayerGameMode {
}
public void tick() {
- this.gameTicks = MinecraftServer.currentTick; // CraftBukkit;
+ this.gameTicks = (int)this.level.getLagCompensationTick(); // CraftBukkit; // Paper - lag compensation
BlockState iblockdata;
if (this.hasDelayedDestroy) {
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -0,0 +0,0 @@ public abstract class LivingEntity extends Entity implements Attackable {
this.resendPossiblyDesyncedDataValues(java.util.List.of(DATA_LIVING_ENTITY_FLAGS), serverPlayer);
}
// Paper end - Properly cancel usable items
+ // Paper start - lag compensate eating
+ protected long eatStartTime;
+ protected int totalEatTimeTicks;
+ // Paper end - lag compensate eating
private void updatingUsingItem() {
if (this.isUsingItem()) {
if (ItemStack.isSameItem(this.getItemInHand(this.getUsedItemHand()), this.useItem)) {
@@ -0,0 +0,0 @@ public abstract class LivingEntity extends Entity implements Attackable {
protected void updateUsingItem(ItemStack stack) {
stack.onUseTick(this.level(), this, this.getUseItemRemainingTicks());
- if (--this.useItemRemaining == 0 && !this.level().isClientSide && !stack.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 && !stack.useOnRelease()) {
+ this.useItemRemaining = 0;
+ // Paper end - lag compensate eating
this.completeUsingItem();
}
@@ -0,0 +0,0 @@ public abstract class LivingEntity extends Entity implements Attackable {
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 = itemstack.getUseDuration(this);
+ this.eatStartTime = System.nanoTime();
+ // Paper end - lag compensate eating
if (!this.level().isClientSide) {
this.setLivingEntityFlag(1, true);
this.setLivingEntityFlag(2, hand == InteractionHand.OFF_HAND);
@@ -0,0 +0,0 @@ public abstract class LivingEntity extends Entity implements Attackable {
}
} else if (!this.isUsingItem() && !this.useItem.isEmpty()) {
this.useItem = ItemStack.EMPTY;
- this.useItemRemaining = 0;
+ // Paper start - lag compensate eating
+ this.useItemRemaining = this.totalEatTimeTicks = 0;
+ this.eatStartTime = -1L;
+ // Paper end - lag compensate eating
}
}
@@ -0,0 +0,0 @@ public abstract class LivingEntity extends Entity implements Attackable {
}
this.useItem = ItemStack.EMPTY;
- this.useItemRemaining = 0;
+ // Paper start - lag compensate eating
+ this.useItemRemaining = this.totalEatTimeTicks = 0;
+ this.eatStartTime = -1L;
+ // Paper end - lag compensate eating
}
public boolean isBlocking() {

View file

@ -1,232 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: kickash32 <kickash32@gmail.com>
Date: Mon, 19 Aug 2019 01:27:58 +0500
Subject: [PATCH] Optional per player mob spawns
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
// Paper start
+ // Paper start - Optional per player mob spawns
+ public void updatePlayerMobTypeMap(final Entity entity) {
+ if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) {
+ return;
+ }
+ final int index = entity.getType().getCategory().ordinal();
+
+ final ca.spottedleaf.moonrise.common.list.ReferenceList<ServerPlayer> inRange =
+ this.level.moonrise$getNearbyPlayers().getPlayers(entity.chunkPosition(), ca.spottedleaf.moonrise.common.misc.NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE);
+ if (inRange == null) {
+ return;
+ }
+ final ServerPlayer[] backingSet = inRange.getRawDataUnchecked();
+ for (int i = 0, len = inRange.size(); i < len; i++) {
+ ++(backingSet[i].mobCounts[index]);
+ }
+ }
public int getMobCountNear(final ServerPlayer player, final net.minecraft.world.entity.MobCategory mobCategory) {
- return -1;
+ return player.mobCounts[mobCategory.ordinal()];
+ // Paper end - Optional per player mob spawns
}
// Paper end
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
gameprofilerfiller.popPush("shuffleChunks");
// Paper start - chunk tick iteration optimisation
this.shuffleRandom.setSeed(this.level.random.nextLong());
- Util.shuffle(list, this.shuffleRandom);
+ if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) Util.shuffle(list, this.shuffleRandom); // Paper - Optional per player mob spawns; do not need this when per-player is enabled
// Paper end - chunk tick iteration optimisation
this.tickChunks(gameprofilerfiller, j, list);
gameprofilerfiller.pop();
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
private void tickChunks(ProfilerFiller profiler, long timeDelta, List<LevelChunk> chunks) {
profiler.popPush("naturalSpawnCount");
int j = this.distanceManager.getNaturalSpawnChunkCount();
- NaturalSpawner.SpawnState spawnercreature_d = NaturalSpawner.createState(j, this.level.getAllEntities(), this::getFullChunk, new LocalMobCapCalculator(this.chunkMap));
+ // Paper start - Optional per player mob spawns
+ final int naturalSpawnChunkCount = j;
+ NaturalSpawner.SpawnState spawnercreature_d; // moved down
+ if ((this.spawnFriendlies || this.spawnEnemies) && this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { // don't count mobs when animals and monsters are disabled
+ // re-set mob counts
+ for (ServerPlayer player : this.level.players) {
+ Arrays.fill(player.mobCounts, 0);
+ }
+ spawnercreature_d = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, null, true);
+ } else {
+ spawnercreature_d = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, !this.level.paperConfig().entities.spawning.perPlayerMobSpawns ? new LocalMobCapCalculator(this.chunkMap) : null, false);
+ }
+ // Paper end - Optional per player mob spawns
this.lastSpawnState = spawnercreature_d;
profiler.popPush("spawnAndTick");
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -0,0 +0,0 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple
public boolean queueHealthUpdatePacket;
public net.minecraft.network.protocol.game.ClientboundSetHealthPacket queuedHealthUpdatePacket;
// Paper end - cancellable death event
+ // Paper start - Optional per player mob spawns
+ public static final int MOBCATEGORY_TOTAL_ENUMS = net.minecraft.world.entity.MobCategory.values().length;
+ public final int[] mobCounts = new int[MOBCATEGORY_TOTAL_ENUMS]; // Paper
+ // Paper end - Optional per player mob spawns
// CraftBukkit start
public CraftPlayer.TransferCookieConnection transferCookieConnection;
diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
@@ -0,0 +0,0 @@ public final class NaturalSpawner {
private NaturalSpawner() {}
public static NaturalSpawner.SpawnState createState(int spawningChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkSource, LocalMobCapCalculator densityCapper) {
+ // Paper start - Optional per player mob spawns
+ return createState(spawningChunkCount, entities, chunkSource, densityCapper, false);
+ }
+
+ public static NaturalSpawner.SpawnState createState(int spawningChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkSource, LocalMobCapCalculator densityCapper, boolean countMobs) {
+ // Paper end - Optional per player mob spawns
PotentialCalculator spawnercreatureprobabilities = new PotentialCalculator();
Object2IntOpenHashMap<MobCategory> object2intopenhashmap = new Object2IntOpenHashMap();
Iterator iterator = entities.iterator();
@@ -0,0 +0,0 @@ public final class NaturalSpawner {
spawnercreatureprobabilities.addCharge(entity.blockPosition(), biomesettingsmobs_b.charge());
}
- if (entity instanceof Mob) {
+ if (densityCapper != null && entity instanceof Mob) { // Paper - Optional per player mob spawns
densityCapper.addMob(chunk.getPos(), enumcreaturetype);
}
object2intopenhashmap.addTo(enumcreaturetype, 1);
+ // Paper start - Optional per player mob spawns
+ if (countMobs) {
+ chunk.level.getChunkSource().chunkMap.updatePlayerMobTypeMap(entity);
+ }
+ // Paper end - Optional per player mob spawns
});
}
}
@@ -0,0 +0,0 @@ public final class NaturalSpawner {
continue;
}
- if ((flag || !enumcreaturetype.isFriendly()) && (flag1 || enumcreaturetype.isFriendly()) && (flag2 || !enumcreaturetype.isPersistent()) && spawnercreature_d.canSpawnForCategoryGlobal(enumcreaturetype, limit)) {
+ if ((flag || !enumcreaturetype.isFriendly()) && (flag1 || enumcreaturetype.isFriendly()) && (flag2 || !enumcreaturetype.isPersistent()) && (worldserver.paperConfig().entities.spawning.perPlayerMobSpawns || spawnercreature_d.canSpawnForCategoryGlobal(enumcreaturetype, limit))) { // Paper - Optional per player mob spawns; remove global check, check later during the local one
// CraftBukkit end
list.add(enumcreaturetype);
}
@@ -0,0 +0,0 @@ public final class NaturalSpawner {
while (iterator.hasNext()) {
MobCategory enumcreaturetype = (MobCategory) iterator.next();
- if (info.canSpawnForCategoryLocal(enumcreaturetype, chunk.getPos())) {
+ // Paper start - Optional per player mob spawns
+ final boolean canSpawn;
+ int maxSpawns = Integer.MAX_VALUE;
+ if (world.paperConfig().entities.spawning.perPlayerMobSpawns) {
+ // Copied from getFilteredSpawningCategories
+ int limit = enumcreaturetype.getMaxInstancesPerChunk();
+ SpawnCategory spawnCategory = CraftSpawnCategory.toBukkit(enumcreaturetype);
+ if (CraftSpawnCategory.isValidForLimits(spawnCategory)) {
+ limit = world.getWorld().getSpawnLimit(spawnCategory);
+ }
+
+ // Apply per-player limit
+ int minDiff = Integer.MAX_VALUE;
+ final ca.spottedleaf.moonrise.common.list.ReferenceList<net.minecraft.server.level.ServerPlayer> inRange =
+ world.moonrise$getNearbyPlayers().getPlayers(chunk.getPos(), ca.spottedleaf.moonrise.common.misc.NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE);
+ if (inRange != null) {
+ final net.minecraft.server.level.ServerPlayer[] backingSet = inRange.getRawDataUnchecked();
+ for (int k = 0, len = inRange.size(); k < len; k++) {
+ minDiff = Math.min(limit - world.getChunkSource().chunkMap.getMobCountNear(backingSet[k], enumcreaturetype), minDiff);
+ }
+ }
+
+ maxSpawns = (minDiff == Integer.MAX_VALUE) ? 0 : minDiff;
+ canSpawn = maxSpawns > 0;
+ } else {
+ canSpawn = info.canSpawnForCategoryLocal(enumcreaturetype, chunk.getPos());
+ }
+ if (canSpawn) {
+ // Paper end - Optional per player mob spawns
Objects.requireNonNull(info);
NaturalSpawner.SpawnPredicate spawnercreature_c = info::canSpawn;
Objects.requireNonNull(info);
- NaturalSpawner.spawnCategoryForChunk(enumcreaturetype, world, chunk, spawnercreature_c, info::afterSpawn);
+ // Paper start - Optional per player mob spawns
+ NaturalSpawner.spawnCategoryForChunk(enumcreaturetype, world, chunk, spawnercreature_c, info::afterSpawn,
+ maxSpawns, world.paperConfig().entities.spawning.perPlayerMobSpawns ? world.getChunkSource().chunkMap::updatePlayerMobTypeMap : null);
+ // Paper end - Optional per player mob spawns
}
}
@@ -0,0 +0,0 @@ public final class NaturalSpawner {
// Paper end - Add mobcaps commands
public static void spawnCategoryForChunk(MobCategory group, ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner) {
+ // Paper start - Optional per player mob spawns
+ spawnCategoryForChunk(group, world, chunk, checker, runner, Integer.MAX_VALUE, null);
+ }
+ public static void spawnCategoryForChunk(MobCategory group, ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner, int maxSpawns, Consumer<Entity> trackEntity) {
+ // Paper end - Optional per player mob spawns
BlockPos blockposition = NaturalSpawner.getRandomPosWithin(world, chunk);
if (blockposition.getY() >= world.getMinY() + 1) {
- NaturalSpawner.spawnCategoryForPosition(group, world, chunk, blockposition, checker, runner);
+ NaturalSpawner.spawnCategoryForPosition(group, world, chunk, blockposition, checker, runner, maxSpawns, trackEntity); // Paper - Optional per player mob spawns
}
}
@@ -0,0 +0,0 @@ public final class NaturalSpawner {
});
}
+ // Paper start - Optional per player mob spawns
public static void spawnCategoryForPosition(MobCategory group, ServerLevel world, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner) {
+ spawnCategoryForPosition(group, world,chunk, pos, checker, runner, Integer.MAX_VALUE, null);
+ }
+ public static void spawnCategoryForPosition(MobCategory group, ServerLevel world, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner, int maxSpawns, Consumer<Entity> trackEntity) {
+ // Paper end - Optional per player mob spawns
StructureManager structuremanager = world.structureManager();
ChunkGenerator chunkgenerator = world.getChunkSource().getGenerator();
int i = pos.getY();
@@ -0,0 +0,0 @@ public final class NaturalSpawner {
++j;
++k1;
runner.run(entityinsentient, chunk);
+ // Paper start - Optional per player mob spawns
+ if (trackEntity != null) {
+ trackEntity.accept(entityinsentient);
+ }
+ // Paper end - Optional per player mob spawns
}
// CraftBukkit end
- if (j >= entityinsentient.getMaxSpawnClusterSize()) {
+ if (j >= entityinsentient.getMaxSpawnClusterSize() || j >= maxSpawns) { // Paper - Optional per player mob spawns
return;
}
@@ -0,0 +0,0 @@ public final class NaturalSpawner {
MobCategory enumcreaturetype = entitytypes.getCategory();
this.mobCategoryCounts.addTo(enumcreaturetype, 1);
- this.localMobCapCalculator.addMob(new ChunkPos(blockposition), enumcreaturetype);
+ if (this.localMobCapCalculator != null) this.localMobCapCalculator.addMob(new ChunkPos(blockposition), enumcreaturetype); // Paper - Optional per player mob spawns
}
public int getSpawnableChunkCount() {

View file

@ -1,89 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: kickash32 <kickash32@gmail.com>
Date: Mon, 5 Apr 2021 01:42:35 -0400
Subject: [PATCH] Improve cancelling PreCreatureSpawnEvent with per player mob
spawns
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
++(backingSet[i].mobCounts[index]);
}
}
+ // Paper start - per player mob count backoff
+ public void updateFailurePlayerMobTypeMap(int chunkX, int chunkZ, net.minecraft.world.entity.MobCategory mobCategory) {
+ if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) {
+ return;
+ }
+ int idx = mobCategory.ordinal();
+ final ca.spottedleaf.moonrise.common.list.ReferenceList<ServerPlayer> inRange =
+ this.level.moonrise$getNearbyPlayers().getPlayersByChunk(chunkX, chunkZ, ca.spottedleaf.moonrise.common.misc.NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE);
+ if (inRange == null) {
+ return;
+ }
+ final ServerPlayer[] backingSet = inRange.getRawDataUnchecked();
+ for (int i = 0, len = inRange.size(); i < len; i++) {
+ ++(backingSet[i].mobBackoffCounts[idx]);
+ }
+ }
+ // Paper end - per player mob count backoff
public int getMobCountNear(final ServerPlayer player, final net.minecraft.world.entity.MobCategory mobCategory) {
- return player.mobCounts[mobCategory.ordinal()];
+ return player.mobCounts[mobCategory.ordinal()] + player.mobBackoffCounts[mobCategory.ordinal()]; // Paper - per player mob count backoff
// Paper end - Optional per player mob spawns
}
// Paper end
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
if ((this.spawnFriendlies || this.spawnEnemies) && this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { // don't count mobs when animals and monsters are disabled
// re-set mob counts
for (ServerPlayer player : this.level.players) {
- Arrays.fill(player.mobCounts, 0);
+ // Paper start - per player mob spawning backoff
+ for (int ii = 0; ii < ServerPlayer.MOBCATEGORY_TOTAL_ENUMS; ii++) {
+ player.mobCounts[ii] = 0;
+
+ int newBackoff = player.mobBackoffCounts[ii] - 1; // TODO make configurable bleed // TODO use nonlinear algorithm?
+ if (newBackoff < 0) {
+ newBackoff = 0;
+ }
+ player.mobBackoffCounts[ii] = newBackoff;
+ }
+ // Paper end - per player mob spawning backoff
}
spawnercreature_d = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, null, true);
} else {
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -0,0 +0,0 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple
public static final int MOBCATEGORY_TOTAL_ENUMS = net.minecraft.world.entity.MobCategory.values().length;
public final int[] mobCounts = new int[MOBCATEGORY_TOTAL_ENUMS]; // Paper
// Paper end - Optional per player mob spawns
+ public final int[] mobBackoffCounts = new int[MOBCATEGORY_TOTAL_ENUMS]; // Paper - per player mob count backoff
// CraftBukkit start
public CraftPlayer.TransferCookieConnection transferCookieConnection;
diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
@@ -0,0 +0,0 @@ public final class NaturalSpawner {
// Paper start - PreCreatureSpawnEvent
PreSpawnStatus doSpawning = isValidSpawnPostitionForType(world, group, structuremanager, chunkgenerator, biomesettingsmobs_c, blockposition_mutableblockposition, d2);
+ // Paper start - per player mob count backoff
+ if (doSpawning == PreSpawnStatus.ABORT || doSpawning == PreSpawnStatus.CANCELLED) {
+ world.getChunkSource().chunkMap.updateFailurePlayerMobTypeMap(blockposition_mutableblockposition.getX() >> 4, blockposition_mutableblockposition.getZ() >> 4, group);
+ }
+ // Paper end - per player mob count backoff
if (doSpawning == PreSpawnStatus.ABORT) {
return;
}

View file

@ -1,51 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Flo0 <flo.roma@web.de>
Date: Thu, 5 Dec 2024 12:15:07 +0100
Subject: [PATCH] Implement chunk view API
diff --git a/src/main/java/io/papermc/paper/FeatureHooks.java b/src/main/java/io/papermc/paper/FeatureHooks.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/io/papermc/paper/FeatureHooks.java
+++ b/src/main/java/io/papermc/paper/FeatureHooks.java
@@ -0,0 +0,0 @@ package io.papermc.paper;
import io.papermc.paper.command.PaperSubcommand;
import io.papermc.paper.command.subcommands.ChunkDebugCommand;
import io.papermc.paper.command.subcommands.FixLightCommand;
+import it.unimi.dsi.fastutil.longs.LongIterator;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
-import it.unimi.dsi.fastutil.longs.LongSet;
import it.unimi.dsi.fastutil.longs.LongSets;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import it.unimi.dsi.fastutil.objects.ObjectSet;
@@ -0,0 +0,0 @@ public final class FeatureHooks {
}
public static Set<Long> getSentChunkKeys(final ServerPlayer player) {
- final LongSet keys = new LongOpenHashSet();
- player.getChunkTrackingView().forEach(pos -> keys.add(pos.longKey));
- return LongSets.unmodifiable(keys);
+ return LongSets.unmodifiable(player.moonrise$getChunkLoader().getSentChunksRaw().clone());
}
public static Set<Chunk> getSentChunks(final ServerPlayer player) {
- final ObjectSet<Chunk> chunks = new ObjectOpenHashSet<>();
+ final LongOpenHashSet rawChunkKeys = player.moonrise$getChunkLoader().getSentChunksRaw();
+ final ObjectSet<org.bukkit.Chunk> chunks = new ObjectOpenHashSet<>(rawChunkKeys.size());
final World world = player.serverLevel().getWorld();
- player.getChunkTrackingView().forEach(pos -> {
- final org.bukkit.Chunk chunk = world.getChunkAt(pos.longKey);
- chunks.add(chunk);
- });
+ final LongIterator iter = player.moonrise$getChunkLoader().getSentChunksRaw().longIterator();
+ while (iter.hasNext()) {
+ chunks.add(world.getChunkAt(iter.nextLong(), false));
+ }
return ObjectSets.unmodifiable(chunks);
}
public static boolean isChunkSent(final ServerPlayer player, final long chunkKey) {
- return player.getChunkTrackingView().contains(new ChunkPos(chunkKey));
+ return player.moonrise$getChunkLoader().getSentChunksRaw().contains(chunkKey);
}
}

View file

@ -4,8 +4,7 @@ mcVersion=1.21.4
# Set to true while updating Minecraft version
updatingMinecraft=false
#cleanPaperRepo=~/IdeaProjects/Paper/Paper-Server/src/main/java
updateTaskListIssue=https://github.com/PaperMC/testing/issues/2
updateTaskListIssue=https://github.com/PaperMC/Paper/issues/11736
org.gradle.configuration-cache=true
org.gradle.caching=true

View file

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

3
gradlew vendored
View file

@ -86,8 +86,7 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum

View file

@ -39,7 +39,7 @@ abstract class MockitoAgentProvider : CommandLineArgumentProvider {
// Paper end - configure mockito agent that is needed in newer java versions
dependencies {
api("com.mojang:brigadier:1.2.9") // Paper - Brigadier command api
api("com.mojang:brigadier:1.3.10") // Paper - Brigadier command api
// api dependencies are listed transitively to API consumers
api("com.google.guava:guava:33.3.1-jre")
api("com.google.code.gson:gson:2.11.0")
@ -92,6 +92,7 @@ dependencies {
testImplementation("org.mockito:mockito-core:5.14.1")
testImplementation("org.ow2.asm:asm-tree:9.7.1")
mockitoAgent("org.mockito:mockito-core:5.14.1") { isTransitive = false } // Paper - configure mockito agent that is needed in newer java versions
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
// Paper start
@ -121,8 +122,13 @@ configurations {
}
outgoing {
capability(mainCapability)
// Paper-MojangAPI has been merged into Paper-API
capability("io.papermc.paper:paper-mojangapi:${project.version}")
capability("com.destroystokyo.paper:paper-mojangapi:${project.version}")
// Conflict with old coordinates
capability("com.destroystokyo.paper:paper-api:${project.version}")
capability("org.spigotmc:spigot-api:${project.version}")
capability("org.bukkit:bukkit:${project.version}")
}
}
}

View file

@ -55,7 +55,7 @@ public class BlockDestroyEvent extends BlockExpEvent implements Cancellable {
* @param effectBlock block effect
*/
public void setEffectBlock(final BlockData effectBlock) {
this.effectBlock = effectBlock;
this.effectBlock = effectBlock.clone();
}
/**

View file

@ -79,7 +79,7 @@ public class PlayerJumpEvent extends PlayerEvent implements Cancellable {
public void setFrom(final Location from) {
Preconditions.checkArgument(from != null, "Cannot use null from location!");
Preconditions.checkArgument(from.getWorld() != null, "Cannot use from location with null world!");
this.from = from;
this.from = from.clone();
}
/**

View file

@ -66,7 +66,7 @@ public class PlayerSetSpawnEvent extends PlayerEvent implements Cancellable {
* @param location the spawn location, or {@code null} to remove the spawn location
*/
public void setLocation(final @Nullable Location location) {
this.location = location;
this.location = location != null ? location.clone() : null;
}
/**

View file

@ -37,10 +37,31 @@ public interface CustomArgumentType<T, N> extends ArgumentType<T> {
* @param reader string reader input
* @return parsed value
* @throws CommandSyntaxException if an error occurs while parsing
* @see #parse(StringReader, Object)
*/
@Override
T parse(final StringReader reader) throws CommandSyntaxException;
/**
* Parses the argument into the custom type ({@code T}). Keep in mind
* that this parsing will be done on the server. This means that if
* you throw a {@link CommandSyntaxException} during parsing, this
* will only show up to the user after the user has executed the command
* not while they are still entering it.
* <p>
* This method provides the command source for additional context when parsing. You
* may have to do your own {@code instanceof} checks for {@link io.papermc.paper.command.brigadier.CommandSourceStack}.
*
* @param reader string reader input
* @param source source of the command
* @return parsed value
* @throws CommandSyntaxException if an error occurs while parsing
*/
@Override
default <S> T parse(final StringReader reader, final S source) throws CommandSyntaxException {
return ArgumentType.super.parse(reader, source);
}
/**
* Gets the native type that this argument uses,
* the type that is sent to the client.
@ -95,13 +116,35 @@ public interface CustomArgumentType<T, N> extends ArgumentType<T> {
return this.convert(this.getNativeType().parse(reader));
}
@ApiStatus.NonExtendable
@Override
default <S> T parse(final StringReader reader, final S source) throws CommandSyntaxException {
return this.convert(this.getNativeType().parse(reader, source), source);
}
/**
* Converts the value from the native type to the custom argument type.
*
* @param nativeType native argument provided value
* @return converted value
* @throws CommandSyntaxException if an exception occurs while parsing
* @see #convert(Object, Object)
*/
T convert(N nativeType) throws CommandSyntaxException;
/**
* Converts the value from the native type to the custom argument type.
* <p>
* This method provides the command source for additional context when converting. You
* may have to do your own {@code instanceof} checks for {@link io.papermc.paper.command.brigadier.CommandSourceStack}.
*
* @param nativeType native argument provided value
* @param source source of the command
* @return converted value
* @throws CommandSyntaxException if an exception occurs while parsing
*/
default <S> T convert(final N nativeType, final S source) throws CommandSyntaxException {
return this.convert(nativeType);
}
}
}

View file

@ -53,7 +53,7 @@ public class EntityMoveEvent extends EntityEvent implements Cancellable {
*/
public void setFrom(final Location from) {
this.validateLocation(from);
this.from = from;
this.from = from.clone();
}
/**
@ -72,7 +72,7 @@ public class EntityMoveEvent extends EntityEvent implements Cancellable {
*/
public void setTo(final Location to) {
this.validateLocation(to);
this.to = to;
this.to = to.clone();
}
/**

View file

@ -52,7 +52,7 @@ public class WorldBorderCenterChangeEvent extends WorldBorderEvent implements Ca
* @param newCenter the new center
*/
public void setNewCenter(final Location newCenter) {
this.newCenter = newCenter;
this.newCenter = newCenter.clone();
}
@Override

View file

@ -30,9 +30,9 @@ public final class PotionMix implements Keyed {
*/
public PotionMix(final NamespacedKey key, final ItemStack result, final RecipeChoice input, final RecipeChoice ingredient) {
this.key = key;
this.result = result;
this.input = input;
this.ingredient = ingredient;
this.result = result.clone();
this.input = input.clone();
this.ingredient = ingredient.clone();
}
/**
@ -58,7 +58,7 @@ public final class PotionMix implements Keyed {
* @return the result itemstack
*/
public ItemStack getResult() {
return this.result;
return this.result.clone();
}
/**
@ -67,7 +67,7 @@ public final class PotionMix implements Keyed {
* @return the bottom 3 slot ingredients
*/
public RecipeChoice getInput() {
return this.input;
return this.input.clone();
}
/**
@ -76,7 +76,7 @@ public final class PotionMix implements Keyed {
* @return the top slot input
*/
public RecipeChoice getIngredient() {
return this.ingredient;
return this.ingredient.clone();
}
@Override

View file

@ -79,7 +79,7 @@ public class Vibration {
private final Location block;
public BlockDestination(@NotNull Location block) {
this.block = block;
this.block = block.clone();
}
public BlockDestination(@NotNull Block block) {
@ -88,7 +88,7 @@ public class Vibration {
@NotNull
public Location getLocation() {
return block;
return block.clone();
}
@NotNull

View file

@ -191,7 +191,7 @@ public interface Mob extends LivingEntity, Lootable, io.papermc.paper.entity.Lea
* set by {@link #setAggressive(boolean)}. {@link Panda}'s are always
* aggressive if their combined {@link Panda.Gene} is {@link Panda.Gene#AGGRESSIVE}.
*
* @return wether the mob is aggressive or not
* @return whether the mob is aggressive or not
*/
boolean isAggressive();
@ -199,7 +199,7 @@ public interface Mob extends LivingEntity, Lootable, io.papermc.paper.entity.Lea
* Some mobs will raise their arm(s) when aggressive,
* see {@link #isAggressive()} for full list.
*
* @param aggressive wether the mob should be aggressive or not
* @param aggressive whether the mob should be aggressive or not
* @see #isAggressive()
*/
void setAggressive(boolean aggressive);

View file

@ -65,7 +65,7 @@ public class BlockDispenseEvent extends BlockEvent implements Cancellable {
* @param vel the velocity of the item being dispensed
*/
public void setVelocity(@NotNull Vector vel) {
velocity = vel;
velocity = vel.clone();
}
@Override

View file

@ -43,7 +43,7 @@ public class FluidLevelChangeEvent extends BlockEvent implements Cancellable {
Preconditions.checkArgument(newData != null, "newData null");
Preconditions.checkArgument(this.newData.getMaterial().equals(newData.getMaterial()), "Cannot change fluid type");
this.newData = newData;
this.newData = newData.clone();
}
@Override

View file

@ -11,7 +11,7 @@ import org.jetbrains.annotations.Nullable;
/**
* Thrown whenever a LivingEntity dies
*/
public class EntityDeathEvent extends EntityEvent implements org.bukkit.event.Cancellable { // Paper - make cancellable
public class EntityDeathEvent extends EntityEvent implements org.bukkit.event.Cancellable { // Paper - make cancellable
private static final HandlerList handlers = new HandlerList();
private final DamageSource damageSource;
private final List<ItemStack> drops;

View file

@ -99,7 +99,7 @@ public class EntityKnockbackEvent extends EntityEvent implements Cancellable {
public void setFinalKnockback(@NotNull Vector knockback) {
Preconditions.checkArgument(knockback != null, "Knockback cannot be null");
this.knockback = knockback;
this.knockback = knockback.clone();
}
@Override

View file

@ -52,7 +52,7 @@ public class EntityTeleportEvent extends EntityEvent implements Cancellable {
* @param from New location this entity moved from
*/
public void setFrom(@NotNull Location from) {
this.from = from;
this.from = from.clone();
}
/**
@ -71,7 +71,7 @@ public class EntityTeleportEvent extends EntityEvent implements Cancellable {
* @param to New Location this entity moved to
*/
public void setTo(@Nullable Location to) {
this.to = to;
this.to = to != null ? to.clone() : null;
}
@NotNull

View file

@ -241,7 +241,7 @@ public class PlayerInteractEvent extends PlayerEvent implements Cancellable {
@Nullable
@Deprecated // Paper
public Vector getClickedPosition() {
return clickedPosistion;
return clickedPosistion.clone();
}
// Paper start

View file

@ -70,7 +70,7 @@ public class PlayerMoveEvent extends PlayerEvent implements Cancellable {
*/
public void setFrom(@NotNull Location from) {
validateLocation(from);
this.from = from;
this.from = from.clone();
}
/**
@ -90,7 +90,7 @@ public class PlayerMoveEvent extends PlayerEvent implements Cancellable {
*/
public void setTo(@NotNull Location to) {
validateLocation(to);
this.to = to;
this.to = to.clone();
}
// Paper start - PlayerMoveEvent improvements

View file

@ -69,7 +69,7 @@ public class PlayerRespawnEvent extends PlayerEvent {
Preconditions.checkArgument(respawnLocation != null, "Respawn location can not be null");
Preconditions.checkArgument(respawnLocation.getWorld() != null, "Respawn world can not be null");
this.respawnLocation = respawnLocation;
this.respawnLocation = respawnLocation.clone();
}
/**

View file

@ -45,7 +45,7 @@ public class PlayerVelocityEvent extends PlayerEvent implements Cancellable {
* @param velocity The velocity vector that will be sent to the player
*/
public void setVelocity(@NotNull Vector velocity) {
this.velocity = velocity;
this.velocity = velocity.clone();
}
@NotNull

View file

@ -45,7 +45,7 @@ public class AsyncStructureSpawnEvent extends WorldEvent implements Cancellable
*/
@NotNull
public BoundingBox getBoundingBox() {
return boundingBox;
return boundingBox.clone();
}
/**

View file

@ -25,7 +25,7 @@ public final class EquipmentSlotGroup implements Predicate<EquipmentSlot> {
public static final EquipmentSlotGroup LEGS = get("legs", EquipmentSlot.LEGS);
public static final EquipmentSlotGroup CHEST = get("chest", EquipmentSlot.CHEST);
public static final EquipmentSlotGroup HEAD = get("head", EquipmentSlot.HEAD);
public static final EquipmentSlotGroup ARMOR = get("armor", (test) -> test == EquipmentSlot.FEET || test == EquipmentSlot.LEGS || test == EquipmentSlot.CHEST || test == EquipmentSlot.HEAD || test == EquipmentSlot.BODY, EquipmentSlot.CHEST); // Paper - add missing slot type
public static final EquipmentSlotGroup ARMOR = get("armor", (test) -> test == EquipmentSlot.FEET || test == EquipmentSlot.LEGS || test == EquipmentSlot.CHEST || test == EquipmentSlot.HEAD || test == EquipmentSlot.BODY, EquipmentSlot.CHEST); // Paper - add missing slot type
public static final EquipmentSlotGroup BODY = get("body", EquipmentSlot.BODY); // Paper - add missing slot group
//
private final String key;

View file

@ -24,7 +24,7 @@ public final class LootContext {
private LootContext(@NotNull Location location, float luck, int lootingModifier, @Nullable Entity lootedEntity, @Nullable HumanEntity killer) {
Preconditions.checkArgument(location != null, "LootContext location cannot be null");
Preconditions.checkArgument(location.getWorld() != null, "LootContext World cannot be null");
this.location = location;
this.location = location.clone();
this.luck = luck;
this.lootingModifier = lootingModifier;
this.lootedEntity = lootedEntity;
@ -38,7 +38,7 @@ public final class LootContext {
*/
@NotNull
public Location getLocation() {
return location;
return location.clone();
}
/**
@ -110,7 +110,7 @@ public final class LootContext {
* @param location the location the LootContext should use
*/
public Builder(@NotNull Location location) {
this.location = location;
this.location = location.clone();
}
/**

View file

@ -27,9 +27,9 @@ public class Transformation {
Preconditions.checkArgument(scale != null, "scale cannot be null");
Preconditions.checkArgument(rightRotation != null, "rightRotation cannot be null");
this.translation = translation;
this.translation = new Vector3f(translation);
this.leftRotation = new Quaternionf(leftRotation);
this.scale = scale;
this.scale = new Vector3f(scale);
this.rightRotation = new Quaternionf(rightRotation);
}
@ -39,10 +39,10 @@ public class Transformation {
Preconditions.checkArgument(scale != null, "scale cannot be null");
Preconditions.checkArgument(rightRotation != null, "rightRotation cannot be null");
this.translation = translation;
this.leftRotation = leftRotation;
this.scale = scale;
this.rightRotation = rightRotation;
this.translation = new Vector3f(translation);
this.leftRotation = new Quaternionf(leftRotation);
this.scale = new Vector3f(scale);
this.rightRotation = new Quaternionf(rightRotation);
}
/**
@ -52,7 +52,7 @@ public class Transformation {
*/
@NotNull
public Vector3f getTranslation() {
return this.translation;
return new Vector3f(this.translation);
}
/**
@ -62,7 +62,7 @@ public class Transformation {
*/
@NotNull
public Quaternionf getLeftRotation() {
return this.leftRotation;
return new Quaternionf(this.leftRotation);
}
/**
@ -72,7 +72,7 @@ public class Transformation {
*/
@NotNull
public Vector3f getScale() {
return this.scale;
return new Vector3f(this.scale);
}
/**
@ -82,7 +82,7 @@ public class Transformation {
*/
@NotNull
public Quaternionf getRightRotation() {
return this.rightRotation;
return new Quaternionf(this.rightRotation);
}
@Override

View file

@ -37,7 +37,7 @@ public class PlayerSpawnLocationEvent extends PlayerEvent {
* @param location the spawn location
*/
public void setSpawnLocation(@NotNull Location location) {
this.spawnLocation = location;
this.spawnLocation = location.clone();
}
@NotNull

View file

@ -11,9 +11,9 @@ plugins {
val paperMavenPublicUrl = "https://repo.papermc.io/repository/maven-public/"
dependencies {
mache("io.papermc:mache:1.21.4+build.5")
mache("io.papermc:mache:1.21.4+build.6")
paperclip("io.papermc:paperclip:3.0.3")
remapper("net.fabricmc:tiny-remapper:0.10.3:fat")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
paperweight {
@ -23,23 +23,24 @@ paperweight {
paper {
reobfMappingsPatch = layout.projectDirectory.file("../build-data/reobf-mappings-patch.tiny")
reobfPackagesToFix.addAll(
"co.aikar.timings",
"com.destroystokyo.paper",
"com.mojang",
"io.papermc.paper",
"ca.spottedleaf",
"net.kyori.adventure.bossbar",
"net.minecraft",
"org.bukkit.craftbukkit",
"org.spigotmc",
)
}
spigot {
buildDataRef = "3edaf46ec1eed4115ce1b18d2846cded42577e42"
packageVersion = "v1_21_R3" // also needs to be updated in MappingEnvironment
}
reobfPackagesToFix.addAll(
"co.aikar.timings",
"com.destroystokyo.paper",
"com.mojang",
"io.papermc.paper",
"ca.spottedleaf",
"net.kyori.adventure.bossbar",
"net.minecraft",
"org.bukkit.craftbukkit",
"org.spigotmc",
)
}
tasks.generateDevelopmentBundle {
@ -58,45 +59,45 @@ abstract class Services {
}
val services = objects.newInstance<Services>()
publishing {
if (project.providers.gradleProperty("publishDevBundle").isPresent) {
val devBundleComponent = services.softwareComponentFactory.adhoc("devBundle")
components.add(devBundleComponent)
if (project.providers.gradleProperty("publishDevBundle").isPresent) {
val devBundleComponent = services.softwareComponentFactory.adhoc("devBundle")
components.add(devBundleComponent)
val devBundle = configurations.consumable("devBundle") {
attributes.attribute(DevBundleOutput.ATTRIBUTE, objects.named(DevBundleOutput.ZIP))
outgoing.artifact(tasks.generateDevelopmentBundle.flatMap { it.devBundleFile })
}
devBundleComponent.addVariantsFromConfiguration(devBundle.get()) {}
val devBundle = configurations.consumable("devBundle") {
attributes.attribute(DevBundleOutput.ATTRIBUTE, objects.named(DevBundleOutput.ZIP))
outgoing.artifact(tasks.generateDevelopmentBundle.flatMap { it.devBundleFile })
}
devBundleComponent.addVariantsFromConfiguration(devBundle.get()) {}
val runtime = configurations.consumable("serverRuntimeClasspath") {
attributes.attribute(DevBundleOutput.ATTRIBUTE, objects.named(DevBundleOutput.SERVER_DEPENDENCIES))
attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
extendsFrom(configurations.runtimeClasspath.get())
}
devBundleComponent.addVariantsFromConfiguration(runtime.get()) {
mapToMavenScope("runtime")
}
val runtime = configurations.consumable("serverRuntimeClasspath") {
attributes.attribute(DevBundleOutput.ATTRIBUTE, objects.named(DevBundleOutput.SERVER_DEPENDENCIES))
attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
extendsFrom(configurations.runtimeClasspath.get())
}
devBundleComponent.addVariantsFromConfiguration(runtime.get()) {
mapToMavenScope("runtime")
}
val compile = configurations.consumable("serverCompileClasspath") {
attributes.attribute(DevBundleOutput.ATTRIBUTE, objects.named(DevBundleOutput.SERVER_DEPENDENCIES))
attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_API))
extendsFrom(configurations.compileClasspath.get())
}
devBundleComponent.addVariantsFromConfiguration(compile.get()) {
mapToMavenScope("compile")
}
val compile = configurations.consumable("serverCompileClasspath") {
attributes.attribute(DevBundleOutput.ATTRIBUTE, objects.named(DevBundleOutput.SERVER_DEPENDENCIES))
attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_API))
extendsFrom(configurations.compileClasspath.get())
}
devBundleComponent.addVariantsFromConfiguration(compile.get()) {
mapToMavenScope("compile")
}
tasks.withType(GenerateMavenPom::class).configureEach {
doLast {
val text = destination.readText()
// Remove dependencies from pom, dev bundle is designed for gradle module metadata consumers
destination.writeText(
text.substringBefore("<dependencies>") + text.substringAfter("</dependencies>")
)
}
tasks.withType(GenerateMavenPom::class).configureEach {
doLast {
val text = destination.readText()
// Remove dependencies from pom, dev bundle is designed for gradle module metadata consumers
destination.writeText(
text.substringBefore("<dependencies>") + text.substringAfter("</dependencies>")
)
}
}
publishing {
publications.create<MavenPublication>("devBundle") {
artifactId = "dev-bundle"
from(devBundleComponent)
@ -149,6 +150,11 @@ dependencies {
runtimeOnly("org.xerial:sqlite-jdbc:3.47.0.0")
runtimeOnly("com.mysql:mysql-connector-j:9.1.0")
runtimeOnly("com.lmax:disruptor:3.4.4") // Paper
// Paper start - Use Velocity cipher
implementation("com.velocitypowered:velocity-native:3.3.0-SNAPSHOT") {
isTransitive = false
}
// Paper end - Use Velocity cipher
runtimeOnly("org.apache.maven:maven-resolver-provider:3.9.6")
runtimeOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.9.18")
@ -176,12 +182,12 @@ dependencies {
// Paper end - spark
}
tasks.jar {
manifest {
val git = Git(rootProject.layout.projectDirectory.path)
val mcVersion = rootProject.providers.gradleProperty("mcVersion").get()
val build = System.getenv("BUILD_NUMBER") ?: null
val buildTime = if (build != null) Instant.now() else Instant.EPOCH
val gitHash = git.exec(providers, "rev-parse", "--short=7", "HEAD").get().trim()
val implementationVersion = "$mcVersion-${build ?: "DEV"}-$gitHash"
val date = git.exec(providers, "show", "-s", "--format=%ci", gitHash).get().trim() // Paper
@ -197,7 +203,7 @@ tasks.jar {
"Brand-Id" to "papermc:paper",
"Brand-Name" to "Paper",
"Build-Number" to (build ?: ""),
"Build-Time" to Instant.now().toString(),
"Build-Time" to buildTime.toString(),
"Git-Branch" to gitBranch, // Paper
"Git-Commit" to gitHash, // Paper
)
@ -256,7 +262,7 @@ fun TaskContainer.registerRunTask(
name: String,
block: JavaExec.() -> Unit
): TaskProvider<JavaExec> = register<JavaExec>(name) {
group = "paper"
group = "runs"
mainClass.set("org.bukkit.craftbukkit.Main")
standardInput = System.`in`
workingDir = rootProject.layout.projectDirectory
@ -266,7 +272,7 @@ fun TaskContainer.registerRunTask(
languageVersion.set(JavaLanguageVersion.of(21))
vendor.set(JvmVendorSpec.JETBRAINS)
})
jvmArgs("-XX:+AllowEnhancedClassRedefinition", "-XX:+AllowRedefinitionToAddDeleteMethods")
jvmArgs("-XX:+AllowEnhancedClassRedefinition")
if (rootProject.childProjects["test-plugin"] != null) {
val testPluginJar = rootProject.project(":test-plugin").tasks.jar.flatMap { it.archiveFile }
@ -312,21 +318,21 @@ tasks.registerRunTask("runDevServer") {
tasks.registerRunTask("runBundler") {
description = "Spin up a test server from the Mojang mapped bundler jar"
classpath(tasks.named<io.papermc.paperweight.tasks.CreateBundlerJar>("createMojmapBundlerJar").flatMap { it.outputZip })
classpath(tasks.createMojmapBundlerJar.flatMap { it.outputZip })
mainClass.set(null as String?)
}
tasks.registerRunTask("runReobfBundler") {
description = "Spin up a test server from the reobf bundler jar"
classpath(rootProject.tasks.named<io.papermc.paperweight.tasks.CreateBundlerJar>("createReobfBundlerJar").flatMap { it.outputZip })
classpath(tasks.createReobfBundlerJar.flatMap { it.outputZip })
mainClass.set(null as String?)
}
tasks.registerRunTask("runPaperclip") {
description = "Spin up a test server from the Mojang mapped Paperclip jar"
classpath(tasks.named<io.papermc.paperweight.tasks.CreatePaperclipJar>("createMojmapPaperclipJar").flatMap { it.outputZip })
classpath(tasks.createMojmapPaperclipJar.flatMap { it.outputZip })
mainClass.set(null as String?)
}
tasks.registerRunTask("runReobfPaperclip") {
description = "Spin up a test server from the reobf Paperclip jar"
classpath(rootProject.tasks.named<io.papermc.paperweight.tasks.CreatePaperclipJar>("createReobfPaperclipJar").flatMap { it.outputZip })
classpath(tasks.createReobfPaperclipJar.flatMap { it.outputZip })
mainClass.set(null as String?)
}

View file

@ -27,34 +27,34 @@ and then catch exceptions and close if they fire.
Part of this commit was authored by: Spottedleaf, sandtechnology
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java
index 42f44c7cb0bd55ddfacd18acb0e596e7a953870e..ad8f8428b75e37097487cdfbd0db2421ee4cbe37 100644
--- a/net/minecraft/network/Connection.java
+++ b/net/minecraft/network/Connection.java
@@ -85,7 +85,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
private static final ProtocolInfo<ServerHandshakePacketListener> INITIAL_PROTOCOL = HandshakeProtocols.SERVERBOUND;
private final PacketFlow receiving;
private volatile boolean sendLoginDisconnect = true;
- private final Queue<Consumer<Connection>> pendingActions = Queues.newConcurrentLinkedQueue();
+ private final Queue<WrappedConsumer> pendingActions = Queues.newConcurrentLinkedQueue(); // Paper
+ private final Queue<WrappedConsumer> pendingActions = Queues.newConcurrentLinkedQueue(); // Paper - Optimize network
public Channel channel;
public SocketAddress address;
// Spigot Start
@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
public java.net.InetSocketAddress virtualHost;
private static boolean enableExplicitFlush = Boolean.getBoolean("paper.explicit-flush"); // Paper - Disable explicit network manager flushing
// Paper end
// Spigot start
@@ -145,6 +145,10 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
}
// Paper end - packet limiter
@Nullable public SocketAddress haProxyAddress; // Paper - Add API to get player's proxy address
+ // Paper start - Optimize network
+ public boolean isPending = true;
+ public boolean queueImmunity;
+ // Paper end - Optimize network
// Paper start - add utility methods
public final net.minecraft.server.level.ServerPlayer getPlayer() {
@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
public Connection(PacketFlow receiving) {
this.receiving = receiving;
@@ -423,11 +427,38 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
}
public void send(Packet<?> packet, @Nullable PacketSendListener callbacks, boolean flush) {
public void send(Packet<?> packet, @Nullable PacketSendListener listener, boolean flush) {
- if (this.isConnected()) {
- this.flushQueue();
+ // Paper start - Optimize network: Handle oversized packets better
@ -67,17 +67,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ 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, callbacks, flush);
this.sendPacket(packet, listener, flush);
} else {
- this.pendingActions.add((networkmanager) -> {
- networkmanager.sendPacket(packet, callbacks, 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<Packet<?>> extraPackets = InnerUtil.buildExtraPackets(packet);
+ final boolean hasExtraPackets = extraPackets != null && !extraPackets.isEmpty();
+ if (!hasExtraPackets) {
+ this.pendingActions.add(new PacketSendAction(packet, callbacks, flush));
+ this.pendingActions.add(new PacketSendAction(packet, listener, flush));
+ } else {
+ final java.util.List<PacketSendAction> 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
@ -85,31 +82,30 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ 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 ? callbacks : 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);
+ }
+
+ this.flushQueue();
+ // Paper end - Optimize network
+ }
}
public void runOnceConnected(Consumer<Connection> task) {
@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
this.flushQueue();
task.accept(this);
} else {
- this.pendingActions.add(task);
+ this.pendingActions.add(new WrappedConsumer(task)); // Paper - Optimize network
}
}
@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
}
private void doSendPacket(Packet<?> packet, @Nullable PacketSendListener callbacks, boolean flush) {
@@ -436,7 +467,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
this.flushQueue();
action.accept(this);
} else {
- this.pendingActions.add(action);
+ this.pendingActions.add(new WrappedConsumer(action)); // Paper - Optimize network
}
}
@@ -450,6 +481,14 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
}
private void doSendPacket(Packet<?> packet, @Nullable PacketSendListener sendListener, boolean flush) {
+ // Paper start - Optimize network
+ final net.minecraft.server.level.ServerPlayer player = this.getPlayer();
+ if (!this.isConnected()) {
@ -118,18 +114,18 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ }
+ try {
+ // Paper end - Optimize network
ChannelFuture channelfuture = flush ? this.channel.writeAndFlush(packet) : this.channel.write(packet);
if (callbacks != null) {
@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
ChannelFuture channelFuture = flush ? this.channel.writeAndFlush(packet) : this.channel.write(packet);
if (sendListener != null) {
channelFuture.addListener(future -> {
@@ -465,14 +504,24 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
});
}
+ // Paper start - Optimize network
+ if (packet.hasFinishListener()) {
+ channelfuture.addListener((ChannelFutureListener) channelFuture -> packet.onPacketDispatchFinish(player, channelFuture));
+ channelFuture.addListener((ChannelFutureListener) future -> packet.onPacketDispatchFinish(player, future));
+ }
channelfuture.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
channelFuture.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
+ } catch (final Exception e) {
+ LOGGER.error("NetworkException: {}", player, e);
+ this.disconnect(Component.translatable("disconnect.genericReason", "Internal Exception: " + e.getMessage()));
@ -145,16 +141,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
- this.pendingActions.add(Connection::flush);
+ this.pendingActions.add(new WrappedConsumer(Connection::flush)); // Paper - Optimize network
}
}
@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -484,16 +533,57 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
}
}
- private void flushQueue() {
- if (this.channel != null && this.channel.isOpen()) {
- Queue queue = this.pendingActions;
-
+ // Paper start - Optimize network: Rewrite this to be safer if ran off main thread
+ private boolean flushQueue() {
+ if (!this.isConnected()) {
@ -165,7 +159,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ } else if (this.isPending) {
+ // Should only happen during login/status stages
synchronized (this.pendingActions) {
- Consumer consumer;
- Consumer<Connection> consumer;
- while ((consumer = this.pendingActions.poll()) != null) {
- consumer.accept(this);
+ return this.processQueue();
+ }
+ }
@ -176,9 +172,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ if (this.pendingActions.isEmpty()) {
+ return true;
+ }
- while ((consumer = (Consumer) this.pendingActions.poll()) != null) {
- consumer.accept(this);
+
+ // If we are on main, we are safe here in that nothing else should be processing queue off main anymore
+ // But if we are not on main due to login/status, the parent is synchronized on packetQueue
+ final java.util.Iterator<WrappedConsumer> iterator = this.pendingActions.iterator();
@ -199,12 +193,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ if (!packet.isReady()) {
+ return false;
}
+ }
}
+
+ iterator.remove();
+ if (queued.tryMarkConsumed()) {
+ queued.accept(this);
}
+ }
}
+ return true;
}
@ -212,35 +206,35 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
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
@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
public void disconnect(DisconnectionDetails disconnectionInfo) {
// Spigot Start
this.preparing = false;
@@ -563,6 +653,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
public void disconnect(DisconnectionDetails disconnectionDetails) {
this.preparing = false; // Spigot
+ this.clearPacketQueue(); // Paper - Optimize network
// Spigot End
if (this.channel == null) {
this.delayedDisconnect = disconnectionInfo;
@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
this.delayedDisconnect = disconnectionDetails;
}
@@ -751,7 +842,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
public void handleDisconnection() {
if (this.channel != null && !this.channel.isOpen()) {
if (this.disconnectionHandled) {
- Connection.LOGGER.warn("handleDisconnection() called twice");
+ // Connection.LOGGER.warn("handleDisconnection() called twice"); // Paper - Don't log useless message
- LOGGER.warn("handleDisconnection() called twice");
+ // LOGGER.warn("handleDisconnection() called twice"); // Paper - Don't log useless message
} else {
this.disconnectionHandled = true;
PacketListener packetlistener = this.getPacketListener();
@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
packetlistener1.onDisconnect(disconnectiondetails);
PacketListener packetListener = this.getPacketListener();
@@ -762,7 +853,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
);
packetListener1.onDisconnect(disconnectionDetails);
}
- this.pendingActions.clear(); // Free up packet queue.
+ this.clearPacketQueue(); // Paper - Optimize network
// Paper start - Add PlayerConnectionCloseEvent
final PacketListener packetListener = this.getPacketListener();
if (packetListener instanceof net.minecraft.server.network.ServerCommonPacketListenerImpl commonPacketListener) {
@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
public void setBandwidthLogger(LocalSampleLogger log) {
this.bandwidthDebugMonitor = new BandwidthDebugMonitor(log);
/* Player was logged in, either game listener or configuration listener */
@@ -797,4 +888,93 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
public void setBandwidthLogger(LocalSampleLogger bandwithLogger) {
this.bandwidthDebugMonitor = new BandwidthDebugMonitor(bandwithLogger);
}
+
+ // Paper start - Optimize network
@ -332,11 +326,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ }
+ // Paper end - Optimize network
}
diff --git a/src/main/java/net/minecraft/network/protocol/Packet.java b/src/main/java/net/minecraft/network/protocol/Packet.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/network/protocol/Packet.java
+++ b/src/main/java/net/minecraft/network/protocol/Packet.java
@@ -0,0 +0,0 @@ public interface Packet<T extends PacketListener> {
diff --git a/net/minecraft/network/protocol/Packet.java b/net/minecraft/network/protocol/Packet.java
index 65ff8b9112ec76eeac48c679044fc02ae7d4ffeb..e4789584cbe43959681a8522c66eab58369bebd0 100644
--- a/net/minecraft/network/protocol/Packet.java
+++ b/net/minecraft/network/protocol/Packet.java
@@ -35,4 +35,32 @@ public interface Packet<T extends PacketListener> {
static <B extends ByteBuf, T extends Packet<?>> StreamCodec<B, T> codec(StreamMemberEncoder<B, T> encoder, StreamDecoder<B, T> decoder) {
return StreamCodec.ofMember(encoder, decoder);
}
@ -352,7 +346,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ * @param player Null if not at PLAY stage yet
+ * @param future Can be null if packet was cancelled
+ */
+ default void onPacketDispatchFinish(@org.jetbrains.annotations.Nullable net.minecraft.server.level.ServerPlayer player, @org.jetbrains.annotations.Nullable io.netty.channel.ChannelFuture future) {}
+ default void onPacketDispatchFinish(@org.jetbrains.annotations.Nullable net.minecraft.server.level.ServerPlayer player, @org.jetbrains.annotations.Nullable io.netty.channel.ChannelFuture future) {
+ }
+
+ default boolean hasFinishListener() {
+ return false;
@ -368,28 +363,29 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ }
+ // Paper end
}
diff --git a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
+++ b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
@@ -0,0 +0,0 @@ public class ServerConnectionListener {
final List<Connection> connections = Collections.synchronizedList(Lists.newArrayList());
diff --git a/net/minecraft/server/network/ServerConnectionListener.java b/net/minecraft/server/network/ServerConnectionListener.java
index b873af7183d9b1aabc87e63d254a4326f17b21c9..7de11ba404f0b60e7f7b7c16954811a343688219 100644
--- a/net/minecraft/server/network/ServerConnectionListener.java
+++ b/net/minecraft/server/network/ServerConnectionListener.java
@@ -66,11 +66,13 @@ public class ServerConnectionListener {
// Paper start - prevent blocking on adding a new connection while the server is ticking
private final java.util.Queue<Connection> pending = new java.util.concurrent.ConcurrentLinkedQueue<>();
+ private static final boolean disableFlushConsolidation = Boolean.getBoolean("Paper.disableFlushConsolidate"); // Paper - Optimize network
private final void addPending() {
Connection connection;
while ((connection = pending.poll()) != null) {
connections.add(connection);
while ((connection = this.pending.poll()) != null) {
this.connections.add(connection);
+ connection.isPending = false; // Paper - Optimize network
}
}
// Paper end - prevent blocking on adding a new connection while the server is ticking
@@ -0,0 +0,0 @@ public class ServerConnectionListener {
;
}
@@ -120,6 +122,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));
if (ServerConnectionListener.this.server.repliesToStatus()) {
+ if (!disableFlushConsolidation) channel.pipeline().addFirst(new io.netty.handler.flush.FlushConsolidationHandler()); // Paper - Optimize network
ChannelPipeline channelPipeline = channel.pipeline().addLast("timeout", new ReadTimeoutHandler(30));
if (ServerConnectionListener.this.server.repliesToStatus()) {
channelPipeline.addLast("legacy_query", new LegacyQueryHandler(ServerConnectionListener.this.getServer()));

View file

@ -30,36 +30,23 @@ This fix also maintains compatability if someone switches server jars to one wit
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/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
@@ -0,0 +0,0 @@ import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
+import java.util.zip.InflaterInputStream; // Paper
import javax.annotation.Nullable;
import net.minecraft.Util;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.profiling.jfr.JvmProfiler;
+import net.minecraft.nbt.CompoundTag; // Paper
+import net.minecraft.nbt.NbtIo; // Paper
import net.minecraft.world.level.ChunkPos;
import org.slf4j.Logger;
@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable {
this.usedSectors = new RegionBitmap();
this.info = storageKey;
diff --git a/net/minecraft/world/level/chunk/storage/RegionFile.java b/net/minecraft/world/level/chunk/storage/RegionFile.java
index d0854fa02be52f560fc91adeb8495a6bd22f6f74..783a2d80f6197dd0af0dc81909f0353a8ea2ecf4 100644
--- a/net/minecraft/world/level/chunk/storage/RegionFile.java
+++ b/net/minecraft/world/level/chunk/storage/RegionFile.java
@@ -53,6 +53,7 @@ public class RegionFile implements AutoCloseable {
this.info = info;
this.path = path;
+ initOversizedState(); // Paper
this.version = compressionFormat;
if (!Files.isDirectory(directory, new LinkOption[0])) {
throw new IllegalArgumentException("Expected directory, got " + String.valueOf(directory.toAbsolutePath()));
@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable {
this.version = version;
+ this.initOversizedState(); // Paper
if (!Files.isDirectory(externalFileDir)) {
throw new IllegalArgumentException("Expected directory, got " + externalFileDir.toAbsolutePath());
} else {
@@ -423,4 +424,75 @@ public class RegionFile implements AutoCloseable {
interface CommitOp {
void run() throws IOException;
}
+
+ // Paper start
+ private final byte[] oversized = new byte[1024];
+ private int oversizedCount;
@ -78,9 +65,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ private static int getChunkIndex(int x, int z) {
+ return (x & 31) + (z & 31) * 32;
+ }
+
+ synchronized boolean isOversized(int x, int z) {
+ return this.oversized[getChunkIndex(x, z)] == 1;
+ }
+
+ synchronized void setOversized(int x, int z, boolean oversized) throws IOException {
+ final int offset = getChunkIndex(x, z);
+ boolean previous = this.oversized[offset] == 1;
@ -120,22 +109,20 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ return this.path.getParent().resolve(this.path.getFileName().toString().replaceAll("\\.mca$", "") + "_oversized_" + x + "_" + z + ".nbt");
+ }
+
+ synchronized CompoundTag getOversizedData(int x, int z) throws IOException {
+ synchronized net.minecraft.nbt.CompoundTag getOversizedData(int x, int z) throws IOException {
+ Path file = getOversizedFile(x, z);
+ try (DataInputStream out = new DataInputStream(new java.io.BufferedInputStream(new InflaterInputStream(Files.newInputStream(file))))) {
+ return NbtIo.read((java.io.DataInput) out);
+ try (DataInputStream out = new DataInputStream(new java.io.BufferedInputStream(new java.util.zip.InflaterInputStream(Files.newInputStream(file))))) {
+ return net.minecraft.nbt.NbtIo.read((java.io.DataInput) out);
+ }
+
+ }
+ // Paper end
private class ChunkBuffer extends ByteArrayOutputStream {
private final ChunkPos pos;
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
@@ -0,0 +0,0 @@ public final class RegionFileStorage implements AutoCloseable {
}
diff --git a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
index 5ac84d6b47e7fdc16e1c09b739829de3d316bf5b..51bf310423013d0ae9d3202d66e36a053a767197 100644
--- a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+++ b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
@@ -47,6 +47,43 @@ public final class RegionFileStorage implements AutoCloseable {
}
}
@ -148,7 +135,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ synchronized (regionfile) {
+ try (DataInputStream datainputstream = regionfile.getChunkDataInputStream(chunkCoordinate)) {
+ CompoundTag oversizedData = regionfile.getOversizedData(chunkCoordinate.x, chunkCoordinate.z);
+ CompoundTag chunk = NbtIo.read((DataInput) datainputstream);
+ CompoundTag chunk = NbtIo.read(datainputstream);
+ if (oversizedData == null) {
+ return chunk;
+ }
@ -177,26 +164,26 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper end
+
@Nullable
public CompoundTag read(ChunkPos pos) throws IOException {
public CompoundTag read(ChunkPos chunkPos) throws IOException {
// CraftBukkit start - SPIGOT-5680: There's no good reason to preemptively create files on read, save that for writing
@@ -0,0 +0,0 @@ public final class RegionFileStorage implements AutoCloseable {
@@ -55,6 +92,12 @@ public final class RegionFileStorage implements AutoCloseable {
return null;
}
// CraftBukkit end
DataInputStream datainputstream = regionfile.getChunkDataInputStream(pos);
+ // Paper start
+ if (regionfile.isOversized(pos.x, pos.z)) {
+ printOversizedLog("Loading Oversized Chunk!", regionfile.getPath(), pos.x, pos.z);
+ return readOversizedChunk(regionfile, pos);
+ if (regionFile.isOversized(chunkPos.x, chunkPos.z)) {
+ printOversizedLog("Loading Oversized Chunk!", regionFile.getPath(), chunkPos.x, chunkPos.z);
+ return readOversizedChunk(regionFile, chunkPos);
+ }
+ // Paper end
CompoundTag nbttagcompound;
label43:
{
@@ -0,0 +0,0 @@ public final class RegionFileStorage implements AutoCloseable {
try {
NbtIo.write(nbt, (DataOutput) dataoutputstream);
+ regionfile.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
} catch (Throwable throwable) {
if (dataoutputstream != null) {
try {
CompoundTag var4;
try (DataInputStream chunkDataInputStream = regionFile.getChunkDataInputStream(chunkPos)) {
@@ -90,6 +133,7 @@ public final class RegionFileStorage implements AutoCloseable {
} 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
}
}
}

View file

@ -0,0 +1,861 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Fri, 13 May 2016 01:38:06 -0400
Subject: [PATCH] Entity Activation Range 2.0
Optimizes performance of Activation Range
Adds many new configurations and a new wake up inactive system
Fixes and adds new Immunities to improve gameplay behavior
Adds water Mobs to activation range config and nerfs fish
Adds flying monsters to control ghast and phantoms
Adds villagers as separate config
diff --git a/io/papermc/paper/entity/activation/ActivationRange.java b/io/papermc/paper/entity/activation/ActivationRange.java
new file mode 100644
index 0000000000000000000000000000000000000000..bd888ef719b9bfc93bace0b1d0fb771ac659f515
--- /dev/null
+++ b/io/papermc/paper/entity/activation/ActivationRange.java
@@ -0,0 +1,318 @@
+package io.papermc.paper.entity.activation;
+
+import net.minecraft.core.BlockPos;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.entity.ExperienceOrb;
+import net.minecraft.world.entity.FlyingMob;
+import net.minecraft.world.entity.LightningBolt;
+import net.minecraft.world.entity.LivingEntity;
+import net.minecraft.world.entity.Mob;
+import net.minecraft.world.entity.ai.Brain;
+import net.minecraft.world.entity.animal.Animal;
+import net.minecraft.world.entity.animal.Bee;
+import net.minecraft.world.entity.animal.Sheep;
+import net.minecraft.world.entity.animal.horse.Llama;
+import net.minecraft.world.entity.boss.EnderDragonPart;
+import net.minecraft.world.entity.boss.enderdragon.EndCrystal;
+import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
+import net.minecraft.world.entity.boss.wither.WitherBoss;
+import net.minecraft.world.entity.item.ItemEntity;
+import net.minecraft.world.entity.item.PrimedTnt;
+import net.minecraft.world.entity.monster.Creeper;
+import net.minecraft.world.entity.monster.Pillager;
+import net.minecraft.world.entity.npc.Villager;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.entity.projectile.AbstractArrow;
+import net.minecraft.world.entity.projectile.AbstractHurtingProjectile;
+import net.minecraft.world.entity.projectile.EyeOfEnder;
+import net.minecraft.world.entity.projectile.FireworkRocketEntity;
+import net.minecraft.world.entity.projectile.ThrowableProjectile;
+import net.minecraft.world.entity.projectile.ThrownTrident;
+import net.minecraft.world.entity.schedule.Activity;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.phys.AABB;
+import org.spigotmc.SpigotWorldConfig;
+
+public final class ActivationRange {
+
+ private ActivationRange() {
+ }
+
+ static Activity[] VILLAGER_PANIC_IMMUNITIES = {
+ Activity.HIDE,
+ Activity.PRE_RAID,
+ Activity.RAID,
+ Activity.PANIC
+ };
+
+ private static int checkInactiveWakeup(final Entity entity) {
+ final Level world = entity.level();
+ final SpigotWorldConfig config = world.spigotConfig;
+ final long inactiveFor = MinecraftServer.currentTick - entity.activatedTick;
+ if (entity.activationType == ActivationType.VILLAGER) {
+ if (inactiveFor > config.wakeUpInactiveVillagersEvery && world.wakeupInactiveRemainingVillagers > 0) {
+ world.wakeupInactiveRemainingVillagers--;
+ return config.wakeUpInactiveVillagersFor;
+ }
+ } else if (entity.activationType == ActivationType.ANIMAL) {
+ if (inactiveFor > config.wakeUpInactiveAnimalsEvery && world.wakeupInactiveRemainingAnimals > 0) {
+ world.wakeupInactiveRemainingAnimals--;
+ return config.wakeUpInactiveAnimalsFor;
+ }
+ } else if (entity.activationType == ActivationType.FLYING_MONSTER) {
+ if (inactiveFor > config.wakeUpInactiveFlyingEvery && world.wakeupInactiveRemainingFlying > 0) {
+ world.wakeupInactiveRemainingFlying--;
+ return config.wakeUpInactiveFlyingFor;
+ }
+ } else if (entity.activationType == ActivationType.MONSTER || entity.activationType == ActivationType.RAIDER) {
+ if (inactiveFor > config.wakeUpInactiveMonstersEvery && world.wakeupInactiveRemainingMonsters > 0) {
+ world.wakeupInactiveRemainingMonsters--;
+ return config.wakeUpInactiveMonstersFor;
+ }
+ }
+ return -1;
+ }
+
+ static AABB maxBB = new AABB(0, 0, 0, 0, 0, 0);
+
+ /**
+ * These entities are excluded from Activation range checks.
+ *
+ * @param entity
+ * @param config
+ * @return boolean If it should always tick.
+ */
+ public static boolean initializeEntityActivationState(final Entity entity, final SpigotWorldConfig config) {
+ return (entity.activationType == ActivationType.MISC && config.miscActivationRange == 0)
+ || (entity.activationType == ActivationType.RAIDER && config.raiderActivationRange == 0)
+ || (entity.activationType == ActivationType.ANIMAL && config.animalActivationRange == 0)
+ || (entity.activationType == ActivationType.MONSTER && config.monsterActivationRange == 0)
+ || (entity.activationType == ActivationType.VILLAGER && config.villagerActivationRange <= 0)
+ || (entity.activationType == ActivationType.WATER && config.waterActivationRange <= 0)
+ || (entity.activationType == ActivationType.FLYING_MONSTER && config.flyingMonsterActivationRange <= 0)
+ || entity instanceof EyeOfEnder
+ || entity instanceof Player
+ || entity instanceof ThrowableProjectile
+ || entity instanceof EnderDragon
+ || entity instanceof EnderDragonPart
+ || entity instanceof WitherBoss
+ || entity instanceof AbstractHurtingProjectile
+ || entity instanceof LightningBolt
+ || entity instanceof PrimedTnt
+ || entity instanceof net.minecraft.world.entity.item.FallingBlockEntity
+ || entity instanceof net.minecraft.world.entity.vehicle.AbstractMinecart
+ || entity instanceof net.minecraft.world.entity.vehicle.AbstractBoat
+ || entity instanceof EndCrystal
+ || entity instanceof FireworkRocketEntity
+ || entity instanceof ThrownTrident;
+ }
+
+ /**
+ * Find what entities are in range of the players in the world and set
+ * active if in range.
+ *
+ * @param world
+ */
+ public static void activateEntities(final Level world) {
+ final int miscActivationRange = world.spigotConfig.miscActivationRange;
+ final int raiderActivationRange = world.spigotConfig.raiderActivationRange;
+ final int animalActivationRange = world.spigotConfig.animalActivationRange;
+ final int monsterActivationRange = world.spigotConfig.monsterActivationRange;
+ final int waterActivationRange = world.spigotConfig.waterActivationRange;
+ final int flyingActivationRange = world.spigotConfig.flyingMonsterActivationRange;
+ final int villagerActivationRange = world.spigotConfig.villagerActivationRange;
+ world.wakeupInactiveRemainingAnimals = Math.min(world.wakeupInactiveRemainingAnimals + 1, world.spigotConfig.wakeUpInactiveAnimals);
+ world.wakeupInactiveRemainingVillagers = Math.min(world.wakeupInactiveRemainingVillagers + 1, world.spigotConfig.wakeUpInactiveVillagers);
+ world.wakeupInactiveRemainingMonsters = Math.min(world.wakeupInactiveRemainingMonsters + 1, world.spigotConfig.wakeUpInactiveMonsters);
+ world.wakeupInactiveRemainingFlying = Math.min(world.wakeupInactiveRemainingFlying + 1, world.spigotConfig.wakeUpInactiveFlying);
+
+ int maxRange = Math.max(monsterActivationRange, animalActivationRange);
+ maxRange = Math.max(maxRange, raiderActivationRange);
+ maxRange = Math.max(maxRange, miscActivationRange);
+ maxRange = Math.max(maxRange, flyingActivationRange);
+ maxRange = Math.max(maxRange, waterActivationRange);
+ maxRange = Math.max(maxRange, villagerActivationRange);
+ maxRange = Math.min((world.spigotConfig.simulationDistance << 4) - 8, maxRange);
+
+ for (final Player player : world.players()) {
+ player.activatedTick = MinecraftServer.currentTick;
+ if (world.spigotConfig.ignoreSpectatorActivation && player.isSpectator()) {
+ continue;
+ }
+
+ final int worldHeight = world.getHeight();
+ ActivationRange.maxBB = player.getBoundingBox().inflate(maxRange, worldHeight, maxRange);
+ ActivationType.MISC.boundingBox = player.getBoundingBox().inflate(miscActivationRange, worldHeight, miscActivationRange);
+ ActivationType.RAIDER.boundingBox = player.getBoundingBox().inflate(raiderActivationRange, worldHeight, raiderActivationRange);
+ ActivationType.ANIMAL.boundingBox = player.getBoundingBox().inflate(animalActivationRange, worldHeight, animalActivationRange);
+ ActivationType.MONSTER.boundingBox = player.getBoundingBox().inflate(monsterActivationRange, worldHeight, monsterActivationRange);
+ ActivationType.WATER.boundingBox = player.getBoundingBox().inflate(waterActivationRange, worldHeight, waterActivationRange);
+ ActivationType.FLYING_MONSTER.boundingBox = player.getBoundingBox().inflate(flyingActivationRange, worldHeight, flyingActivationRange);
+ ActivationType.VILLAGER.boundingBox = player.getBoundingBox().inflate(villagerActivationRange, worldHeight, villagerActivationRange);
+
+ final java.util.List<Entity> entities = world.getEntities((Entity) null, ActivationRange.maxBB, e -> true);
+ final boolean tickMarkers = world.paperConfig().entities.markers.tick;
+ for (final Entity entity : entities) {
+ if (!tickMarkers && entity instanceof net.minecraft.world.entity.Marker) {
+ continue;
+ }
+
+ ActivationRange.activateEntity(entity);
+ }
+ }
+ }
+
+ /**
+ * Tries to activate an entity.
+ *
+ * @param entity
+ */
+ private static void activateEntity(final Entity entity) {
+ if (MinecraftServer.currentTick > entity.activatedTick) {
+ if (entity.defaultActivationState) {
+ entity.activatedTick = MinecraftServer.currentTick;
+ return;
+ }
+ if (entity.activationType.boundingBox.intersects(entity.getBoundingBox())) {
+ entity.activatedTick = MinecraftServer.currentTick;
+ }
+ }
+ }
+
+ /**
+ * If an entity is not in range, do some more checks to see if we should
+ * give it a shot.
+ *
+ * @param entity
+ * @return
+ */
+ public static int checkEntityImmunities(final Entity entity) { // return # of ticks to get immunity
+ final SpigotWorldConfig config = entity.level().spigotConfig;
+ final int inactiveWakeUpImmunity = checkInactiveWakeup(entity);
+ if (inactiveWakeUpImmunity > -1) {
+ return inactiveWakeUpImmunity;
+ }
+ if (entity.getRemainingFireTicks() > 0) {
+ return 2;
+ }
+ if (entity.activatedImmunityTick >= MinecraftServer.currentTick) {
+ return 1;
+ }
+ final long inactiveFor = MinecraftServer.currentTick - entity.activatedTick;
+ if ((entity.activationType != ActivationType.WATER && entity.isInWater() && entity.isPushedByFluid())) {
+ return 100;
+ }
+ if (!entity.onGround() || entity.getDeltaMovement().horizontalDistanceSqr() > 9.999999747378752E-6D) {
+ return 100;
+ }
+ if (!(entity instanceof final AbstractArrow arrow)) {
+ if ((!entity.onGround() && !(entity instanceof FlyingMob))) {
+ return 10;
+ }
+ } else if (!arrow.isInGround()) {
+ return 1;
+ }
+ // special cases.
+ if (entity instanceof final LivingEntity living) {
+ if (living.onClimbable() || living.jumping || living.hurtTime > 0 || !living.activeEffects.isEmpty() || living.isFreezing()) {
+ return 1;
+ }
+ if (entity instanceof final Mob mob && mob.getTarget() != null) {
+ return 20;
+ }
+ if (entity instanceof final Bee bee) {
+ final BlockPos movingTarget = bee.getMovingTarget();
+ if (bee.isAngry() ||
+ (bee.getHivePos() != null && bee.getHivePos().equals(movingTarget)) ||
+ (bee.getSavedFlowerPos() != null && bee.getSavedFlowerPos().equals(movingTarget))
+ ) {
+ return 20;
+ }
+ }
+ if (entity instanceof final Villager villager) {
+ final Brain<Villager> behaviorController = villager.getBrain();
+
+ if (config.villagersActiveForPanic) {
+ for (final Activity activity : VILLAGER_PANIC_IMMUNITIES) {
+ if (behaviorController.isActive(activity)) {
+ return 20 * 5;
+ }
+ }
+ }
+
+ if (config.villagersWorkImmunityAfter > 0 && inactiveFor >= config.villagersWorkImmunityAfter) {
+ if (behaviorController.isActive(Activity.WORK)) {
+ return config.villagersWorkImmunityFor;
+ }
+ }
+ }
+ if (entity instanceof final Llama llama && llama.inCaravan()) {
+ return 1;
+ }
+ if (entity instanceof final Animal animal) {
+ if (animal.isBaby() || animal.isInLove()) {
+ return 5;
+ }
+ if (entity instanceof final Sheep sheep && sheep.isSheared()) {
+ return 1;
+ }
+ }
+ if (entity instanceof final Creeper creeper && creeper.isIgnited()) { // isExplosive
+ return 20;
+ }
+ if (entity instanceof final Mob mob && mob.targetSelector.hasTasks()) {
+ return 0;
+ }
+ if (entity instanceof final Pillager pillager) {
+ // TODO:?
+ }
+ }
+ // SPIGOT-6644: Otherwise the target refresh tick will be missed
+ if (entity instanceof ExperienceOrb) {
+ return 20;
+ }
+ return -1;
+ }
+
+ /**
+ * Checks if the entity is active for this tick.
+ *
+ * @param entity
+ * @return
+ */
+ public static boolean checkIfActive(final Entity entity) {
+ // Never safe to skip fireworks or item gravity
+ if (entity instanceof FireworkRocketEntity || (entity instanceof ItemEntity && (entity.tickCount + entity.getId()) % 4 == 0)) { // Needed for item gravity, see ItemEntity tick
+ return true;
+ }
+ // special case always immunities
+ // immunize brand-new entities, dead entities, and portal scenarios
+ if (entity.defaultActivationState || entity.tickCount < 20 * 10 || !entity.isAlive() || (entity.portalProcess != null && !entity.portalProcess.hasExpired()) || entity.portalCooldown > 0) {
+ return true;
+ }
+ // immunize leashed entities
+ if (entity instanceof final Mob mob && mob.getLeashHolder() instanceof Player) {
+ return true;
+ }
+
+ boolean isActive = entity.activatedTick >= MinecraftServer.currentTick;
+ entity.isTemporarilyActive = false;
+
+ // Should this entity tick?
+ if (!isActive) {
+ if ((MinecraftServer.currentTick - entity.activatedTick - 1) % 20 == 0) {
+ // Check immunities every 20 ticks.
+ final int immunity = checkEntityImmunities(entity);
+ if (immunity >= 0) {
+ entity.activatedTick = MinecraftServer.currentTick + immunity;
+ } else {
+ entity.isTemporarilyActive = true;
+ }
+ isActive = true;
+ }
+ }
+ // removed the original's dumb tick skipping for active entities
+ return isActive;
+ }
+}
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
index d95413af04121fe91ca0f3b0c70025b9808acef9..ad665c7535c615d2b03a3e7864be435f933235dd 100644
--- a/net/minecraft/server/level/ChunkMap.java
+++ b/net/minecraft/server/level/ChunkMap.java
@@ -4,7 +4,6 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Queues;
-import com.google.common.collect.Sets;
import com.google.common.collect.ImmutableList.Builder;
import com.mojang.datafixers.DataFixer;
import com.mojang.logging.LogUtils;
@@ -19,7 +18,6 @@ import it.unimi.dsi.fastutil.longs.LongIterator;
import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet;
-import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry;
import java.io.IOException;
import java.io.Writer;
import java.nio.file.Path;
@@ -95,7 +93,6 @@ import net.minecraft.world.level.levelgen.structure.StructureStart;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
import net.minecraft.world.level.storage.DimensionDataStorage;
import net.minecraft.world.level.storage.LevelStorageSource;
-import net.minecraft.world.phys.Vec3;
import org.apache.commons.lang3.mutable.MutableBoolean;
import org.slf4j.Logger;
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 3164f3784131babf9a6663335517a12df7e88a7b..da8848e2a3e3745949eb2356a049451d30f763a7 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -551,6 +551,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
profilerFiller.pop();
}
+ io.papermc.paper.entity.activation.ActivationRange.activateEntities(this); // Paper - EAR
this.entityTickList
.forEach(
entity -> {
@@ -979,12 +980,15 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
entity.tickCount++;
profilerFiller.push(() -> BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString());
profilerFiller.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();
for (Entity entity1 : entity.getPassengers()) {
- this.tickPassenger(entity, entity1);
+ this.tickPassenger(entity, entity1, isActive); // Paper - EAR 2
}
// Paper start - log detailed entity tick information
} finally {
@@ -995,7 +999,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
// 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)) {
@@ -1004,12 +1008,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");
+ // Paper start - EAR 2
+ if (isActive) {
passengerEntity.rideTick();
passengerEntity.postTick(); // CraftBukkit
+ } else {
+ passengerEntity.setDeltaMovement(Vec3.ZERO);
+ passengerEntity.inactiveTick();
+ // copied from inside of if (isPassenger()) of passengerTick, but that ifPassenger is unnecessary
+ ridingEntity.positionRider(passengerEntity);
+ }
+ // Paper end - EAR 2
profilerFiller.pop();
for (Entity entity : passengerEntity.getPassengers()) {
- this.tickPassenger(passengerEntity, entity);
+ this.tickPassenger(passengerEntity, entity, isActive); // Paper - EAR 2
}
}
}
diff --git a/net/minecraft/world/entity/AgeableMob.java b/net/minecraft/world/entity/AgeableMob.java
index a9f01e616ef6b0d74caf57cd68eb371a4fd30fd5..179f4e4b9b1eb57f78bbb2f9fa34b11ea79b7a88 100644
--- a/net/minecraft/world/entity/AgeableMob.java
+++ b/net/minecraft/world/entity/AgeableMob.java
@@ -126,6 +126,23 @@ public abstract class AgeableMob extends PathfinderMob {
super.onSyncedDataUpdated(key);
}
+ // Paper start - EAR 2
+ @Override
+ public void inactiveTick() {
+ super.inactiveTick();
+ if (this.level().isClientSide || this.ageLocked) { // CraftBukkit
+ this.refreshDimensions();
+ } else {
+ int age = this.getAge();
+ if (age < 0) {
+ this.setAge(++age);
+ } else if (age > 0) {
+ this.setAge(--age);
+ }
+ }
+ }
+ // Paper end - EAR 2
+
@Override
public void aiStep() {
super.aiStep();
diff --git a/net/minecraft/world/entity/AreaEffectCloud.java b/net/minecraft/world/entity/AreaEffectCloud.java
index 24735284fda151414d99faad401d25ba60995f9a..23b342cc31c7e72ade0e1ccad86a9ccf34380f13 100644
--- a/net/minecraft/world/entity/AreaEffectCloud.java
+++ b/net/minecraft/world/entity/AreaEffectCloud.java
@@ -128,6 +128,16 @@ public class AreaEffectCloud extends Entity implements TraceableEntity {
this.duration = duration;
}
+ // Paper start - EAR 2
+ @Override
+ public void inactiveTick() {
+ super.inactiveTick();
+ if (this.tickCount >= this.waitTime + this.duration) {
+ this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
+ }
+ }
+ // Paper end - EAR 2
+
@Override
public void tick() {
super.tick();
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index e7889c9c7b155db46730f5e168bb7fd3d1732a8c..334859c5ff7023c730513301cc11c9837b2c7823 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -380,6 +380,15 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
public boolean fixedPose = false; // Paper - Expand Pose API
private final int despawnTime; // Paper - entity despawn time limit
public final io.papermc.paper.entity.activation.ActivationType activationType = io.papermc.paper.entity.activation.ActivationType.activationTypeFor(this); // Paper - EAR 2/tracking ranges
+ // Paper start - EAR 2
+ public final boolean defaultActivationState;
+ public long activatedTick = Integer.MIN_VALUE;
+ public boolean isTemporarilyActive;
+ public long activatedImmunityTick = Integer.MIN_VALUE;
+
+ public void inactiveTick() {
+ }
+ // Paper end - EAR 2
public void setOrigin(@javax.annotation.Nonnull org.bukkit.Location location) {
this.origin = location.toVector();
@@ -417,6 +426,13 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
this.position = Vec3.ZERO;
this.blockPosition = BlockPos.ZERO;
this.chunkPosition = ChunkPos.ZERO;
+ // Paper start - EAR 2
+ if (level != null) {
+ this.defaultActivationState = io.papermc.paper.entity.activation.ActivationRange.initializeEntityActivationState(this, level.spigotConfig);
+ } else {
+ 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());
@@ -981,6 +997,8 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
} else {
this.wasOnFire = this.isOnFire();
if (type == MoverType.PISTON) {
+ this.activatedTick = Math.max(this.activatedTick, MinecraftServer.currentTick + 20); // Paper - EAR 2
+ this.activatedImmunityTick = Math.max(this.activatedImmunityTick, MinecraftServer.currentTick + 20); // Paper - EAR 2
movement = this.limitPistonMovement(movement);
if (movement.equals(Vec3.ZERO)) {
return;
@@ -994,6 +1012,13 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
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) {
+ setDeltaMovement(Vec3.ZERO);
+ profilerFiller.pop();
+ return;
+ }
+ // Paper end
movement = this.maybeBackOffFromEdge(movement, type);
Vec3 vec3 = this.collide(movement);
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
index a71a153a91de5a564b946091d8d3ccbb330e4b89..195e1151f7b2a32d6c4eb67edd1952e38f58b266 100644
--- a/net/minecraft/world/entity/LivingEntity.java
+++ b/net/minecraft/world/entity/LivingEntity.java
@@ -3094,6 +3094,14 @@ public abstract class LivingEntity extends Entity implements Attackable {
return false;
}
+ // Paper start - EAR 2
+ @Override
+ public void inactiveTick() {
+ super.inactiveTick();
+ ++this.noActionTime; // Above all the floats
+ }
+ // Paper end - EAR 2
+
@Override
public void tick() {
super.tick();
diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java
index f7d69db61d1293510428ae275e8a50571dde5ddf..1ed07fd23985a6bf8cf8300f74c92b7531a79fc6 100644
--- a/net/minecraft/world/entity/Mob.java
+++ b/net/minecraft/world/entity/Mob.java
@@ -215,6 +215,19 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab
return this.lookControl;
}
+ // Paper start
+ @Override
+ public void inactiveTick() {
+ super.inactiveTick();
+ if (this.goalSelector.inactiveTick()) {
+ this.goalSelector.tick();
+ }
+ if (this.targetSelector.inactiveTick()) {
+ this.targetSelector.tick();
+ }
+ }
+ // Paper end
+
public MoveControl getMoveControl() {
return this.getControlledVehicle() instanceof Mob mob ? mob.getMoveControl() : this.moveControl;
}
diff --git a/net/minecraft/world/entity/PathfinderMob.java b/net/minecraft/world/entity/PathfinderMob.java
index 0caf50ec50f056b83a20bbc6a2fe0144593aef39..af59a700755654eb68d6bf57d0712c4a2ac6c09b 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 {
super(entityType, level);
}
+ public BlockPos movingTarget; public BlockPos getMovingTarget() { return movingTarget; } // Paper
+
public float getWalkTargetValue(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 9338e63cc28413f5559bb0122ef5e04a84bd51d1..eeba224bd575451ba6023df65ef9d9b97f7f1c71 100644
--- a/net/minecraft/world/entity/ai/goal/GoalSelector.java
+++ b/net/minecraft/world/entity/ai/goal/GoalSelector.java
@@ -25,6 +25,7 @@ public class GoalSelector {
private final Map<Goal.Flag, WrappedGoal> lockedFlags = new EnumMap<>(Goal.Flag.class);
private final Set<WrappedGoal> availableGoals = new ObjectLinkedOpenHashSet<>();
private final EnumSet<Goal.Flag> 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));
@@ -35,6 +36,22 @@ public class GoalSelector {
this.availableGoals.removeIf(wrappedGoal -> filter.test(wrappedGoal.getGoal()));
}
+ // Paper start - EAR 2
+ public boolean inactiveTick() {
+ this.curRate++;
+ return this.curRate % 3 == 0; // TODO newGoalRate was already unused in 1.20.4, check if this is correct
+ }
+
+ public boolean hasTasks() {
+ for (WrappedGoal task : this.availableGoals) {
+ if (task.isRunning()) {
+ return true;
+ }
+ }
+ return false;
+ }
+ // Paper end - EAR 2
+
public void removeGoal(Goal goal) {
for (WrappedGoal wrappedGoal : this.availableGoals) {
if (wrappedGoal.getGoal() == goal && wrappedGoal.isRunning()) {
diff --git a/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java b/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
index 789fea258d70e60d38271ebb31270562dc7eb3ab..d0ab3db7bbd2942db19f473474371b20ce822608 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) {
this(mob, speedModifier, searchRange, 1);
}
+ // Paper start - activation range improvements
+ @Override
+ public void stop() {
+ super.stop();
+ this.blockPos = BlockPos.ZERO;
+ this.mob.movingTarget = null;
+ }
+ // Paper end
public MoveToBlockGoal(PathfinderMob mob, double speedModifier, int searchRange, 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.isWithinRestriction(mutableBlockPos) && this.isValidTarget(this.mob.level(), mutableBlockPos)) {
this.blockPos = mutableBlockPos;
+ this.mob.movingTarget = mutableBlockPos == BlockPos.ZERO ? null : mutableBlockPos.immutable(); // Paper
return true;
}
}
diff --git a/net/minecraft/world/entity/item/ItemEntity.java b/net/minecraft/world/entity/item/ItemEntity.java
index 8b034b6bda937b25dbb3d09b8293fed6d7dc512c..52a7ed0d991758bad0dcedcb7f97fb15ac6c6d04 100644
--- a/net/minecraft/world/entity/item/ItemEntity.java
+++ b/net/minecraft/world/entity/item/ItemEntity.java
@@ -124,6 +124,29 @@ public class ItemEntity extends Entity implements TraceableEntity {
return 0.04;
}
+ // Paper start - EAR 2
+ @Override
+ public void inactiveTick() {
+ super.inactiveTick();
+ if (this.pickupDelay > 0 && this.pickupDelay != 32767) {
+ this.pickupDelay--;
+ }
+ if (this.age != -32768) {
+ this.age++;
+ }
+
+ if (!this.level().isClientSide && this.age >= this.despawnRate) {// Paper - Alternative item-despawn-rate
+ // CraftBukkit start - fire ItemDespawnEvent
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callItemDespawnEvent(this).isCancelled()) {
+ this.age = 0;
+ return;
+ }
+ // CraftBukkit end
+ this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
+ }
+ }
+ // Paper end - EAR 2
+
@Override
public void tick() {
if (this.getItem().isEmpty()) {
diff --git a/net/minecraft/world/entity/npc/Villager.java b/net/minecraft/world/entity/npc/Villager.java
index 27568a1604d2dd5d46e836bbc25431929e218aa1..2b83262e4a13eae86df82913ce4f3121e3631a43 100644
--- a/net/minecraft/world/entity/npc/Villager.java
+++ b/net/minecraft/world/entity/npc/Villager.java
@@ -265,11 +265,35 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
return this.assignProfessionWhenSpawned;
}
+ // Paper start - EAR 2
+ @Override
+ public void inactiveTick() {
+ // SPIGOT-3874, SPIGOT-3894, SPIGOT-3846, SPIGOT-5286 :(
+ if (this.getUnhappyCounter() > 0) {
+ this.setUnhappyCounter(this.getUnhappyCounter() - 1);
+ }
+ if (this.isEffectiveAi()) {
+ if (this.level().spigotConfig.tickInactiveVillagers) {
+ this.customServerAiStep(this.level().getMinecraftWorld());
+ } else {
+ this.customServerAiStep(this.level().getMinecraftWorld(), true);
+ }
+ }
+ maybeDecayGossip();
+ super.inactiveTick();
+ }
+ // Paper end - EAR 2
+
@Override
protected void customServerAiStep(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");
- this.getBrain().tick(level, this);
+ if (!inactive) this.getBrain().tick(level, this); // Paper - EAR 2
profilerFiller.pop();
if (this.assignProfessionWhenSpawned) {
this.assignProfessionWhenSpawned = false;
@@ -293,7 +317,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
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()) {
level.broadcastEntityEvent(this, (byte)42);
@@ -303,6 +327,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
if (this.getVillagerData().getProfession() == VillagerProfession.NONE && this.isTrading()) {
this.stopTrading();
}
+ if (inactive) return; // Paper - EAR 2
super.customServerAiStep(level);
}
diff --git a/net/minecraft/world/entity/projectile/Arrow.java b/net/minecraft/world/entity/projectile/Arrow.java
index c1e09e701757a300183b62d343ded03033e63aa7..56574f8ef879159edc0114da09300143a2c79a35 100644
--- a/net/minecraft/world/entity/projectile/Arrow.java
+++ b/net/minecraft/world/entity/projectile/Arrow.java
@@ -66,6 +66,16 @@ public class Arrow extends AbstractArrow {
builder.define(ID_EFFECT_COLOR, -1);
}
+ // Paper start - EAR 2
+ @Override
+ public void inactiveTick() {
+ if (this.isInGround()) {
+ this.life++;
+ }
+ super.inactiveTick();
+ }
+ // Paper end
+
@Override
public void tick() {
super.tick();
diff --git a/net/minecraft/world/entity/projectile/FireworkRocketEntity.java b/net/minecraft/world/entity/projectile/FireworkRocketEntity.java
index 7c0862c50b44555fb27ce7dc46f4ec95a3eb0022..774ca9e0b56fd175ae246051de762d0c4256ca58 100644
--- a/net/minecraft/world/entity/projectile/FireworkRocketEntity.java
+++ b/net/minecraft/world/entity/projectile/FireworkRocketEntity.java
@@ -102,6 +102,21 @@ public class FireworkRocketEntity extends Projectile implements ItemSupplier {
return super.shouldRender(x, y, z) && !this.isAttachedToEntity();
}
+ // Paper start - EAR 2
+ @Override
+ public void inactiveTick() {
+ this.life++;
+ if (this.life > this.lifetime && this.level() instanceof ServerLevel serverLevel) {
+ // CraftBukkit start
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callFireworkExplodeEvent(this).isCancelled()) {
+ this.explode(serverLevel);
+ }
+ // CraftBukkit end
+ }
+ super.inactiveTick();
+ }
+ // Paper end - EAR 2
+
@Override
public void tick() {
super.tick();
diff --git a/net/minecraft/world/entity/vehicle/MinecartHopper.java b/net/minecraft/world/entity/vehicle/MinecartHopper.java
index c553cf0592dfa606dbbb1e6854d3377b9feb5efb..8341e7f01606fca90e69384c16fc19bb9e20d1b7 100644
--- a/net/minecraft/world/entity/vehicle/MinecartHopper.java
+++ b/net/minecraft/world/entity/vehicle/MinecartHopper.java
@@ -47,6 +47,7 @@ public class MinecartHopper extends AbstractMinecartContainer implements Hopper
if (flag != this.isEnabled()) {
this.setEnabled(flag);
}
+ this.immunize(); // Paper
}
public boolean isEnabled() {
@@ -100,11 +101,13 @@ public class MinecartHopper extends AbstractMinecartContainer implements Hopper
public boolean suckInItems() {
if (HopperBlockEntity.suckInItems(this.level(), this)) {
+ this.immunize(); // Paper
return true;
} else {
for (ItemEntity itemEntity : this.level()
.getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate(0.25, 0.0, 0.25), EntitySelector.ENTITY_STILL_ALIVE)) {
if (HopperBlockEntity.addItem(this, itemEntity)) {
+ this.immunize(); // Paper
return true;
}
}
@@ -139,4 +142,11 @@ public class MinecartHopper extends AbstractMinecartContainer implements Hopper
public AbstractContainerMenu createMenu(int id, Inventory playerInventory) {
return new HopperMenu(id, playerInventory, this);
}
+
+ // Paper start
+ public void immunize() {
+ this.activatedImmunityTick = Math.max(this.activatedImmunityTick, net.minecraft.server.MinecraftServer.currentTick + 20);
+ }
+ // Paper end
+
}
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index 32f184288f6065259c921293922c1b0163df4dc4..0f346faa82b988e86834c38837f6f11bea7f31c6 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -153,6 +153,12 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
public Map<BlockPos, BlockEntity> capturedTileEntities = new java.util.LinkedHashMap<>(); // Paper - Retain block place order when capturing blockstates
public List<net.minecraft.world.entity.item.ItemEntity> captureDrops;
public final it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap<SpawnCategory> ticksPerSpawnCategory = new it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap<>();
+ // Paper start
+ public int wakeupInactiveRemainingAnimals;
+ public int wakeupInactiveRemainingFlying;
+ public int wakeupInactiveRemainingMonsters;
+ public int wakeupInactiveRemainingVillagers;
+ // Paper end
public boolean populating;
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 754cfdcd5a28287aa3545aaffdce1e391cbefc1e..1e6e940fca9d96ef410c7bf05524bd9b24db4a79 100644
--- a/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
+++ b/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
@@ -149,6 +149,10 @@ public class PistonMovingBlockEntity extends BlockEntity {
}
entity.setDeltaMovement(d1, d2, d3);
+ // 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);
+ // Paper end
break;
}
}

View file

@ -0,0 +1,623 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: stonar96 <minecraft.stonar96@gmail.com>
Date: Thu, 25 Nov 2021 13:27:51 +0100
Subject: [PATCH] Anti-Xray
diff --git a/io/papermc/paper/FeatureHooks.java b/io/papermc/paper/FeatureHooks.java
index b97e0a8d4c429776b86def10739faee089e2bc9b..184e6c6fe2ba522d0ea0774604839320c4152371 100644
--- a/io/papermc/paper/FeatureHooks.java
+++ b/io/papermc/paper/FeatureHooks.java
@@ -7,6 +7,7 @@ import it.unimi.dsi.fastutil.longs.LongSets;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import it.unimi.dsi.fastutil.objects.ObjectSet;
import it.unimi.dsi.fastutil.objects.ObjectSets;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -36,20 +37,25 @@ public final class FeatureHooks {
}
public static LevelChunkSection createSection(final Registry<Biome> biomeRegistry, final Level level, final ChunkPos chunkPos, final int chunkSection) {
- return new LevelChunkSection(biomeRegistry);
+ return new LevelChunkSection(biomeRegistry, level, chunkPos, chunkSection); // Paper - Anti-Xray - Add parameters
}
public static void sendChunkRefreshPackets(final List<ServerPlayer> playersInRange, final LevelChunk chunk) {
- final ClientboundLevelChunkWithLightPacket refreshPacket = new ClientboundLevelChunkWithLightPacket(chunk, chunk.level.getLightEngine(), null, null);
+ // Paper start - Anti-Xray
+ final Map<Object, ClientboundLevelChunkWithLightPacket> refreshPackets = new HashMap<>();
for (final ServerPlayer player : playersInRange) {
if (player.connection == null) continue;
- player.connection.send(refreshPacket);
+ final Boolean shouldModify = chunk.getLevel().chunkPacketBlockController.shouldModify(player, chunk);
+ player.connection.send(refreshPackets.computeIfAbsent(shouldModify, s -> { // Use connection to prevent creating firing event
+ return new ClientboundLevelChunkWithLightPacket(chunk, chunk.level.getLightEngine(), null, null, (Boolean) s);
+ }));
}
+ // Paper end - Anti-Xray
}
public static PalettedContainer<BlockState> emptyPalettedBlockContainer() {
- return new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
+ return new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, null); // Paper - Anti-Xray - Add preset block states
}
public static Set<Long> getSentChunkKeys(final ServerPlayer player) {
diff --git a/net/minecraft/network/protocol/game/ClientboundChunksBiomesPacket.java b/net/minecraft/network/protocol/game/ClientboundChunksBiomesPacket.java
index d4872b7f4e9591b3b1c67406312905851303f521..cb41460e94161675e2ab43f4b1b5286ee38e2e13 100644
--- a/net/minecraft/network/protocol/game/ClientboundChunksBiomesPacket.java
+++ b/net/minecraft/network/protocol/game/ClientboundChunksBiomesPacket.java
@@ -70,8 +70,10 @@ public record ClientboundChunksBiomesPacket(List<ClientboundChunksBiomesPacket.C
}
public static void extractChunkData(FriendlyByteBuf buffer, LevelChunk chunk) {
+ int chunkSectionIndex = 0; // Paper - Anti-Xray
for (LevelChunkSection levelChunkSection : chunk.getSections()) {
- levelChunkSection.getBiomes().write(buffer);
+ levelChunkSection.getBiomes().write(buffer, null, chunkSectionIndex); // Paper - Anti-Xray
+ chunkSectionIndex++; // Paper - Anti-Xray
}
}
diff --git a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
index 5d1943d37dfad0c12e77179f0866851532d983e9..3aea76690bc3e35758d3bf274777130af17d8a0f 100644
--- a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
+++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
@@ -28,7 +28,13 @@ public class ClientboundLevelChunkPacketData {
private final byte[] buffer;
private final List<ClientboundLevelChunkPacketData.BlockEntityInfo> blockEntitiesData;
+ // Paper start - Anti-Xray - Add chunk packet info
+ @Deprecated @io.papermc.paper.annotation.DoNotUse
public ClientboundLevelChunkPacketData(LevelChunk levelChunk) {
+ this(levelChunk, null);
+ }
+ public ClientboundLevelChunkPacketData(LevelChunk levelChunk, io.papermc.paper.antixray.ChunkPacketInfo<net.minecraft.world.level.block.state.BlockState> chunkPacketInfo) {
+ // Paper end
this.heightmaps = new CompoundTag();
for (Entry<Heightmap.Types, Heightmap> entry : levelChunk.getHeightmaps()) {
@@ -38,7 +44,11 @@ public class ClientboundLevelChunkPacketData {
}
this.buffer = new byte[calculateChunkSize(levelChunk)];
- extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), levelChunk);
+ // Paper start - Anti-Xray - Add chunk packet info
+ if (chunkPacketInfo != null) {
+ chunkPacketInfo.setBuffer(this.buffer);
+ }
+ extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), levelChunk, chunkPacketInfo);
this.blockEntitiesData = Lists.newArrayList();
for (Entry<BlockPos, BlockEntity> entryx : levelChunk.getBlockEntities().entrySet()) {
@@ -85,9 +95,17 @@ public class ClientboundLevelChunkPacketData {
return byteBuf;
}
+ // Paper start - Anti-Xray - Add chunk packet info
+ @Deprecated @io.papermc.paper.annotation.DoNotUse
public static void extractChunkData(FriendlyByteBuf buffer, LevelChunk chunk) {
+ ClientboundLevelChunkPacketData.extractChunkData(buffer, chunk, null);
+ }
+ public static void extractChunkData(FriendlyByteBuf buffer, LevelChunk chunk, io.papermc.paper.antixray.ChunkPacketInfo<net.minecraft.world.level.block.state.BlockState> chunkPacketInfo) {
+ int chunkSectionIndex = 0;
for (LevelChunkSection levelChunkSection : chunk.getSections()) {
- levelChunkSection.write(buffer);
+ levelChunkSection.write(buffer, chunkPacketInfo, chunkSectionIndex);
+ chunkSectionIndex++;
+ // Paper end - Anti-Xray - Add chunk packet info
}
}
diff --git a/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java b/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
index 3a384175f8e7f204234bbaf3081bdc20c47a0d4b..5699bc15eba92e22433a20cb8326b59f2ebd3036 100644
--- a/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
+++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
@@ -18,18 +18,31 @@ public class ClientboundLevelChunkWithLightPacket implements Packet<ClientGamePa
private final int z;
private final ClientboundLevelChunkPacketData chunkData;
private final ClientboundLightUpdatePacketData lightData;
- // Paper start - Anti-Xray
+ // Paper start - Async-Anti-Xray - Ready flag for the connection, add chunk packet info
+ private volatile boolean ready;
+
+ @Override
+ public boolean isReady() {
+ return this.ready;
+ }
+
public void setReady(final boolean ready) {
- // Empty hook, updated by feature patch
+ this.ready = ready;
}
- // Paper end - Anti-Xray
+ @Deprecated @io.papermc.paper.annotation.DoNotUse
public ClientboundLevelChunkWithLightPacket(LevelChunk chunk, LevelLightEngine lightEngine, @Nullable BitSet skyLight, @Nullable BitSet blockLight) {
+ this(chunk, lightEngine, skyLight, blockLight, true);
+ }
+ public ClientboundLevelChunkWithLightPacket(LevelChunk chunk, LevelLightEngine lightEngine, @Nullable BitSet skyLight, @Nullable BitSet blockLight, boolean modifyBlocks) {
+ // Paper end - Anti-Xray
ChunkPos pos = chunk.getPos();
this.x = pos.x;
this.z = pos.z;
- this.chunkData = new ClientboundLevelChunkPacketData(chunk);
+ io.papermc.paper.antixray.ChunkPacketInfo<net.minecraft.world.level.block.state.BlockState> chunkPacketInfo = modifyBlocks ? chunk.getLevel().chunkPacketBlockController.getChunkPacketInfo(this, chunk) : null; // Paper - Ant-Xray
+ this.chunkData = new ClientboundLevelChunkPacketData(chunk, chunkPacketInfo); // Paper - Anti-Xray
this.lightData = new ClientboundLightUpdatePacketData(pos, lightEngine, skyLight, blockLight);
+ chunk.getLevel().chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo); // Paper - Anti-Xray - Modify blocks
}
private ClientboundLevelChunkWithLightPacket(RegistryFriendlyByteBuf buffer) {
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index da8848e2a3e3745949eb2356a049451d30f763a7..192977dd661ee795ada13db895db770293e9b402 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -348,7 +348,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
org.bukkit.generator.BiomeProvider biomeProvider // CraftBukkit
) {
// 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.location(), spigotConfig, server.registryAccess(), serverLevelData.getGameRules()))); // Paper - create paper world configs
+ 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.location(), spigotConfig, server.registryAccess(), serverLevelData.getGameRules())), dispatcher); // Paper - create paper world configs; Async-Anti-Xray: Pass executor
this.pvpMode = server.isPvpAllowed();
this.levelStorageAccess = levelStorageAccess;
this.uuid = org.bukkit.craftbukkit.util.WorldUUID.getUUID(levelStorageAccess.levelDirectory.path().toFile());
diff --git a/net/minecraft/server/level/ServerPlayerGameMode.java b/net/minecraft/server/level/ServerPlayerGameMode.java
index 47ed3ad5c0b4753f58e0bafff5e5e70b2f0bb40b..623c069f1fe079e020c6391a3db1a3d95cd3dbf5 100644
--- a/net/minecraft/server/level/ServerPlayerGameMode.java
+++ b/net/minecraft/server/level/ServerPlayerGameMode.java
@@ -299,6 +299,7 @@ public class ServerPlayerGameMode {
org.bukkit.craftbukkit.event.CraftEventFactory.callBlockDamageAbortEvent(this.player, pos, this.player.getInventory().getSelected()); // CraftBukkit
}
}
+ this.level.chunkPacketBlockController.onPlayerLeftClickBlock(this, pos, action, face, maxBuildHeight, sequence); // Paper - Anti-Xray
}
public void destroyAndAck(BlockPos pos, int sequence, String message) {
diff --git a/net/minecraft/server/network/PlayerChunkSender.java b/net/minecraft/server/network/PlayerChunkSender.java
index 342bc843c384761e883de861044f4f8930ae8763..14878690a88fd4de3e2c127086607e6c819c636c 100644
--- a/net/minecraft/server/network/PlayerChunkSender.java
+++ b/net/minecraft/server/network/PlayerChunkSender.java
@@ -78,8 +78,11 @@ public class PlayerChunkSender {
}
}
- private static void sendChunk(ServerGamePacketListenerImpl packetListener, ServerLevel level, LevelChunk chunk) {
- packetListener.send(new ClientboundLevelChunkWithLightPacket(chunk, level.getLightEngine(), null, null));
+ // Paper start - Anti-Xray
+ public static void sendChunk(ServerGamePacketListenerImpl packetListener, ServerLevel level, LevelChunk chunk) {
+ final boolean shouldModify = level.chunkPacketBlockController.shouldModify(packetListener.player, chunk);
+ packetListener.send(new ClientboundLevelChunkWithLightPacket(chunk, level.getLightEngine(), null, null, shouldModify));
+ // Paper end - Anti-Xray
// 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();
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index 38fb0f569ffcd96e0eb6cb6f0769155a17d62874..3d5d84c1c1d431e9b369aa727ab0876f90ff5d61 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -403,7 +403,7 @@ public abstract class PlayerList {
.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)
+ serverLevel.getLightEngine(), (java.util.BitSet)null, (java.util.BitSet) null, true) // Paper - Anti-Xray
);
}
// Paper end - Send empty chunk
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index 0f346faa82b988e86834c38837f6f11bea7f31c6..771d6ed6a7c889c09efd4ff6e20298c851eaa79f 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -168,6 +168,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
}
// Paper end - add paper world config
+ public final io.papermc.paper.antixray.ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray
public static BlockPos lastPhysicsProblem; // Spigot
private org.spigotmc.TickLimiter entityLimiter;
private org.spigotmc.TickLimiter tileLimiter;
@@ -214,7 +215,8 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
org.bukkit.generator.BiomeProvider biomeProvider, // CraftBukkit
org.bukkit.World.Environment env, // CraftBukkit
java.util.function.Function<org.spigotmc.SpigotWorldConfig, // Spigot - create per world config
- io.papermc.paper.configuration.WorldConfiguration> paperWorldConfigCreator // Paper - create paper world config
+ io.papermc.paper.configuration.WorldConfiguration> paperWorldConfigCreator, // Paper - create paper world config
+ java.util.concurrent.Executor executor // Paper - Anti-Xray
) {
this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) levelData).getLevelName()); // Spigot
this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config
@@ -295,6 +297,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
// CraftBukkit end
this.entityLimiter = new org.spigotmc.TickLimiter(this.spigotConfig.entityMaxTickTime);
this.tileLimiter = new org.spigotmc.TickLimiter(this.spigotConfig.tileMaxTickTime);
+ this.chunkPacketBlockController = this.paperConfig().anticheat.antiXray.enabled ? new io.papermc.paper.antixray.ChunkPacketBlockControllerAntiXray(this, executor) : io.papermc.paper.antixray.ChunkPacketBlockController.NO_OPERATION_INSTANCE; // Paper - Anti-Xray
}
// Paper start - Cancel hit for vanished players
@@ -495,6 +498,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
// CraftBukkit end
BlockState blockState = chunkAt.setBlockState(pos, state, (flags & 64) != 0, (flags & 1024) == 0); // CraftBukkit custom NO_PLACE flag
+ this.chunkPacketBlockController.onBlockChange(this, pos, state, blockState, flags, recursionLeft); // Paper - Anti-Xray
if (blockState == null) {
// CraftBukkit start - remove blockstate if failed (or the same)
diff --git a/net/minecraft/world/level/chunk/ChunkAccess.java b/net/minecraft/world/level/chunk/ChunkAccess.java
index 809b3c37d3749c76c3c243cd91c593d03693e9b3..860d1c9729c4ee97ec6f40f7aa969829070b27c0 100644
--- a/net/minecraft/world/level/chunk/ChunkAccess.java
+++ b/net/minecraft/world/level/chunk/ChunkAccess.java
@@ -114,14 +114,14 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh
}
}
- replaceMissingSections(biomeRegistry, this.sections);
+ this.replaceMissingSections(biomeRegistry, this.sections); // Paper - Anti-Xray - make it a non-static method
this.biomeRegistry = biomeRegistry; // CraftBukkit
}
- private static void replaceMissingSections(Registry<Biome> biomeRegistry, LevelChunkSection[] sections) {
+ private void replaceMissingSections(Registry<Biome> biomeRegistry, LevelChunkSection[] sections) { // Paper - Anti-Xray - make it a non-static method
for (int i = 0; i < sections.length; i++) {
if (sections[i] == null) {
- sections[i] = new LevelChunkSection(biomeRegistry);
+ sections[i] = new LevelChunkSection(biomeRegistry, this.levelHeightAccessor instanceof net.minecraft.world.level.Level ? (net.minecraft.world.level.Level) this.levelHeightAccessor : null, this.chunkPos, this.levelHeightAccessor.getSectionYFromSectionIndex(i)); // Paper - Anti-Xray - Add parameters
}
}
}
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
index 51a136cf015de730ca0d1b48cf618a2ed69ea89f..96b0342ab7b922aa16d07b6c00542e6cb66c974a 100644
--- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -109,7 +109,7 @@ public class LevelChunk extends ChunkAccess {
@Nullable LevelChunk.PostLoadProcessor postLoad,
@Nullable BlendingData blendingData
) {
- super(pos, data, level, level.registryAccess().lookupOrThrow(Registries.BIOME), inhabitedTime, sections, blendingData);
+ super(pos, data, level, net.minecraft.server.MinecraftServer.getServer().registryAccess().lookupOrThrow(Registries.BIOME), inhabitedTime, sections, blendingData); // Paper - Anti-Xray - The world isn't ready yet, use server singleton for registry
this.level = (net.minecraft.server.level.ServerLevel) level; // CraftBukkit - type
this.gameEventListenerRegistrySections = new Int2ObjectOpenHashMap<>();
diff --git a/net/minecraft/world/level/chunk/LevelChunkSection.java b/net/minecraft/world/level/chunk/LevelChunkSection.java
index baa9f3e2e6e45c250930658e82bad70a3a292b05..fc21c3268c4b4fda2933d71f0913db28e3796653 100644
--- a/net/minecraft/world/level/chunk/LevelChunkSection.java
+++ b/net/minecraft/world/level/chunk/LevelChunkSection.java
@@ -38,9 +38,15 @@ public class LevelChunkSection {
this.recalcBlockCounts();
}
+ // Paper start - Anti-Xray - Add parameters
+ @Deprecated @io.papermc.paper.annotation.DoNotUse
public LevelChunkSection(Registry<Biome> biomeRegistry) {
- this.states = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
- this.biomes = new PalettedContainer<>(biomeRegistry.asHolderIdMap(), biomeRegistry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
+ this(biomeRegistry, null, null, 0);
+ }
+ public LevelChunkSection(Registry<Biome> biomeRegistry, net.minecraft.world.level.Level level, net.minecraft.world.level.ChunkPos chunkPos, int chunkSectionY) {
+ // Paper end - Anti-Xray
+ this.states = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, level == null || level.chunkPacketBlockController == null ? null : level.chunkPacketBlockController.getPresetBlockStates(level, chunkPos, chunkSectionY)); // Paper - Anti-Xray - Add preset block states
+ this.biomes = new PalettedContainer<>(biomeRegistry.asHolderIdMap(), biomeRegistry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES, null); // Paper - Anti-Xray - Add preset biomes
}
public BlockState getBlockState(int x, int y, int z) {
@@ -168,10 +174,16 @@ public class LevelChunkSection {
this.biomes = palettedContainer;
}
+ // Paper start - Anti-Xray - Add chunk packet info
+ @Deprecated @io.papermc.paper.annotation.DoNotUse
public void write(FriendlyByteBuf buffer) {
+ this.write(buffer, null, 0);
+ }
+ public void write(FriendlyByteBuf buffer, io.papermc.paper.antixray.ChunkPacketInfo<BlockState> chunkPacketInfo, int chunkSectionIndex) {
buffer.writeShort(this.nonEmptyBlockCount);
- this.states.write(buffer);
- this.biomes.write(buffer);
+ this.states.write(buffer, chunkPacketInfo, chunkSectionIndex);
+ this.biomes.write(buffer, null, chunkSectionIndex);
+ // Paper end - Anti-Xray
}
public int getSerializedSize() {
diff --git a/net/minecraft/world/level/chunk/PalettedContainer.java b/net/minecraft/world/level/chunk/PalettedContainer.java
index e8ec28ce3fe13561b45c4654e174776d9d2d7b71..a6028a54c75de068515e95913b21160ab4326985 100644
--- a/net/minecraft/world/level/chunk/PalettedContainer.java
+++ b/net/minecraft/world/level/chunk/PalettedContainer.java
@@ -28,6 +28,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
private static final int MIN_PALETTE_BITS = 0;
private final PaletteResize<T> dummyPaletteResize = (bits, objectAdded) -> 0;
public final IdMap<T> registry;
+ private final T @org.jetbrains.annotations.Nullable [] presetValues; // Paper - Anti-Xray - Add preset values
private volatile PalettedContainer.Data<T> data;
private final PalettedContainer.Strategy strategy;
//private final ThreadingDetector threadingDetector = new ThreadingDetector("PalettedContainer"); // Paper - unused
@@ -40,13 +41,21 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
// this.threadingDetector.checkAndUnlock(); // Paper - disable this - use proper synchronization
}
+ // Paper start - Anti-Xray - Add preset values
+ @Deprecated @io.papermc.paper.annotation.DoNotUse
public static <T> Codec<PalettedContainer<T>> codecRW(IdMap<T> registry, Codec<T> codec, PalettedContainer.Strategy strategy, T value) {
- PalettedContainerRO.Unpacker<T, PalettedContainer<T>> unpacker = PalettedContainer::unpack;
+ return PalettedContainer.codecRW(registry, codec, strategy, value, null);
+ }
+ public static <T> Codec<PalettedContainer<T>> codecRW(IdMap<T> registry, Codec<T> codec, PalettedContainer.Strategy strategy, T value, T @org.jetbrains.annotations.Nullable [] presetValues) {
+ PalettedContainerRO.Unpacker<T, PalettedContainer<T>> unpacker = (idListx, paletteProviderx, serialized) -> {
+ return unpack(idListx, paletteProviderx, serialized, value, presetValues);
+ };
+ // Paper end
return codec(registry, codec, strategy, value, unpacker);
}
public static <T> Codec<PalettedContainerRO<T>> codecRO(IdMap<T> registry, Codec<T> codec, PalettedContainer.Strategy strategy, T value) {
- PalettedContainerRO.Unpacker<T, PalettedContainerRO<T>> unpacker = (registry1, strategy1, packedData) -> unpack(registry1, strategy1, packedData)
+ PalettedContainerRO.Unpacker<T, PalettedContainerRO<T>> unpacker = (registry1, strategy1, packedData) -> unpack(registry1, strategy1, packedData, value, null) // Paper - Anti-Xray - Add preset values
.map(container -> (PalettedContainerRO<T>)container);
return codec(registry, codec, strategy, value, unpacker);
}
@@ -66,27 +75,66 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
);
}
+ // Paper start - Anti-Xray - Add preset values
+ @Deprecated @io.papermc.paper.annotation.DoNotUse
+ public PalettedContainer(IdMap<T> registry, PalettedContainer.Strategy strategy, PalettedContainer.Configuration<T> configuration, BitStorage storage, List<T> values) {
+ this(registry, strategy, configuration, storage, values, null, null);
+ }
public PalettedContainer(
- IdMap<T> registry, PalettedContainer.Strategy strategy, PalettedContainer.Configuration<T> configuration, BitStorage storage, List<T> values
+ IdMap<T> registry, PalettedContainer.Strategy strategy, PalettedContainer.Configuration<T> configuration, BitStorage storage, List<T> values, T defaultValue, T @org.jetbrains.annotations.Nullable [] presetValues
) {
+ this.presetValues = presetValues;
this.registry = registry;
this.strategy = strategy;
this.data = new PalettedContainer.Data<>(configuration, storage, configuration.factory().create(configuration.bits(), registry, this, values));
+ if (presetValues != null && (configuration.factory() == PalettedContainer.Strategy.SINGLE_VALUE_PALETTE_FACTORY ? this.data.palette.valueFor(0) != defaultValue : configuration.factory() != PalettedContainer.Strategy.GLOBAL_PALETTE_FACTORY)) {
+ // In 1.18 Mojang unfortunately removed code that already handled possible resize operations on read from disk for us
+ // We readd this here but in a smarter way than it was before
+ int maxSize = 1 << configuration.bits();
+
+ for (T presetValue : presetValues) {
+ if (this.data.palette.getSize() >= maxSize) {
+ java.util.Set<T> allValues = new java.util.HashSet<>(values);
+ allValues.addAll(Arrays.asList(presetValues));
+ int newBits = Mth.ceillog2(allValues.size());
+
+ if (newBits > configuration.bits()) {
+ this.onResize(newBits, null);
+ }
+
+ break;
+ }
+
+ this.data.palette.idFor(presetValue);
+ }
+ }
+ // Paper end
}
- private PalettedContainer(IdMap<T> registry, PalettedContainer.Strategy strategy, PalettedContainer.Data<T> data) {
+ // Paper start - Anti-Xray - Add preset values
+ private PalettedContainer(IdMap<T> registry, PalettedContainer.Strategy strategy, PalettedContainer.Data<T> data, T @org.jetbrains.annotations.Nullable [] presetValues) {
+ this.presetValues = presetValues;
+ // Paper end - Anti-Xray
this.registry = registry;
this.strategy = strategy;
this.data = data;
}
- private PalettedContainer(PalettedContainer<T> other) {
+ private PalettedContainer(PalettedContainer<T> other, T @org.jetbrains.annotations.Nullable [] presetValues) { // Paper - Anti-Xray - Add preset values
+ this.presetValues = presetValues; // Paper - Anti-Xray - Add preset values
this.registry = other.registry;
this.strategy = other.strategy;
this.data = other.data.copy(this);
}
+ // Paper start - Anti-Xray - Add preset values
+ @Deprecated @io.papermc.paper.annotation.DoNotUse
public PalettedContainer(IdMap<T> registry, T palette, PalettedContainer.Strategy strategy) {
+ this(registry, palette, strategy, null);
+ }
+ public PalettedContainer(IdMap<T> registry, T palette, PalettedContainer.Strategy strategy, T @org.jetbrains.annotations.Nullable [] presetValues) {
+ this.presetValues = presetValues;
+ // Paper end - Anti-Xray
this.strategy = strategy;
this.registry = registry;
this.data = this.createOrReuseData(null, 0);
@@ -101,11 +149,30 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
@Override
public synchronized int onResize(int bits, T objectAdded) { // Paper - synchronize
PalettedContainer.Data<T> data = this.data;
+ // Paper start - Anti-Xray - Add preset values
+ if (this.presetValues != null && objectAdded != null && data.configuration().factory() == PalettedContainer.Strategy.SINGLE_VALUE_PALETTE_FACTORY) {
+ int duplicates = 0;
+ List<T> presetValues = Arrays.asList(this.presetValues);
+ duplicates += presetValues.contains(objectAdded) ? 1 : 0;
+ duplicates += presetValues.contains(data.palette.valueFor(0)) ? 1 : 0;
+ bits = Mth.ceillog2((1 << this.strategy.calculateBitsForSerialization(this.registry, 1 << bits)) + presetValues.size() - duplicates);
+ }
+ // Paper end - Anti-Xray
PalettedContainer.Data<T> data1 = this.createOrReuseData(data, bits);
data1.copyFrom(data.palette, data.storage);
this.data = data1;
- return data1.palette.idFor(objectAdded);
+ // Paper start - Anti-Xray
+ this.addPresetValues();
+ return objectAdded == null ? -1 : data1.palette.idFor(objectAdded);
+ }
+ private void addPresetValues() {
+ if (this.presetValues != null && this.data.configuration().factory() != PalettedContainer.Strategy.GLOBAL_PALETTE_FACTORY) {
+ for (T presetValue : this.presetValues) {
+ this.data.palette.idFor(presetValue);
+ }
+ }
}
+ // Paper end - Anti-Xray
public synchronized T getAndSet(int x, int y, int z, T state) { // Paper - synchronize
this.acquire();
@@ -172,24 +239,35 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
data.palette.read(buffer);
buffer.readLongArray(data.storage.getRaw());
this.data = data;
+ this.addPresetValues(); // Paper - Anti-Xray - Add preset values (inefficient, but this isn't used by the server)
} finally {
this.release();
}
}
+ // Paper start - Anti-Xray; Add chunk packet info
@Override
- public synchronized void write(FriendlyByteBuf buffer) { // Paper - synchronize
+ @Deprecated @io.papermc.paper.annotation.DoNotUse
+ public void write(FriendlyByteBuf buffer) {
+ this.write(buffer, null, 0);
+ }
+ @Override
+ public synchronized void write(FriendlyByteBuf buffer, @Nullable io.papermc.paper.antixray.ChunkPacketInfo<T> chunkPacketInfo, int chunkSectionIndex) { // Paper - Synchronize
this.acquire();
try {
- this.data.write(buffer);
+ this.data.write(buffer, chunkPacketInfo, chunkSectionIndex);
+ if (chunkPacketInfo != null) {
+ chunkPacketInfo.setPresetValues(chunkSectionIndex, this.presetValues);
+ }
+ // Paper end - Anti-Xray
} finally {
this.release();
}
}
private static <T> DataResult<PalettedContainer<T>> unpack(
- IdMap<T> registry, PalettedContainer.Strategy strategy, PalettedContainerRO.PackedData<T> packedData
+ IdMap<T> registry, PalettedContainer.Strategy strategy, PalettedContainerRO.PackedData<T> packedData, T defaultValue, T @org.jetbrains.annotations.Nullable [] presetValues // Paper - Anti-Xray - Add preset values
) {
List<T> list = packedData.paletteEntries();
int size = strategy.size();
@@ -222,7 +300,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
}
}
- return DataResult.success(new PalettedContainer<>(registry, strategy, configuration, bitStorage, list));
+ return DataResult.success(new PalettedContainer<>(registry, strategy, configuration, bitStorage, list, defaultValue, presetValues)); // Paper - Anti-Xray - Add preset values
}
@Override
@@ -280,12 +358,12 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
@Override
public PalettedContainer<T> copy() {
- return new PalettedContainer<>(this);
+ return new PalettedContainer<>(this, this.presetValues); // Paper - Anti-Xray - Add preset values
}
@Override
public PalettedContainer<T> recreate() {
- return new PalettedContainer<>(this.registry, this.data.palette.valueFor(0), this.strategy);
+ return new PalettedContainer<>(this.registry, this.data.palette.valueFor(0), this.strategy, this.presetValues); // Paper - Anti-Xray - Add preset values
}
@Override
@@ -324,9 +402,16 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
return 1 + this.palette.getSerializedSize() + VarInt.getByteSize(this.storage.getRaw().length) + this.storage.getRaw().length * 8;
}
- public void write(FriendlyByteBuf buffer) {
+ // Paper start - Anti-Xray - Add chunk packet info
+ public void write(FriendlyByteBuf buffer, @Nullable io.papermc.paper.antixray.ChunkPacketInfo<T> chunkPacketInfo, int chunkSectionIndex) {
buffer.writeByte(this.storage.getBits());
this.palette.write(buffer);
+ if (chunkPacketInfo != null) {
+ chunkPacketInfo.setBits(chunkSectionIndex, this.configuration.bits());
+ chunkPacketInfo.setPalette(chunkSectionIndex, this.palette);
+ chunkPacketInfo.setIndex(chunkSectionIndex, buffer.writerIndex() + VarInt.getByteSize(this.storage.getRaw().length));
+ }
+ // Paper end
buffer.writeLongArray(this.storage.getRaw());
}
diff --git a/net/minecraft/world/level/chunk/PalettedContainerRO.java b/net/minecraft/world/level/chunk/PalettedContainerRO.java
index bfbb1a2bb4abbb369a24f2f01439e9ea3e16794b..8d6ed8be4d93f7d4e6ea80c351020d88ee98aa4d 100644
--- a/net/minecraft/world/level/chunk/PalettedContainerRO.java
+++ b/net/minecraft/world/level/chunk/PalettedContainerRO.java
@@ -14,7 +14,10 @@ public interface PalettedContainerRO<T> {
void getAll(Consumer<T> consumer);
- void write(FriendlyByteBuf buffer);
+ // Paper start - Anti-Xray - Add chunk packet info
+ @Deprecated @io.papermc.paper.annotation.DoNotUse void write(FriendlyByteBuf buffer);
+ void write(FriendlyByteBuf buffer, @javax.annotation.Nullable io.papermc.paper.antixray.ChunkPacketInfo<T> chunkPacketInfo, int chunkSectionIndex);
+ // Paper end
int getSerializedSize();
diff --git a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
index 37437a86d74291fab1de9495008aafb15dfadce0..cf6e2053d81f7b0f8c8e58b9c0fad3285ebc047d 100644
--- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
+++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
@@ -94,7 +94,7 @@ public record SerializableChunkData(
, @Nullable net.minecraft.nbt.Tag persistentDataContainer // CraftBukkit - persistentDataContainer
) {
public static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codecRW(
- Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState()
+ Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState(), null // Paper - Anti-Xray
);
private static final Logger LOGGER = LogUtils.getLogger();
private static final String TAG_UPGRADE_DATA = "UpgradeData";
@@ -128,6 +128,7 @@ public record SerializableChunkData(
@Nullable
public static SerializableChunkData parse(LevelHeightAccessor levelHeightAccessor, RegistryAccess registries, CompoundTag tag) {
+ net.minecraft.server.level.ServerLevel serverLevel = (net.minecraft.server.level.ServerLevel) levelHeightAccessor; // Paper - Anti-Xray This is is seemingly only called from ChunkMap, where, we have a server level. We'll fight this later if needed.
if (!tag.contains("Status", 8)) {
return null;
} else {
@@ -212,18 +213,21 @@ public record SerializableChunkData(
Codec<PalettedContainer<Holder<Biome>>> codec = makeBiomeCodecRW(registry); // CraftBukkit - read/write
for (int i2 = 0; i2 < list7.size(); i2++) {
- CompoundTag compound2 = list7.getCompound(i2);
+ CompoundTag compound2 = list7.getCompound(i2); final CompoundTag sectionData = compound2; // Paper - Anti-Xray - OBFHELPER
int _byte = compound2.getByte("Y");
LevelChunkSection levelChunkSection;
if (_byte >= levelHeightAccessor.getMinSectionY() && _byte <= levelHeightAccessor.getMaxSectionY()) {
+ // Paper start - Anti-Xray - Add preset block states
+ BlockState[] presetBlockStates = serverLevel.chunkPacketBlockController.getPresetBlockStates(serverLevel, chunkPos, _byte);
PalettedContainer<BlockState> palettedContainer;
if (compound2.contains("block_states", 10)) {
- palettedContainer = BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, compound2.getCompound("block_states"))
+ Codec<PalettedContainer<BlockState>> blockStateCodec = presetBlockStates == null ? BLOCK_STATE_CODEC : PalettedContainer.codecRW(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState(), presetBlockStates); // Paper - Anti-Xray
+ palettedContainer = blockStateCodec.parse(NbtOps.INSTANCE, sectionData.getCompound("block_states")) // Paper - Anti-Xray
.promotePartial(string -> logErrors(chunkPos, _byte, string))
.getOrThrow(SerializableChunkData.ChunkReadException::new);
} else {
palettedContainer = new PalettedContainer<>(
- Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES
+ Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, presetBlockStates // Paper - Anti-Xray
);
}
@@ -234,7 +238,7 @@ public record SerializableChunkData(
.getOrThrow(SerializableChunkData.ChunkReadException::new);
} else {
palettedContainerRo = new PalettedContainer<>(
- registry.asHolderIdMap(), registry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES
+ registry.asHolderIdMap(), registry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES, null // Paper - Anti-Xray - Add preset biomes
);
}
@@ -414,7 +418,7 @@ public record SerializableChunkData(
// CraftBukkit start - read/write
private static Codec<PalettedContainer<Holder<Biome>>> makeBiomeCodecRW(Registry<Biome> iregistry) {
- return PalettedContainer.codecRW(iregistry.asHolderIdMap(), iregistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, iregistry.getOrThrow(Biomes.PLAINS));
+ return PalettedContainer.codecRW(iregistry.asHolderIdMap(), iregistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, iregistry.getOrThrow(Biomes.PLAINS), null); // Paper - Anti-Xray - Add preset biomes
}
// CraftBukkit end

View file

@ -0,0 +1,354 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Andrew Steinborn <git@steinborn.me>
Date: Mon, 26 Jul 2021 02:15:17 -0400
Subject: [PATCH] Use Velocity compression and cipher natives
diff --git a/net/minecraft/network/CipherDecoder.java b/net/minecraft/network/CipherDecoder.java
index 429325ffb7db2b85ed271ddf3da64c6fdc593673..d764552aea825af7bbf0f47c9d88af527d9dbe62 100644
--- a/net/minecraft/network/CipherDecoder.java
+++ b/net/minecraft/network/CipherDecoder.java
@@ -7,14 +7,30 @@ import java.util.List;
import javax.crypto.Cipher;
public class CipherDecoder extends MessageToMessageDecoder<ByteBuf> {
- private final CipherBase cipher;
+ private final com.velocitypowered.natives.encryption.VelocityCipher cipher; // Paper - Use Velocity cipher
- public CipherDecoder(Cipher cipher) {
- this.cipher = new CipherBase(cipher);
+ public CipherDecoder(com.velocitypowered.natives.encryption.VelocityCipher cipher) { // Paper - Use Velocity cipher
+ this.cipher = cipher; // Paper - Use Velocity cipher
}
@Override
protected void decode(ChannelHandlerContext context, ByteBuf in, List<Object> out) throws Exception {
- out.add(this.cipher.decipher(context, in));
+ // Paper start - Use Velocity cipher
+ ByteBuf compatible = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(context.alloc(), this.cipher, in);
+ try {
+ this.cipher.process(compatible);
+ out.add(compatible);
+ } catch (Exception e) {
+ compatible.release(); // compatible will never be used if we throw an exception
+ throw e;
+ }
+ // Paper end - Use Velocity cipher
}
+
+ // Paper start - Use Velocity cipher
+ @Override
+ public void handlerRemoved(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 992b9c7aed57ce29cdd2b4f66737d39db214f0cf..b927c85923147d2b346e892a3e4deee48b3d073e 100644
--- a/net/minecraft/network/CipherEncoder.java
+++ b/net/minecraft/network/CipherEncoder.java
@@ -5,15 +5,31 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import javax.crypto.Cipher;
-public class CipherEncoder extends MessageToByteEncoder<ByteBuf> {
- private final CipherBase cipher;
+public class CipherEncoder extends io.netty.handler.codec.MessageToMessageEncoder<ByteBuf> { // Paper - Use Velocity cipher; change superclass
+ private final com.velocitypowered.natives.encryption.VelocityCipher cipher; // Paper - Use Velocity cipher
- public CipherEncoder(Cipher cipher) {
- this.cipher = new CipherBase(cipher);
+ public CipherEncoder(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 context, ByteBuf message, ByteBuf out) throws Exception {
- this.cipher.encipher(message, out);
+ protected void encode(ChannelHandlerContext context, ByteBuf message, java.util.List<Object> list) throws Exception {
+ ByteBuf compatible = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(context.alloc(), this.cipher, message);
+ try {
+ this.cipher.process(compatible);
+ list.add(compatible);
+ } catch (Exception e) {
+ compatible.release(); // compatible will never be used if we throw an exception
+ throw e;
+ }
+ // Paper end - Use Velocity cipher
}
+
+ // Paper start - Use Velocity cipher
+ @Override
+ public void handlerRemoved(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 fcf0e557fbcbd5f306625096d859578fe8511734..2c7f935fcecb24a4394fdde523219a5b5984a673 100644
--- a/net/minecraft/network/CompressionDecoder.java
+++ b/net/minecraft/network/CompressionDecoder.java
@@ -12,14 +12,22 @@ import java.util.zip.Inflater;
public class CompressionDecoder extends ByteToMessageDecoder {
public static final int MAXIMUM_COMPRESSED_LENGTH = 2097152;
public static final int MAXIMUM_UNCOMPRESSED_LENGTH = 8388608;
+ private com.velocitypowered.natives.compression.VelocityCompressor compressor; // Paper - Use Velocity cipher
private Inflater inflater;
private int threshold;
private boolean validateDecompressed;
+ // Paper start - Use Velocity cipher
+ @io.papermc.paper.annotation.DoNotUse
public CompressionDecoder(int threshold, boolean validateDecompressed) {
+ this(null, threshold, validateDecompressed);
+ }
+ public CompressionDecoder(com.velocitypowered.natives.compression.VelocityCompressor compressor, int threshold, boolean validateDecompressed) {
this.threshold = threshold;
this.validateDecompressed = validateDecompressed;
- this.inflater = new Inflater();
+ this.inflater = compressor == null ? new Inflater() : null;
+ this.compressor = compressor;
+ // Paper end - Use Velocity cipher
}
@Override
@@ -39,14 +47,42 @@ public class CompressionDecoder extends ByteToMessageDecoder {
}
}
+ if (inflater != null) { // Paper - Use Velocity cipher; fallback to vanilla inflater
this.setupInflaterInput(in);
ByteBuf byteBuf = this.inflate(context, i);
this.inflater.reset();
out.add(byteBuf);
+ 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(context.alloc(), this.compressor, in);
+ ByteBuf uncompressed = com.velocitypowered.natives.util.MoreByteBufUtils.preferredBuffer(context.alloc(), this.compressor, claimedUncompressedSize);
+ try {
+ this.compressor.inflate(compatibleIn, uncompressed, claimedUncompressedSize);
+ out.add(uncompressed);
+ in.clear();
+ } catch (Exception e) {
+ uncompressed.release();
+ throw e;
+ } finally {
+ compatibleIn.release();
+ }
+ // Paper end - Use Velocity cipher
}
}
}
+ // Paper start - Use Velocity cipher
+ @Override
+ public void handlerRemoved0(ChannelHandlerContext ctx) {
+ if (this.compressor != null) {
+ this.compressor.close();
+ }
+ }
+ // Paper end - Use Velocity cipher
+
private void setupInflaterInput(ByteBuf buffer) {
ByteBuffer byteBuffer;
if (buffer.nioBufferCount() > 0) {
@@ -81,7 +117,13 @@ public class CompressionDecoder extends ByteToMessageDecoder {
}
}
- public void setThreshold(int threshold, boolean validateDecompressed) {
+ // Paper start - Use Velocity cipher
+ public void setThreshold(com.velocitypowered.natives.compression.VelocityCompressor compressor, int threshold, 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;
+ }
+ // Paper end - Use Velocity cipher
this.threshold = threshold;
this.validateDecompressed = validateDecompressed;
}
diff --git a/net/minecraft/network/CompressionEncoder.java b/net/minecraft/network/CompressionEncoder.java
index bc674b08a41d5529fe06c6d3f077051cf4138f73..ea8a894158c44c2e7943dea43ecd8e1f0075b18f 100644
--- a/net/minecraft/network/CompressionEncoder.java
+++ b/net/minecraft/network/CompressionEncoder.java
@@ -6,17 +6,31 @@ import io.netty.handler.codec.MessageToByteEncoder;
import java.util.zip.Deflater;
public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
- private final byte[] encodeBuf = new byte[8192];
+ @javax.annotation.Nullable private final byte[] encodeBuf; // Paper - Use Velocity cipher
+ @javax.annotation.Nullable // Paper - Use Velocity cipher
private final Deflater deflater;
+ @javax.annotation.Nullable private final com.velocitypowered.natives.compression.VelocityCompressor compressor; // Paper - Use Velocity cipher
private int threshold;
+ // Paper start - Use Velocity cipher
public CompressionEncoder(int threshold) {
+ this(null, threshold);
+ }
+ public CompressionEncoder(@javax.annotation.Nullable com.velocitypowered.natives.compression.VelocityCompressor compressor, int threshold) {
this.threshold = threshold;
- this.deflater = new Deflater();
+ if (compressor == null) {
+ this.encodeBuf = new byte[8192];
+ this.deflater = new Deflater();
+ } else {
+ this.encodeBuf = null;
+ this.deflater = null;
+ }
+ this.compressor = compressor;
+ // Paper end - Use Velocity cipher
}
@Override
- protected void encode(ChannelHandlerContext context, ByteBuf encodingByteBuf, ByteBuf byteBuf) {
+ protected void encode(ChannelHandlerContext context, 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)");
@@ -25,6 +39,7 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
VarInt.write(byteBuf, 0);
byteBuf.writeBytes(encodingByteBuf);
} else {
+ if (this.deflater != null) { // Paper - Use Velocity cipher
byte[] bytes = new byte[i];
encodingByteBuf.readBytes(bytes);
VarInt.write(byteBuf, bytes.length);
@@ -37,6 +52,17 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
}
this.deflater.reset();
+ // Paper start - Use Velocity cipher
+ return;
+ }
+
+ VarInt.write(byteBuf, i);
+ final ByteBuf compatibleIn = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(context.alloc(), this.compressor, encodingByteBuf);
+ try {
+ this.compressor.deflate(compatibleIn, byteBuf);
+ } finally {
+ compatibleIn.release();
+ }
}
}
}
@@ -48,4 +74,31 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
public void setThreshold(int threshold) {
this.threshold = threshold;
}
+
+ // Paper start - Use Velocity cipher
+ @Override
+ protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, ByteBuf msg, boolean preferDirect) throws Exception {
+ if (this.compressor != null) {
+ // We allocate bytes to be compressed plus 1 byte. This covers two cases:
+ //
+ // - Compression
+ // According to https://github.com/ebiggers/libdeflate/blob/master/libdeflate.h#L103,
+ // if the data compresses well (and we do not have some pathological case) then the maximum
+ // size the compressed size will ever be is the input size minus one.
+ // - Uncompressed
+ // This is fairly obvious - we will then have one more than the uncompressed size.
+ final int initialBufferSize = msg.readableBytes() + 1;
+ return com.velocitypowered.natives.util.MoreByteBufUtils.preferredBuffer(ctx.alloc(), this.compressor, initialBufferSize);
+ }
+
+ return super.allocateBuffer(ctx, msg, preferDirect);
+ }
+
+ @Override
+ public void handlerRemoved(ChannelHandlerContext ctx) {
+ if (this.compressor != null) {
+ this.compressor.close();
+ }
+ }
+ // Paper end - Use Velocity cipher
}
diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java
index ad8f8428b75e37097487cdfbd0db2421ee4cbe37..208efae06c7c44f220d4192219a86ec55c98a2fe 100644
--- a/net/minecraft/network/Connection.java
+++ b/net/minecraft/network/Connection.java
@@ -772,11 +772,22 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
return connection;
}
- public void setEncryptionKey(Cipher decryptingCipher, Cipher encryptingCipher) {
- this.encrypted = true;
- this.channel.pipeline().addBefore("splitter", "decrypt", new CipherDecoder(decryptingCipher));
- this.channel.pipeline().addBefore("prepender", "encrypt", new CipherEncoder(encryptingCipher));
+ // Paper start - Use Velocity cipher
+ public void setEncryptionKey(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);
+
+ 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) {
+ throw new net.minecraft.util.CryptException(e);
+ }
+ }
}
+ // Paper end - Use Velocity cipher
public boolean isEncrypted() {
return this.encrypted;
@@ -815,16 +826,17 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
// Paper end - add proper async disconnect
public void setupCompression(int threshold, 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) {
- 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
}
if (this.channel.pipeline().get("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().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 7de11ba404f0b60e7f7b7c16954811a343688219..bd07e6a5aa1883786d789ea71711a0c0c0a95c26 100644
--- a/net/minecraft/server/network/ServerConnectionListener.java
+++ b/net/minecraft/server/network/ServerConnectionListener.java
@@ -108,6 +108,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.
+ // Paper start - Use Velocity cipher
+ 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(
diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
index bb28453d230921d662ed69c8dd48021f428ef060..6689aeacf50d1498e8d23cce696fb4fecbd1cf39 100644
--- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
@@ -276,11 +276,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);
this.state = ServerLoginPacketListenerImpl.State.AUTHENTICATING;
- this.connection.setEncryptionKey(cipher, cipher1);
+ this.connection.setEncryptionKey(secretKey); // Paper - Use Velocity cipher
} catch (CryptException var7) {
throw new IllegalStateException("Protocol error", var7);
}

View file

@ -13,44 +13,44 @@ If that serting is not enabled, collisions will be ignored for players, since
movement will load only the chunk the player enters anyways and avoids loading
massive amounts of surrounding chunks due to large AABB lookups.
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -0,0 +0,0 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 334859c5ff7023c730513301cc11c9837b2c7823..45f69a914d5a0565196c4105d61541047301470f 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -218,6 +218,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
// Paper end - Share random for entities to make them more random
public org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason; // Paper - Entity#getEntitySpawnReason
+ public boolean collisionLoadChunks = false; // Paper
private CraftEntity bukkitEntity;
private org.bukkit.craftbukkit.entity.CraftEntity bukkitEntity;
public CraftEntity getBukkitEntity() {
diff --git a/src/main/java/net/minecraft/world/level/BlockCollisions.java b/src/main/java/net/minecraft/world/level/BlockCollisions.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/BlockCollisions.java
+++ b/src/main/java/net/minecraft/world/level/BlockCollisions.java
@@ -0,0 +0,0 @@ public class BlockCollisions<T> extends AbstractIterator<T> {
public org.bukkit.craftbukkit.entity.CraftEntity getBukkitEntity() {
diff --git a/net/minecraft/world/level/BlockCollisions.java b/net/minecraft/world/level/BlockCollisions.java
index fd2c338db43aad070cc32c24891b40599c544ac9..2861ea4b699d403b1245f8be5a62503d366ded65 100644
--- a/net/minecraft/world/level/BlockCollisions.java
+++ b/net/minecraft/world/level/BlockCollisions.java
@@ -80,16 +80,37 @@ public class BlockCollisions<T> extends AbstractIterator<T> {
@Override
protected T computeNext() {
while (this.cursor.advance()) {
- int i = this.cursor.nextX();
- int j = this.cursor.nextY();
- int k = this.cursor.nextZ();
- int i1 = this.cursor.nextY();
- int i2 = this.cursor.nextZ();
+ int i = this.cursor.nextX(); final int x = i; // Paper - OBFHELPER
+ int j = this.cursor.nextY(); final int y = j; // Paper - OBFHELPER
+ int k = this.cursor.nextZ(); final int z = k; // Paper - OBFHELPER
int l = this.cursor.getNextType();
if (l != 3) {
- BlockGetter blockGetter = this.getChunk(i, k);
- if (blockGetter != null) {
- this.pos.set(i, j, k);
- BlockState blockState = blockGetter.getBlockState(this.pos);
- if ((!this.onlySuffocatingBlocks || blockState.isSuffocating(blockGetter, this.pos))
+ int i1 = this.cursor.nextY(); final int y = i1; // Paper - OBFHELPER
+ int i2 = this.cursor.nextZ(); final int z = i2; // Paper - OBFHELPER
int nextType = this.cursor.getNextType();
if (nextType != 3) {
- BlockGetter chunk = this.getChunk(i, i2);
- if (chunk != null) {
- this.pos.set(i, i1, i2);
- BlockState blockState = chunk.getBlockState(this.pos);
- if ((!this.onlySuffocatingBlocks || blockState.isSuffocating(chunk, this.pos))
+ // Paper start - ensure we don't load chunks
+ // BlockGetter blockGetter = this.getChunk(i, k);
+ if (true) {
+ final @Nullable Entity source = this.context instanceof net.minecraft.world.phys.shapes.EntityCollisionContext entityContext ? entityContext.getEntity() : null;
+ boolean far = source != null && io.papermc.paper.util.MCUtil.distanceSq(source.getX(), y, source.getZ(), x, y, z) > 14;
+ @Nullable final Entity source = this.context instanceof net.minecraft.world.phys.shapes.EntityCollisionContext entityContext ? entityContext.getEntity() : null;
+ final boolean far = source != null && io.papermc.paper.util.MCUtil.distanceSq(source.getX(), y, source.getZ(), x, y, z) > 14;
+ this.pos.set(x, y, z);
+ BlockState blockState;
+ if (this.collisionGetter instanceof net.minecraft.server.level.WorldGenRegion) {
@ -72,25 +72,24 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ }
+ if (true // onlySuffocatingBlocks is only true on the client, so we don't care about it here
+ // Paper end - ensure we don't load chunks
&& (l != 1 || blockState.hasLargeCollisionShape())
&& (l != 2 || blockState.is(Blocks.MOVING_PISTON))) {
VoxelShape voxelShape = this.context.getCollisionShape(blockState, this.collisionGetter, this.pos);
diff --git a/src/main/java/net/minecraft/world/level/CollisionGetter.java b/src/main/java/net/minecraft/world/level/CollisionGetter.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/CollisionGetter.java
+++ b/src/main/java/net/minecraft/world/level/CollisionGetter.java
@@ -0,0 +0,0 @@ public interface CollisionGetter extends BlockGetter {
&& (nextType != 1 || blockState.hasLargeCollisionShape())
&& (nextType != 2 || blockState.is(Blocks.MOVING_PISTON))) {
VoxelShape collisionShape = this.context.getCollisionShape(blockState, this.collisionGetter, this.pos);
diff --git a/net/minecraft/world/level/CollisionGetter.java b/net/minecraft/world/level/CollisionGetter.java
index cb54c3aadd8f3c719d3f7ef1fda4aa517919b7c3..844f76a38884e823a558fe59c421ffd4711f80b4 100644
--- a/net/minecraft/world/level/CollisionGetter.java
+++ b/net/minecraft/world/level/CollisionGetter.java
@@ -50,11 +50,13 @@ public interface CollisionGetter extends BlockGetter {
}
default boolean noCollision(@Nullable Entity entity, AABB box, boolean checkFluid) {
- for (VoxelShape voxelShape : checkFluid ? this.getBlockAndLiquidCollisions(entity, box) : this.getBlockCollisions(entity, box)) {
default boolean noCollision(@Nullable Entity entity, AABB collisionBox, boolean checkLiquid) {
+ try { if (entity != null) entity.collisionLoadChunks = true; // Paper
+ for (VoxelShape voxelShape : checkFluid ? this.getBlockAndLiquidCollisions(entity, box) : this.getBlockCollisions(entity, box)) {
for (VoxelShape voxelShape : checkLiquid ? this.getBlockAndLiquidCollisions(entity, collisionBox) : this.getBlockCollisions(entity, collisionBox)) {
if (!voxelShape.isEmpty()) {
return false;
}
}
+ } finally { if (entity != null) entity.collisionLoadChunks = false; } // Paper
if (!this.getEntityCollisions(entity, box).isEmpty()) {
if (!this.getEntityCollisions(entity, collisionBox).isEmpty()) {
return false;

View file

@ -0,0 +1,168 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Mon, 6 Apr 2020 17:53:29 -0700
Subject: [PATCH] Optimize GoalSelector Goal.Flag Set operations
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 d4f8387254a65b25fc808932a069298d0f8da091..5f5bf0e710ecff09a571091e5a923332be70cb74 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;
import net.minecraft.world.level.Level;
public abstract class Goal {
- private final EnumSet<Goal.Flag> flags = EnumSet.noneOf(Goal.Flag.class);
+ private final ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<net.minecraft.world.entity.ai.goal.Goal.Flag> goalTypes = new ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from GoalSelector
+
+ // Paper start - remove streams from GoalSelector; make sure types are not empty
+ protected Goal() {
+ if (this.goalTypes.size() == 0) {
+ this.goalTypes.addUnchecked(Flag.UNKNOWN_BEHAVIOR);
+ }
+ }
+ // Paper end - remove streams from GoalSelector
public abstract boolean canUse();
@@ -33,8 +41,13 @@ public abstract class Goal {
}
public void setFlags(EnumSet<Goal.Flag> flagSet) {
- this.flags.clear();
- this.flags.addAll(flagSet);
+ // Paper start - remove streams from GoalSelector
+ this.goalTypes.clear();
+ this.goalTypes.addAllUnchecked(flagSet);
+ if (this.goalTypes.size() == 0) {
+ this.goalTypes.addUnchecked(Flag.UNKNOWN_BEHAVIOR);
+ }
+ // Paper end - remove streams from GoalSelector
}
@Override
@@ -42,18 +55,20 @@ public abstract class Goal {
return this.getClass().getSimpleName();
}
- public EnumSet<Goal.Flag> getFlags() {
- return this.flags;
+ // Paper start - remove streams from GoalSelector
+ public ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<Goal.Flag> getFlags() {
+ return this.goalTypes;
+ // Paper end - remove streams from GoalSelector
}
// Paper start - Mob Goal API
public boolean hasFlag(final Goal.Flag flag) {
- return this.flags.contains(flag);
+ return this.goalTypes.hasElement(flag);
}
public void addFlag(final Goal.Flag flag) {
- this.flags.add(flag);
+ this.goalTypes.addUnchecked(flag);
}
// 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 eeba224bd575451ba6023df65ef9d9b97f7f1c71..a927c2790c8ab9ccaa7161b970e10b0b44817dd8 100644
--- a/net/minecraft/world/entity/ai/goal/GoalSelector.java
+++ b/net/minecraft/world/entity/ai/goal/GoalSelector.java
@@ -24,7 +24,8 @@ public class GoalSelector {
};
private final Map<Goal.Flag, WrappedGoal> lockedFlags = new EnumMap<>(Goal.Flag.class);
private final Set<WrappedGoal> availableGoals = new ObjectLinkedOpenHashSet<>();
- private final EnumSet<Goal.Flag> disabledFlags = EnumSet.noneOf(Goal.Flag.class);
+ private static final Goal.Flag[] GOAL_FLAG_VALUES = Goal.Flag.values(); // Paper - remove streams from GoalSelector
+ private final ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<net.minecraft.world.entity.ai.goal.Goal.Flag> 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) {
@@ -62,18 +63,18 @@ public class GoalSelector {
this.availableGoals.removeIf(wrappedGoal1 -> wrappedGoal1.getGoal() == goal);
}
- private static boolean goalContainsAnyFlags(WrappedGoal goal, EnumSet<Goal.Flag> flag) {
- for (Goal.Flag flag1 : goal.getFlags()) {
- if (flag.contains(flag1)) {
- return true;
- }
- }
-
- return false;
+ // Paper start - Perf: optimize goal types
+ private static boolean goalContainsAnyFlags(WrappedGoal goal, ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<Goal.Flag> flags) {
+ return goal.getFlags().hasCommonElements(flags);
}
private static boolean goalCanBeReplacedForAllFlags(WrappedGoal goal, Map<Goal.Flag, WrappedGoal> flag) {
- for (Goal.Flag flag1 : 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)];
+ flagIterator ^= ca.spottedleaf.concurrentutil.util.IntegerUtil.getTrailingBit(flagIterator);
+ // Paper end - Perf: optimize goal types
if (!flag.getOrDefault(flag1, NO_GOAL).canBeReplacedBy(goal)) {
return false;
}
@@ -87,7 +88,7 @@ public class GoalSelector {
profilerFiller.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();
}
}
@@ -97,11 +98,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()) {
+ // 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();
+ 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);
@@ -133,11 +137,11 @@ public class GoalSelector {
}
public void disableControlFlag(Goal.Flag flag) {
- this.disabledFlags.add(flag);
+ this.goalTypes.addUnchecked(flag); // Paper - remove streams from GoalSelector
}
public void enableControlFlag(Goal.Flag flag) {
- this.disabledFlags.remove(flag);
+ this.goalTypes.removeUnchecked(flag); // Paper - remove streams from GoalSelector
}
public void setControlFlag(Goal.Flag flag, 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
--- 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 {
}
@Override
- public EnumSet<Goal.Flag> getFlags() {
+ public ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<Goal.Flag> getFlags() { // Paper - remove streams from GoalSelector
return this.goal.getFlags();
}

View file

@ -0,0 +1,122 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
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 e1b4c4b53844b0755e0640a05e8782fd9a7700a2..e759221fb54aa510d2d8bbba47e1d794367aec6d 100644
--- a/net/minecraft/world/phys/shapes/Shapes.java
+++ b/net/minecraft/world/phys/shapes/Shapes.java
@@ -279,9 +279,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) {
@@ -289,14 +302,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
}
}

View file

@ -0,0 +1,152 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Sun, 22 Aug 2021 15:21:57 -0700
Subject: [PATCH] Fix entity type tags suggestions in selectors
This would really be better as a client fix because just to fix it
all EntityArguments have been told to ask the server for completions
when if this was fixed on the client, that wouldn't be needed.
Mojira Issue: https://bugs.mojang.com/browse/MC-235045
diff --git a/net/minecraft/commands/CommandSourceStack.java b/net/minecraft/commands/CommandSourceStack.java
index 704a63890a06d793f8ac3452838917e7c7335232..75262c8c9eaecb4a88a94f4076d67119c67a97da 100644
--- a/net/minecraft/commands/CommandSourceStack.java
+++ b/net/minecraft/commands/CommandSourceStack.java
@@ -652,4 +652,20 @@ public class CommandSourceStack implements ExecutionCommandSource<CommandSourceS
return this.source.getBukkitSender(this);
}
// CraftBukkit end
+ // Paper start - tell clients to ask server for suggestions for EntityArguments
+ @Override
+ public Collection<String> getSelectedEntities() {
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().commands.fixTargetSelectorTagCompletion && this.source instanceof ServerPlayer player) {
+ final Entity cameraEntity = player.getCamera();
+ final double pickDistance = player.entityInteractionRange();
+ final Vec3 min = cameraEntity.getEyePosition(1.0F);
+ final Vec3 viewVector = cameraEntity.getViewVector(1.0F);
+ final Vec3 max = min.add(viewVector.x * pickDistance, viewVector.y * pickDistance, viewVector.z * pickDistance);
+ final net.minecraft.world.phys.AABB aabb = cameraEntity.getBoundingBox().expandTowards(viewVector.scale(pickDistance)).inflate(1.0D, 1.0D, 1.0D);
+ final net.minecraft.world.phys.EntityHitResult hitResult = net.minecraft.world.entity.projectile.ProjectileUtil.getEntityHitResult(cameraEntity, min, max, aabb, (e) -> !e.isSpectator() && e.isPickable(), pickDistance * pickDistance);
+ return hitResult != null ? java.util.Collections.singletonList(hitResult.getEntity().getStringUUID()) : SharedSuggestionProvider.super.getSelectedEntities();
+ }
+ return SharedSuggestionProvider.super.getSelectedEntities();
+ }
+ // Paper end - tell clients to ask server for suggestions for EntityArguments
}
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
index fa8c5ba4e0efd0c36613aaa8eaafba0cb70ceb87..19ccf3abf14c67f72a1ca065e4a304f50e645ef4 100644
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -509,6 +509,7 @@ public class Commands {
Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> commandNodeToSuggestionNode
) {
commandNodeToSuggestionNode.keySet().removeIf((node) -> !org.spigotmc.SpigotConfig.sendNamespaced && node.getName().contains(":")); // Paper - Remove namedspaced from result nodes to prevent redirect trimming ~ see comment below
+ boolean registeredAskServerSuggestionsForTree = false; // Paper - tell clients to ask server for suggestions for EntityArguments
for (CommandNode<CommandSourceStack> commandNode : children) { // Paper - Perf: Async command map building; pass copy of children
// Paper start - Brigadier API
if (commandNode.clientNode != null) {
@@ -572,6 +573,12 @@ public class Commands {
RequiredArgumentBuilder<SharedSuggestionProvider, ?> requiredArgumentBuilder = (RequiredArgumentBuilder<SharedSuggestionProvider, ?>)argumentBuilder;
if (requiredArgumentBuilder.getSuggestionsProvider() != null) {
requiredArgumentBuilder.suggests(SuggestionProviders.safelySwap(requiredArgumentBuilder.getSuggestionsProvider()));
+ // Paper start - tell clients to ask server for suggestions for EntityArguments
+ registeredAskServerSuggestionsForTree = requiredArgumentBuilder.getSuggestionsProvider() == net.minecraft.commands.synchronization.SuggestionProviders.ASK_SERVER;
+ } else if (io.papermc.paper.configuration.GlobalConfiguration.get().commands.fixTargetSelectorTagCompletion && !registeredAskServerSuggestionsForTree && requiredArgumentBuilder.getType() instanceof net.minecraft.commands.arguments.EntityArgument) {
+ requiredArgumentBuilder.suggests(requiredArgumentBuilder.getType()::listSuggestions);
+ registeredAskServerSuggestionsForTree = true; // You can only
+ // Paper end - tell clients to ask server for suggestions for EntityArguments
}
}
diff --git a/net/minecraft/commands/arguments/EntityArgument.java b/net/minecraft/commands/arguments/EntityArgument.java
index 0a01df6ebd14afe79bc76364cb1df5e0c5c08074..7a06ad9940d25e163178370bfa9ccc3bbd3d1189 100644
--- a/net/minecraft/commands/arguments/EntityArgument.java
+++ b/net/minecraft/commands/arguments/EntityArgument.java
@@ -138,7 +138,7 @@ public class EntityArgument implements ArgumentType<EntitySelector> {
final boolean permission = sharedSuggestionProvider instanceof CommandSourceStack stack
? stack.bypassSelectorPermissions || stack.hasPermission(2, "minecraft.command.selector")
: sharedSuggestionProvider.hasPermission(2);
- EntitySelectorParser entitySelectorParser = new EntitySelectorParser(stringReader, permission);
+ EntitySelectorParser entitySelectorParser = new EntitySelectorParser(stringReader, permission, true); // Paper - tell clients to ask server for suggestions for EntityArguments
// Paper end - Fix EntityArgument permissions
try {
@@ -149,7 +149,19 @@ public class EntityArgument implements ArgumentType<EntitySelector> {
return entitySelectorParser.fillSuggestions(
builder,
offsetBuilder -> {
- Collection<String> onlinePlayerNames = sharedSuggestionProvider.getOnlinePlayerNames();
+ // Paper start - tell clients to ask server for suggestions for EntityArguments
+ final Collection<String> onlinePlayerNames;
+ if (sharedSuggestionProvider instanceof CommandSourceStack commandSourceStack && commandSourceStack.getEntity() instanceof ServerPlayer sourcePlayer) {
+ onlinePlayerNames = new java.util.ArrayList<>();
+ for (final ServerPlayer player : commandSourceStack.getServer().getPlayerList().getPlayers()) {
+ if (sourcePlayer.getBukkitEntity().canSee(player.getBukkitEntity())) {
+ onlinePlayerNames.add(player.getGameProfile().getName());
+ }
+ }
+ } else {
+ onlinePlayerNames = sharedSuggestionProvider.getOnlinePlayerNames();
+ }
+ // Paper end - tell clients to ask server for suggestions for EntityArguments
Iterable<String> iterable = (Iterable<String>)(this.playersOnly
? onlinePlayerNames
: Iterables.concat(onlinePlayerNames, sharedSuggestionProvider.getSelectedEntities()));
diff --git a/net/minecraft/commands/arguments/selector/EntitySelectorParser.java b/net/minecraft/commands/arguments/selector/EntitySelectorParser.java
index a6f232747df631f6afe440606bea94c588f1a0dd..fb42630741674c6cbd20b7d45d78dea1dc73a78f 100644
--- a/net/minecraft/commands/arguments/selector/EntitySelectorParser.java
+++ b/net/minecraft/commands/arguments/selector/EntitySelectorParser.java
@@ -115,8 +115,15 @@ public class EntitySelectorParser {
private boolean hasScores;
private boolean hasAdvancements;
private boolean usesSelectors;
+ public boolean parsingEntityArgumentSuggestions; // Paper - tell clients to ask server for suggestions for EntityArguments
public EntitySelectorParser(StringReader reader, boolean allowSelectors) {
+ // Paper start - tell clients to ask server for suggestions for EntityArguments
+ this(reader, allowSelectors, false);
+ }
+ public EntitySelectorParser(StringReader reader, boolean allowSelectors, boolean parsingEntityArgumentSuggestions) {
+ this.parsingEntityArgumentSuggestions = parsingEntityArgumentSuggestions;
+ // Paper end - tell clients to ask server for suggestions for EntityArguments
this.reader = reader;
this.allowSelectors = allowSelectors;
}
diff --git a/net/minecraft/commands/arguments/selector/options/EntitySelectorOptions.java b/net/minecraft/commands/arguments/selector/options/EntitySelectorOptions.java
index f6b58139aace70436034f0a16370236d975cb4ae..ee9949c41d38817b21b6f4fd728059a46fddf135 100644
--- a/net/minecraft/commands/arguments/selector/options/EntitySelectorOptions.java
+++ b/net/minecraft/commands/arguments/selector/options/EntitySelectorOptions.java
@@ -76,6 +76,19 @@ public class EntitySelectorOptions {
public static final DynamicCommandExceptionType ERROR_ENTITY_TYPE_INVALID = new DynamicCommandExceptionType(
type -> Component.translatableEscape("argument.entity.options.type.invalid", type)
);
+ // Paper start - tell clients to ask server for suggestions for EntityArguments
+ public static final DynamicCommandExceptionType ERROR_ENTITY_TAG_INVALID = new DynamicCommandExceptionType((object) -> {
+ return io.papermc.paper.adventure.PaperAdventure
+ .asVanilla(net.kyori.adventure.text.Component
+ .text("Invalid or unknown entity type tag '" + object + "'")
+ .hoverEvent(net.kyori.adventure.text.event.HoverEvent
+ .showText(net.kyori.adventure.text.Component
+ .text("You can disable this error in 'paper.yml'")
+ )
+ )
+ );
+ });
+ // Paper end - tell clients to ask server for suggestions for EntityArguments
private static void register(String id, EntitySelectorOptions.Modifier handler, Predicate<EntitySelectorParser> predicate, Component tooltip) {
OPTIONS.put(id, new EntitySelectorOptions.Option(handler, predicate, tooltip));
@@ -299,6 +312,12 @@ public class EntitySelectorOptions {
if (parser.isTag()) {
TagKey<EntityType<?>> tagKey = TagKey.create(Registries.ENTITY_TYPE, ResourceLocation.read(parser.getReader()));
+ // Paper start - tell clients to ask server for suggestions for EntityArguments; throw error if invalid entity tag (only on suggestions to keep cmd success behavior)
+ if (parser.parsingEntityArgumentSuggestions && io.papermc.paper.configuration.GlobalConfiguration.get().commands.fixTargetSelectorTagCompletion && net.minecraft.core.registries.BuiltInRegistries.ENTITY_TYPE.get(tagKey).isEmpty()) {
+ parser.getReader().setCursor(cursor);
+ throw ERROR_ENTITY_TAG_INVALID.createWithContext(parser.getReader(), tagKey);
+ }
+ // Paper end - tell clients to ask server for suggestions for EntityArguments
parser.addPredicate(entity -> entity.getType().is(tagKey) != shouldInvertValue);
} else {
ResourceLocation resourceLocation = ResourceLocation.read(parser.getReader());

View file

@ -8,17 +8,17 @@ creating too large of a packet to sed.
Co-authored-by: Spottedleaf <Spottedleaf@users.noreply.github.com>
diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
@@ -0,0 +0,0 @@ public class ClientboundLevelChunkPacketData {
diff --git a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
index 3aea76690bc3e35758d3bf274777130af17d8a0f..9e321ef1c3d5803519b243685f4ee598dc0cf640 100644
--- a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
+++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
@@ -27,6 +27,14 @@ public class ClientboundLevelChunkPacketData {
private final CompoundTag heightmaps;
private final byte[] buffer;
private final List<ClientboundLevelChunkPacketData.BlockEntityInfo> blockEntitiesData;
+ // Paper start - Handle oversized block entities in chunks
+ private final java.util.List<net.minecraft.network.protocol.Packet<?>> extraPackets = new java.util.ArrayList<>();
+ private static final int TE_LIMIT = Integer.getInteger("Paper.excessiveTELimit", 750);
+ private static final int BLOCK_ENTITY_LIMIT = Integer.getInteger("Paper.excessiveTELimit", 750);
+
+ public List<net.minecraft.network.protocol.Packet<?>> getExtraPackets() {
+ return this.extraPackets;
@ -26,31 +26,31 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper end - Handle oversized block entities in chunks
// Paper start - Anti-Xray - Add chunk packet info
@Deprecated @io.papermc.paper.annotation.DoNotUse public ClientboundLevelChunkPacketData(LevelChunk chunk) { this(chunk, null); }
@@ -0,0 +0,0 @@ public class ClientboundLevelChunkPacketData {
extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), chunk, chunkPacketInfo);
// Paper end
@Deprecated @io.papermc.paper.annotation.DoNotUse
@@ -50,8 +58,18 @@ public class ClientboundLevelChunkPacketData {
}
extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), levelChunk, chunkPacketInfo);
this.blockEntitiesData = Lists.newArrayList();
+ int totalTileEntities = 0; // Paper - Handle oversized block entities in chunks
for (Entry<BlockPos, BlockEntity> entry2 : chunk.getBlockEntities().entrySet()) {
for (Entry<BlockPos, BlockEntity> entryx : levelChunk.getBlockEntities().entrySet()) {
+ // Paper start - Handle oversized block entities in chunks
+ if (++totalTileEntities > TE_LIMIT) {
+ var packet = entry2.getValue().getUpdatePacket();
+ if (++totalTileEntities > BLOCK_ENTITY_LIMIT) {
+ net.minecraft.network.protocol.Packet<ClientGamePacketListener> packet = entryx.getValue().getUpdatePacket();
+ if (packet != null) {
+ this.extraPackets.add(packet);
+ continue;
+ }
+ }
+ // Paper end - Handle oversized block entities in chunks
this.blockEntitiesData.add(ClientboundLevelChunkPacketData.BlockEntityInfo.create(entry2.getValue()));
this.blockEntitiesData.add(ClientboundLevelChunkPacketData.BlockEntityInfo.create(entryx.getValue()));
}
}
diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
@@ -0,0 +0,0 @@ public class ClientboundLevelChunkWithLightPacket implements Packet<ClientGamePa
diff --git a/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java b/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
index 5699bc15eba92e22433a20cb8326b59f2ebd3036..8578d1f78ddd1bb75f3230f04bfaa35af9f5f822 100644
--- a/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
+++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
@@ -84,4 +84,11 @@ public class ClientboundLevelChunkWithLightPacket implements Packet<ClientGamePa
public ClientboundLightUpdatePacketData getLightData() {
return this.lightData;
}

View file

@ -0,0 +1,78 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: lukas81298 <lukas81298@gommehd.net>
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 11937aa74efe08bdbd66a619c7a825f91d971afd..722f2b9a24679e0fc67aae2cd27051f96f962efe 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 levelReader, BlockPos pos) {
+ // Paper start - Perf: optimize dirt and snow spreading
+ return canBeGrass(levelReader.getChunk(pos), state, levelReader, pos);
+ }
+ private static boolean canBeGrass(net.minecraft.world.level.chunk.ChunkAccess chunk, BlockState state, LevelReader levelReader, BlockPos pos) {
+ // Paper end - Perf: optimize dirt and snow spreading
BlockPos blockPos = pos.above();
- BlockState blockState = levelReader.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<? extends SpreadingSnowyDirtBlock> 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, Boolean.valueOf(isSnowySetting(level.getBlockState(blockPos.above()))))); // 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) blockState.setValue(SpreadingSnowyDirtBlock.SNOWY, isSnowySetting(access.getBlockState(blockPos.above())))); // CraftBukkit
+ // Paper end - Perf: optimize dirt and snow spreading
}
}
}

View file

@ -0,0 +1,57 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Sat, 25 Jan 2020 17:04:35 -0800
Subject: [PATCH] Optimise getChunkAt calls for loaded chunks
bypass the need to get a player chunk, then get the either,
then unwrap it...
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
index c3553385074108d686425a7637b8975076d906b9..2f49dbc919f7f5eea9abce6106723c72f5ae45fb 100644
--- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -218,6 +218,12 @@ public class ServerChunkCache extends ChunkSource {
if (Thread.currentThread() != this.mainThread) {
return CompletableFuture.<ChunkAccess>supplyAsync(() -> this.getChunk(x, z, chunkStatus, requireChunk), this.mainThreadProcessor).join();
} else {
+ // Paper start - Perf: Optimise getChunkAt calls for loaded chunks
+ LevelChunk ifLoaded = this.getChunkAtIfCachedImmediately(x, z);
+ if (ifLoaded != null) {
+ return ifLoaded;
+ }
+ // Paper end - Perf: Optimise getChunkAt calls for loaded chunks
ProfilerFiller profilerFiller = Profiler.get();
profilerFiller.incrementCounter("getChunk");
long packedChunkPos = ChunkPos.asLong(x, z);
@@ -252,30 +258,7 @@ public class ServerChunkCache extends ChunkSource {
if (Thread.currentThread() != this.mainThread) {
return null;
} else {
- Profiler.get().incrementCounter("getChunkNow");
- long packedChunkPos = ChunkPos.asLong(chunkX, chunkZ);
-
- for (int i = 0; i < 4; i++) {
- if (packedChunkPos == this.lastChunkPos[i] && this.lastChunkStatus[i] == ChunkStatus.FULL) {
- ChunkAccess chunkAccess = this.lastChunk[i];
- return chunkAccess instanceof LevelChunk ? (LevelChunk)chunkAccess : null;
- }
- }
-
- ChunkHolder visibleChunkIfPresent = this.getVisibleChunkIfPresent(packedChunkPos);
- if (visibleChunkIfPresent == 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;
- }
- }
-
- return null;
- }
+ return this.getChunkAtIfCachedImmediately(chunkX, chunkZ); // Paper - Perf: Optimise getChunkAt calls for loaded chunks
}
}

View file

@ -0,0 +1,212 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Thu, 4 Jun 2020 02:24:49 -0400
Subject: [PATCH] Optimize Bit Operations by inlining
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 98f0b1cf19d7a035849a9a2fa25e2be3a4c5a980..a81694a22e94cca6f7110f7d5b205d1303f4e071 100644
--- a/net/minecraft/core/BlockPos.java
+++ b/net/minecraft/core/BlockPos.java
@@ -51,15 +51,17 @@ public class BlockPos extends Vec3i {
};
private static final Logger LOGGER = LogUtils.getLogger();
public static final BlockPos ZERO = new BlockPos(0, 0, 0);
- public static final int PACKED_HORIZONTAL_LENGTH = 1 + Mth.log2(Mth.smallestEncompassingPowerOfTwo(30000000));
- public static final int PACKED_Y_LENGTH = 64 - 2 * PACKED_HORIZONTAL_LENGTH;
- private static final long PACKED_X_MASK = (1L << PACKED_HORIZONTAL_LENGTH) - 1L;
- private static final long PACKED_Y_MASK = (1L << PACKED_Y_LENGTH) - 1L;
- private static final long PACKED_Z_MASK = (1L << PACKED_HORIZONTAL_LENGTH) - 1L;
+ // Paper start - Optimize Bit Operations by inlining
+ public static final int PACKED_HORIZONTAL_LENGTH = 26;
+ public static final int PACKED_Y_LENGTH = 12;
+ private static final long PACKED_X_MASK = 67108863;
+ private static final long PACKED_Y_MASK = 4095;
+ private static final long PACKED_Z_MASK = 67108863;
private static final int Y_OFFSET = 0;
- private static final int Z_OFFSET = PACKED_Y_LENGTH;
- private static final int X_OFFSET = PACKED_Y_LENGTH + PACKED_HORIZONTAL_LENGTH;
- public static final int MAX_HORIZONTAL_COORDINATE = (1 << PACKED_HORIZONTAL_LENGTH) / 2 - 1;
+ private static final int Z_OFFSET = 12;
+ private static final int X_OFFSET = 38;
+ public static final int MAX_HORIZONTAL_COORDINATE = 33554431;
+ // Paper end - Optimize Bit Operations by inlining
public BlockPos(int x, int y, int z) {
super(x, y, z);
@@ -69,28 +71,29 @@ public class BlockPos extends Vec3i {
this(vector.getX(), vector.getY(), vector.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(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 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 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 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 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 containing(double x, double y, double z) {
@@ -114,10 +117,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 getFlatIndex(long packedPos) {
diff --git a/net/minecraft/core/SectionPos.java b/net/minecraft/core/SectionPos.java
index 1780d8e14cea62971da75e4dcc80d1805434037b..ce8c394ea1a7bc5bf5c568c82e6158b19df517d8 100644
--- a/net/minecraft/core/SectionPos.java
+++ b/net/minecraft/core/SectionPos.java
@@ -38,7 +38,7 @@ public class SectionPos extends Vec3i {
}
public static SectionPos of(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) {
@@ -54,7 +54,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 bottomOf(ChunkAccess chunk) {
@@ -65,8 +65,16 @@ public class SectionPos extends Vec3i {
return offset(packed, direction.getStepX(), direction.getStepY(), direction.getStepZ());
}
+ // Paper start
+ public static long getAdjacentFromBlockPos(int x, int y, int z, Direction direction) {
+ return (((long) ((x >> 4) + direction.getStepX()) & 4194303L) << 42) | (((long) ((y >> 4) + direction.getStepY()) & 1048575L)) | (((long) ((z >> 4) + direction.getStepZ()) & 4194303L) << 20);
+ }
+ public static long getAdjacentFromSectionPos(int x, int y, int z, Direction direction) {
+ 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 int posToSectionCoord(double pos) {
@@ -86,10 +94,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);
+ return (short) ((pos.getX() & 15) << 8 | (pos.getZ() & 15) << 4 | pos.getY() & 15); // Paper - simplify/inline
}
public static int sectionRelativeX(short x) {
@@ -152,16 +157,16 @@ public class SectionPos extends Vec3i {
return this.getZ();
}
- public int minBlockX() {
- return sectionToBlockCoord(this.x());
+ public final int minBlockX() { // Paper - final
+ return this.getX() << 4; // Paper - inline
}
- public int minBlockY() {
- return sectionToBlockCoord(this.y());
+ public final int minBlockY() { // Paper - final
+ return this.getY() << 4; // Paper - inline
}
- public int minBlockZ() {
- return sectionToBlockCoord(this.z());
+ public final int minBlockZ() { // Paper - final
+ return this.getZ() << 4; // Paper - inline
}
public int maxBlockX() {
@@ -177,7 +182,7 @@ public class SectionPos extends Vec3i {
}
public static long blockToSection(long levelPos) {
- return asLong(blockToSectionCoord(BlockPos.getX(levelPos)), blockToSectionCoord(BlockPos.getY(levelPos)), blockToSectionCoord(BlockPos.getZ(levelPos)));
+ return (((long) (int) (levelPos >> 42) & 4194303L) << 42) | (((long) (int) ((levelPos << 52) >> 56) & 1048575L)) | (((long) (int) ((levelPos << 26) >> 42) & 4194303L) << 20); // Simplify to reduce instruction count
}
public static long getZeroNode(int x, int z) {
@@ -205,15 +210,17 @@ public class SectionPos extends Vec3i {
return asLong(blockToSectionCoord(blockPos.getX()), blockToSectionCoord(blockPos.getY()), blockToSectionCoord(blockPos.getZ()));
}
+ // Paper start
+ public static long blockPosAsSectionLong(int x, int y, int z) {
+ 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;
+ return ((x & 4194303L) << 42) | ((y & 1048575L)) | ((z & 4194303L) << 20); // Paper - Simplify to reduce instruction count
}
public long asLong() {
- return asLong(this.x(), this.y(), this.z());
+ return ((this.getX() & 4194303L) << 42) | ((this.getY() & 1048575L)) | ((this.getZ() & 4194303L) << 20); // Paper - Simplify to reduce instruction count
}
@Override
@@ -226,16 +233,13 @@ public class SectionPos extends Vec3i {
}
public static Stream<SectionPos> 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);
+ 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<SectionPos> aroundChunk(ChunkPos chunkPos, int x, int y, int z) {
+ public static Stream<SectionPos> aroundChunk(ChunkPos chunkPos, int radius, int minY, int maxY) { // Paper - fix params
int i = chunkPos.x;
int i1 = chunkPos.z;
- return betweenClosedStream(i - x, y, i1 - x, i + x, z, i1 + x);
+ return betweenClosedStream(i - radius, minY, i1 - radius, i + radius, maxY, i1 + radius); // Paper - simplify/inline
}
public static Stream<SectionPos> betweenClosedStream(final int x1, final int y1, final int z1, final int x2, final int y2, final int z2) {

View file

@ -0,0 +1,223 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Josh Roy <10731363+JRoy@users.noreply.github.com>
Date: Wed, 1 Jul 2020 18:01:49 -0400
Subject: [PATCH] Remove streams from hot code
Co-authored-by: Bjarne Koll <git@lynxplay.dev>
Co-authored-by: Spottedleaf <Spottedleaf@users.noreply.github.com>
diff --git a/net/minecraft/world/entity/ai/behavior/GateBehavior.java b/net/minecraft/world/entity/ai/behavior/GateBehavior.java
index c215d97c24e6501e1a48a76fc08bf48ff4dfe462..bd31d1cac0d022a72bd536c41d1ef811886e7068 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<E extends LivingEntity> implements BehaviorControl<E>
if (this.hasRequiredMemories(entity)) {
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
return true;
} else {
return false;
@@ -66,10 +66,13 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E>
@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));
+ // Paper start - Perf: Remove streams from hot code
+ for (final BehaviorControl<? super E> behavior : this.behaviors) {
+ if (behavior.getStatus() == Behavior.Status.RUNNING) {
+ behavior.tickOrStop(level, entity, gameTime);
+ }
+ }
+ // Paper end - Perf: Remove streams from hot code
if (this.behaviors.stream().noneMatch(behavior -> behavior.getStatus() == Behavior.Status.RUNNING)) {
this.doStop(level, entity, gameTime);
}
@@ -78,11 +81,16 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E>
@Override
public final void doStop(ServerLevel level, E entity, long gameTime) {
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);
+ // Paper start - Perf: Remove streams from hot code
+ for (final BehaviorControl<? super E> behavior : this.behaviors) {
+ if (behavior.getStatus() == Behavior.Status.RUNNING) {
+ behavior.doStop(level, entity, gameTime);
+ }
+ }
+ for (final MemoryModuleType<?> exitErasedMemory : this.exitErasedMemories) {
+ entity.getBrain().eraseMemory(exitErasedMemory);
+ }
+ // Paper end - Perf: Remove streams from hot code
}
@Override
@@ -116,20 +124,30 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E>
public static enum RunningPolicy {
RUN_ONE {
+ // Paper start - Perf: Remove streams from hot code
@Override
- public <E extends LivingEntity> void apply(Stream<BehaviorControl<? super E>> behaviors, ServerLevel level, E owner, long gameTime) {
- behaviors.filter(behavior -> behavior.getStatus() == Behavior.Status.STOPPED)
- .filter(behavior -> behavior.tryStart(level, owner, gameTime))
- .findFirst();
+ public <E extends LivingEntity> void apply(ShufflingList<BehaviorControl<? super E>> behaviors, ServerLevel level, E owner, long gameTime) {
+ for (final BehaviorControl<? super E> behavior : behaviors) {
+ if (behavior.getStatus() == Behavior.Status.STOPPED && behavior.tryStart(level, owner, gameTime)) {
+ break;
+ }
+ }
+ // Paper end - Perf: Remove streams from hot code
}
},
TRY_ALL {
+ // Paper start - Perf: Remove streams from hot code
@Override
- public <E extends LivingEntity> void apply(Stream<BehaviorControl<? super E>> behaviors, ServerLevel level, E owner, long gameTime) {
- behaviors.filter(behavior -> behavior.getStatus() == Behavior.Status.STOPPED).forEach(behavior -> behavior.tryStart(level, owner, gameTime));
+ public <E extends LivingEntity> void apply(ShufflingList<BehaviorControl<? super E>> behaviors, ServerLevel level, E owner, long gameTime) {
+ for (final BehaviorControl<? super E> behavior : behaviors) {
+ if (behavior.getStatus() == Behavior.Status.STOPPED) {
+ behavior.tryStart(level, owner, gameTime);
+ }
+ }
+ // Paper end - Perf: Remove streams from hot code
}
};
- public abstract <E extends LivingEntity> void apply(Stream<BehaviorControl<? super E>> behaviors, ServerLevel level, E owner, long gameTime);
+ public abstract <E extends LivingEntity> void apply(ShufflingList<BehaviorControl<? super E>> behaviors, ServerLevel level, E owner, long gameTime); // 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 2c839dc80f451c83135828a97aced1a531004bab..b74a4ce1b629d440681a1f5c026997ccaf1d0373 100644
--- a/net/minecraft/world/entity/ai/gossip/GossipContainer.java
+++ b/net/minecraft/world/entity/ai/gossip/GossipContainer.java
@@ -59,8 +59,22 @@ public class GossipContainer {
return this.gossips.entrySet().stream().flatMap(gossip -> gossip.getValue().unpack(gossip.getKey()));
}
+ // Paper start - Perf: Remove streams from hot code
+ private List<GossipContainer.GossipEntry> decompress() {
+ List<GossipContainer.GossipEntry> list = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>();
+ for (Map.Entry<UUID, GossipContainer.EntityGossips> entry : this.gossips.entrySet()) {
+ for (GossipContainer.GossipEntry cur : entry.getValue().decompress(entry.getKey())) {
+ if (cur.weightedValue() != 0) {
+ list.add(cur);
+ }
+ }
+ }
+ return list;
+ }
+ // Paper end - Perf: Remove streams from hot code
+
private Collection<GossipContainer.GossipEntry> selectGossipsForTransfer(RandomSource random, int amount) {
- List<GossipContainer.GossipEntry> list = this.unpack().toList();
+ List<GossipContainer.GossipEntry> list = this.decompress(); // Paper - Perf: Remove streams from hot code
if (list.isEmpty()) {
return Collections.emptyList();
} else {
@@ -145,7 +159,7 @@ public class GossipContainer {
public <T> T store(DynamicOps<T> ops) {
return GossipContainer.GossipEntry.LIST_CODEC
- .encodeStart(ops, this.unpack().toList())
+ .encodeStart(ops, this.decompress()) // Paper - Perf: Remove streams from hot code
.resultOrPartial(errorMessage -> LOGGER.warn("Failed to serialize gossips: {}", errorMessage))
.orElseGet(ops::emptyList);
}
@@ -172,12 +186,23 @@ public class GossipContainer {
final Object2IntMap<GossipType> entries = new Object2IntOpenHashMap<>();
public int weightedValue(Predicate<GossipType> gossipType) {
- return this.entries
- .object2IntEntrySet()
- .stream()
- .filter(gossip -> gossipType.test(gossip.getKey()))
- .mapToInt(gossip -> gossip.getIntValue() * gossip.getKey().weight)
- .sum();
+ // Paper start - Perf: Remove streams from hot code
+ int weight = 0;
+ for (Object2IntMap.Entry<GossipType> entry : entries.object2IntEntrySet()) {
+ if (gossipType.test(entry.getKey())) {
+ weight += entry.getIntValue() * entry.getKey().weight;
+ }
+ }
+ return weight;
+ }
+
+ public List<GossipContainer.GossipEntry> decompress(UUID uuid) {
+ List<GossipContainer.GossipEntry> list = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>();
+ for (Object2IntMap.Entry<GossipType> entry : entries.object2IntEntrySet()) {
+ list.add(new GossipContainer.GossipEntry(uuid, entry.getKey(), entry.getIntValue()));
+ }
+ return list;
+ // Paper end - Perf: Remove streams from hot code
}
public Stream<GossipContainer.GossipEntry> unpack(UUID identifier) {
diff --git a/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java b/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java
index 38873e56e95dc772b184e4271f7af1fb411ac9f8..09fd13e2d958da8326276c4dadf25bf488aff5ac 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<Mob> {
@Override
protected void doTick(ServerLevel level, Mob entity) {
Brain<?> brain = entity.getBrain();
- List<ItemEntity> entitiesOfClass = level.getEntitiesOfClass(ItemEntity.class, entity.getBoundingBox().inflate(32.0, 16.0, 32.0), itemEntity -> true);
+ List<ItemEntity> 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<ItemEntity> optional = entitiesOfClass.stream()
- .filter(itemEntity -> entity.wantsToPickUp(level, itemEntity.getItem()))
- .filter(itemEntity -> itemEntity.closerThan(entity, 32.0))
- .filter(entity::hasLineOfSight)
- .findFirst();
- brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM, optional);
+ // 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;
+ break;
+ }
+ }
+ brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM, Optional.ofNullable(nearest));
+ // Paper end - Perf: remove streams from hot code
}
}
diff --git a/net/minecraft/world/level/levelgen/Beardifier.java b/net/minecraft/world/level/levelgen/Beardifier.java
index 1a09da5aa1ae047a002d6779326c2a29e47d32b5..131923282c9ecbcb1d7f45a826da907c02bd2716 100644
--- a/net/minecraft/world/level/levelgen/Beardifier.java
+++ b/net/minecraft/world/level/levelgen/Beardifier.java
@@ -35,9 +35,10 @@ public class Beardifier implements DensityFunctions.BeardifierOrMarker {
int minBlockZ = chunkPos.getMinBlockZ();
ObjectList<Beardifier.Rigid> list = new ObjectArrayList<>(10);
ObjectList<JigsawJunction> list1 = new ObjectArrayList<>(32);
- structureManager.startsForStructure(chunkPos, structure -> structure.terrainAdaptation() != TerrainAdjustment.NONE)
- .forEach(
- structureStart -> {
+ // Paper start - Perf: Remove streams from hot code
+ for (net.minecraft.world.level.levelgen.structure.StructureStart structureStart : structureManager.startsForStructure(chunkPos, structure -> {
+ return structure.terrainAdaptation() != TerrainAdjustment.NONE;
+ })) { // Paper end - Perf: Remove streams from hot code
TerrainAdjustment terrainAdjustment = structureStart.getStructure().terrainAdaptation();
for (StructurePiece structurePiece : structureStart.getPieces()) {
@@ -65,8 +66,7 @@ public class Beardifier implements DensityFunctions.BeardifierOrMarker {
}
}
}
- }
- );
+ } // Paper - Perf: Remove streams from hot code
return new Beardifier(list.iterator(), list1.iterator());
}

View file

@ -15,100 +15,103 @@ Optimize collection by creating a list instead of a set of the key and value.
This lets us get faster foreach iteration, as well as avoids map lookups on
the values when needed.
diff --git a/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java b/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java
+++ b/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java
@@ -0,0 +0,0 @@ public class PathFinder {
if (node == null) {
diff --git a/net/minecraft/world/level/pathfinder/PathFinder.java b/net/minecraft/world/level/pathfinder/PathFinder.java
index a6ef296f7af1f784e1f0772947a6cb3519a3bc2a..81de6c1bbef1cafd3036e736dd305fbedc8368c6 100644
--- a/net/minecraft/world/level/pathfinder/PathFinder.java
+++ b/net/minecraft/world/level/pathfinder/PathFinder.java
@@ -43,8 +43,12 @@ public class PathFinder {
if (start == null) {
return null;
} else {
- Map<Target, BlockPos> map = positions.stream()
- .collect(Collectors.toMap(pos -> this.nodeEvaluator.getTarget((double)pos.getX(), (double)pos.getY(), (double)pos.getZ()), Function.identity()));
- Map<Target, BlockPos> map = targetPositions.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.Entry<Target, BlockPos>> map = Lists.newArrayList();
+ for (final BlockPos pos : positions) {
+ for (BlockPos pos : targetPositions) {
+ map.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(node, map, followRange, distance, rangeMultiplier);
Path path = this.findPath(start, map, maxRange, accuracy, searchDepthMultiplier);
this.nodeEvaluator.done();
return path;
@@ -0,0 +0,0 @@ public class PathFinder {
@@ -52,19 +56,19 @@ public class PathFinder {
}
@Nullable
- private Path findPath(Node startNode, Map<Target, BlockPos> positions, float followRange, int distance, float rangeMultiplier) {
+ // Paper start - Perf: remove streams and optimize collection
+ private Path findPath(Node startNode, List<Map.Entry<Target, BlockPos>> positions, float followRange, int distance, float rangeMultiplier) {
- private Path findPath(Node node, Map<Target, BlockPos> targetPositions, float maxRange, int accuracy, float searchDepthMultiplier) {
+ private Path findPath(Node node, List<Map.Entry<Target, BlockPos>> positions, float maxRange, int accuracy, float searchDepthMultiplier) { // Paper - optimize collection
ProfilerFiller profilerFiller = Profiler.get();
profilerFiller.push("find_path");
profilerFiller.markForCharting(MetricCategory.PATH_FINDING);
- Set<Target> set = positions.keySet();
+ // Set<Target> set = positions.keySet();
startNode.g = 0.0F;
- startNode.h = this.getBestH(startNode, set);
+ startNode.h = this.getBestH(startNode, positions); // Paper - optimize collection
startNode.f = startNode.h;
- Set<Target> set = targetPositions.keySet();
+ // Set<Target> set = targetPositions.keySet(); // Paper
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(startNode);
- Set<Node> set2 = ImmutableSet.of();
+ // Set<Node> set2 = ImmutableSet.of(); // Paper - unused - diff on change
this.openSet.insert(node);
- Set<Node> set1 = ImmutableSet.of();
+ // Set<Node> set1 = ImmutableSet.of(); // Paper - unused - diff on change
int i = 0;
- Set<Target> set3 = Sets.newHashSetWithExpectedSize(set.size());
- Set<Target> set2 = Sets.newHashSetWithExpectedSize(set.size());
+ List<Map.Entry<Target, BlockPos>> entryList = Lists.newArrayListWithExpectedSize(positions.size()); // Paper - optimize collection
int j = (int)((float)this.maxVisitedNodes * rangeMultiplier);
int i1 = (int)(this.maxVisitedNodes * searchDepthMultiplier);
while (!this.openSet.isEmpty()) {
@@ -0,0 +0,0 @@ public class PathFinder {
Node node = this.openSet.pop();
node.closed = true;
@@ -75,14 +79,18 @@ public class PathFinder {
Node node1 = this.openSet.pop();
node1.closed = true;
- for (Target target : set) {
+ // Paper start - optimize collection
+ for (int i1 = 0; i1 < positions.size(); i1++) {
+ final Map.Entry<Target, BlockPos> entry = positions.get(i1);
+ for (int positionIndex = 0, size = positions.size(); positionIndex < size; positionIndex++) {
+ final Map.Entry<Target, BlockPos> entry = positions.get(positionIndex);
+ Target target = entry.getKey();
if (node.distanceManhattan(target) <= (float)distance) {
if (node1.distanceManhattan(target) <= accuracy) {
target.setReached();
- set3.add(target);
- set2.add(target);
+ entryList.add(entry);
+ // Paper end - Perf: remove streams and optimize collection
}
}
- if (!set3.isEmpty()) {
- if (!set2.isEmpty()) {
+ if (!entryList.isEmpty()) { // Paper - Perf: remove streams and optimize collection; rename
break;
}
@@ -0,0 +0,0 @@ public class PathFinder {
if (node2.walkedDistance < followRange && (!node2.inOpenSet() || g < node2.g)) {
node2.cameFrom = node;
node2.g = g;
@@ -97,7 +105,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 {
@@ -0,0 +0,0 @@ public class PathFinder {
@@ -109,25 +117,34 @@ public class PathFinder {
}
}
- Optional<Path> optional = !set3.isEmpty()
- ? set3.stream().map(node -> this.reconstructPath(node.getBestNode(), positions.get(node), true)).min(Comparator.comparingInt(Path::getNodeCount))
- Optional<Path> optional = !set2.isEmpty()
- ? set2.stream()
- .map(pathfinder -> this.reconstructPath(pathfinder.getBestNode(), targetPositions.get(pathfinder), true))
- .min(Comparator.comparingInt(Path::getNodeCount))
- : set.stream()
- .map(targetx -> this.reconstructPath(targetx.getBestNode(), positions.get(targetx), false))
- .map(pathfinder -> this.reconstructPath(pathfinder.getBestNode(), targetPositions.get(pathfinder), false))
- .min(Comparator.comparingDouble(Path::getDistToTarget).thenComparingInt(Path::getNodeCount));
+ // Paper start - Perf: remove streams and optimize collection
+ Path best = null;
+ boolean entryListIsEmpty = entryList.isEmpty();
+ Comparator<Path> comparator = entryListIsEmpty ? Comparator.comparingInt(Path::getNodeCount)
+ Comparator<Path> comparator = entryListIsEmpty
+ ? Comparator.comparingInt(Path::getNodeCount)
+ : Comparator.comparingDouble(Path::getDistToTarget).thenComparingInt(Path::getNodeCount);
+ for (Map.Entry<Target, BlockPos> entry : entryListIsEmpty ? positions : entryList) {
+ Path path = this.reconstructPath(entry.getKey().getBestNode(), entry.getValue(), !entryListIsEmpty);
+ if (best == null || comparator.compare(path, best) < 0)
+ if (best == null || comparator.compare(path, best) < 0) {
+ best = path;
+ }
+ }
profilerFiller.pop();
- return optional.isEmpty() ? null : optional.get();
@ -116,8 +119,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper end - Perf: remove streams and optimize collection
}
protected float distance(Node a, Node b) {
return a.distanceTo(b);
protected float distance(Node first, Node second) {
return first.distanceTo(second);
}
- private float getBestH(Node node, Set<Target> targets) {
@ -129,6 +132,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ 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 g = node.distanceTo(target);
target.updateBest(g, node);
f = Math.min(g, f);
float f1 = node.distanceTo(target);
target.updateBest(f1, node);
f = Math.min(f1, f);

View file

@ -28,30 +28,30 @@ which is most likely in an unloaded chunk - which means that the
client will not tick the entity and thus not lerp the entity
from its old position to its new position.
diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundAddEntityPacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundAddEntityPacket.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundAddEntityPacket.java
+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundAddEntityPacket.java
@@ -0,0 +0,0 @@ public class ClientboundAddEntityPacket implements Packet<ClientGamePacketListen
diff --git a/net/minecraft/network/protocol/game/ClientboundAddEntityPacket.java b/net/minecraft/network/protocol/game/ClientboundAddEntityPacket.java
index db31989ebe3d7021cfd2311439e9a00f819b0841..1373977b339405ef59bb3ea03d195285c96dd3fe 100644
--- a/net/minecraft/network/protocol/game/ClientboundAddEntityPacket.java
+++ b/net/minecraft/network/protocol/game/ClientboundAddEntityPacket.java
@@ -42,9 +42,11 @@ public class ClientboundAddEntityPacket implements Packet<ClientGamePacketListen
this(
entity.getId(),
entity.getUUID(),
- entityTrackerEntry.getPositionBase().x(),
- entityTrackerEntry.getPositionBase().y(),
- entityTrackerEntry.getPositionBase().z(),
- serverEntity.getPositionBase().x(),
- serverEntity.getPositionBase().y(),
- serverEntity.getPositionBase().z(),
+ // Paper start - fix entity tracker desync
+ entity.trackingPosition().x(),
+ entity.trackingPosition().y(),
+ entity.trackingPosition().z(),
+ // Paper end - fix entity tracker desync
entityTrackerEntry.getLastSentXRot(),
entityTrackerEntry.getLastSentYRot(),
serverEntity.getLastSentXRot(),
serverEntity.getLastSentYRot(),
entity.getType(),
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
index 3dff97f13586be3b52bbe786852c185f6753a019..ff6503bf8eb88d1264c3d848a89d0255b4b3ae68 100644
--- a/net/minecraft/server/level/ChunkMap.java
+++ b/net/minecraft/server/level/ChunkMap.java
@@ -1208,6 +1208,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.serverEntity.addPairing(player);
}
// Paper end - entity tracking events
@ -59,11 +59,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
}
} else if (this.seenBy.remove(player.connection)) {
this.serverEntity.removePairing(player);
diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ServerEntity.java
+++ b/src/main/java/net/minecraft/server/level/ServerEntity.java
@@ -0,0 +0,0 @@ public class ServerEntity {
diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java
index 870b9efd445ddadb3725e88351555ad986ce7c72..a4da36060ca75968f5831adfc3f7117281649b7a 100644
--- a/net/minecraft/server/level/ServerEntity.java
+++ b/net/minecraft/server/level/ServerEntity.java
@@ -90,6 +90,13 @@ public class ServerEntity {
this.trackedDataValues = entity.getEntityData().getNonDefaultValues();
}
@ -77,29 +77,29 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
public void sendChanges() {
// Paper start - optimise collisions
if (((ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity)this.entity).moonrise$isHardColliding()) {
@@ -0,0 +0,0 @@ public class ServerEntity {
}
@@ -130,7 +137,7 @@ public class ServerEntity {
this.sendDirtyEntityData();
}
- if (this.tickCount % this.updateInterval == 0 || this.entity.hasImpulse || this.entity.getEntityData().isDirty()) {
+ if (this.forceStateResync || this.tickCount % this.updateInterval == 0 || this.entity.hasImpulse || this.entity.getEntityData().isDirty()) { // Paper - fix desync when a player is added to the tracker
byte b0 = Mth.packDegrees(this.entity.getYRot());
byte b = Mth.packDegrees(this.entity.getYRot());
byte b1 = Mth.packDegrees(this.entity.getXRot());
boolean flag = Math.abs(b0 - this.lastSentYRot) >= 1 || Math.abs(b1 - this.lastSentXRot) >= 1;
@@ -0,0 +0,0 @@ public class ServerEntity {
long k = this.positionCodec.encodeZ(vec3d);
boolean flag5 = i < -32768L || i > 32767L || j < -32768L || j > 32767L || k < -32768L || k > 32767L;
- if (!flag5 && this.teleportDelay <= 400 && !this.wasRiding && this.wasOnGround == this.entity.onGround()) {
+ if (!this.forceStateResync && !flag5 && this.teleportDelay <= 400 && !this.wasRiding && this.wasOnGround == this.entity.onGround()) { // Paper - fix desync when a player is added to the tracker
if ((!flag2 || !flag) && !(this.entity instanceof AbstractArrow)) {
if (flag2) {
packet1 = new ClientboundMoveEntityPacket.Pos(this.entity.getId(), (short) ((int) i), (short) ((int) j), (short) ((int) k), this.entity.onGround());
@@ -0,0 +0,0 @@ public class ServerEntity {
boolean flag = Math.abs(b - this.lastSentYRot) >= 1 || Math.abs(b1 - this.lastSentXRot) >= 1;
@@ -165,7 +172,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;
- if (flag5 || this.teleportDelay > 400 || this.wasRiding || this.wasOnGround != this.entity.onGround()) {
+ if (this.forceStateResync || flag5 || this.teleportDelay > 400 || this.wasRiding || this.wasOnGround != this.entity.onGround()) { // Paper - fix desync when a player is added to the tracker
this.wasOnGround = this.entity.onGround();
this.teleportDelay = 0;
packet = ClientboundEntityPositionSyncPacket.of(this.entity);
@@ -230,6 +237,7 @@ public class ServerEntity {
}
this.entity.hasImpulse = false;
+ this.forceStateResync = false; // Paper - fix desync when a player is added to the tracker
}
++this.tickCount;
this.tickCount++;

View file

@ -3,8 +3,6 @@ From: theosib <millerti@172.16.221.1>
Date: Thu, 27 Sep 2018 01:43:35 -0600
Subject: [PATCH] Eigencraft redstone implementation
Author: theosib <millerti@172.16.221.1>
Original license: MIT
This patch implements theosib's redstone algorithms to completely overhaul the way redstone works.
@ -17,19 +15,15 @@ A lot of this code is self-contained in a helper class.
Aside from making the obvious class/function renames and obfhelpers I didn't need to modify much.
Just added Bukkit's event system and took a few liberties with dead code and comment misspellings.
== AT ==
public net.minecraft.world.level.block.RedStoneWireBlock shouldSignal
public net.minecraft.world.level.block.RedStoneWireBlock canSurvive(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/LevelReader;Lnet/minecraft/core/BlockPos;)Z
Co-authored-by: egg82 <eggys82@gmail.com>
diff --git a/src/main/java/com/destroystokyo/paper/util/RedstoneWireTurbo.java b/src/main/java/com/destroystokyo/paper/util/RedstoneWireTurbo.java
diff --git a/io/papermc/paper/redstone/RedstoneWireTurbo.java b/io/papermc/paper/redstone/RedstoneWireTurbo.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
index 0000000000000000000000000000000000000000..ff747a1ecdf3c888bca0d69de4f85dcd810b6139
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/util/RedstoneWireTurbo.java
@@ -0,0 +0,0 @@
+package com.destroystokyo.paper.util;
+++ b/io/papermc/paper/redstone/RedstoneWireTurbo.java
@@ -0,0 +1,954 @@
+package io.papermc.paper.redstone;
+
+import java.util.List;
+import java.util.Map;
@ -48,14 +42,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+/**
+ * Used for the faster redstone algorithm.
+ * Original author: theosib
+ * Original license: MIT
+ *
+ * Ported to Paper and updated to 1.13 by egg82
+ */
+public class RedstoneWireTurbo {
+public final class RedstoneWireTurbo {
+ /*
+ * This is Helper class for BlockRedstoneWire. It implements a minimally-invasive
+ * bolt-on accelerator that performs a breadth-first search through redstone wire blocks
@ -862,7 +849,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ */
+ private BlockState calculateCurrentChanges(final Level worldIn, final UpdateNode upd) {
+ BlockState state = upd.currentState;
+ final int i = state.getValue(RedStoneWireBlock.POWER).intValue();
+ final int i = state.getValue(RedStoneWireBlock.POWER);
+ int j = 0;
+ j = getMaxCurrentStrength(upd, j);
+ int l = 0;
@ -986,21 +973,21 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ */
+ private static int getMaxCurrentStrength(final UpdateNode upd, final int strength) {
+ if (upd.type != UpdateNode.Type.REDSTONE) return strength;
+ final int i = upd.currentState.getValue(RedStoneWireBlock.POWER).intValue();
+ final int i = upd.currentState.getValue(RedStoneWireBlock.POWER);
+ return i > strength ? i : strength;
+ }
+}
diff --git a/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java b/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java
@@ -0,0 +0,0 @@ public class RedStoneWireBlock extends Block {
return floor.isFaceSturdy(world, pos, Direction.UP) || floor.is(Blocks.HOPPER);
diff --git a/net/minecraft/world/level/block/RedStoneWireBlock.java b/net/minecraft/world/level/block/RedStoneWireBlock.java
index 84e6c986917128d4488afa23d29c689cadb4f55d..f02232ce97779db0d12a5d5da1d767326d78ea4c 100644
--- a/net/minecraft/world/level/block/RedStoneWireBlock.java
+++ b/net/minecraft/world/level/block/RedStoneWireBlock.java
@@ -290,6 +290,60 @@ public class RedStoneWireBlock extends Block {
return state.isFaceSturdy(level, pos, Direction.UP) || state.is(Blocks.HOPPER);
}
+ // Paper start - Optimize redstone
+ // The bulk of the new functionality is found in RedstoneWireTurbo.java
+ com.destroystokyo.paper.util.RedstoneWireTurbo turbo = new com.destroystokyo.paper.util.RedstoneWireTurbo(this);
+ io.papermc.paper.redstone.RedstoneWireTurbo turbo = new io.papermc.paper.redstone.RedstoneWireTurbo(this);
+
+ /*
+ * Modified version of pre-existing updateSurroundingRedstone, which is called from
@ -1052,46 +1039,46 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ }
+ // Paper end
+
private void updatePowerStrength(Level world, BlockPos pos, BlockState state, @Nullable Orientation orientation, boolean blockAdded) {
if (useExperimentalEvaluator(world)) {
new ExperimentalRedstoneWireEvaluator(this).updatePowerStrength(world, pos, state, orientation, blockAdded);
@@ -0,0 +0,0 @@ public class RedStoneWireBlock extends Block {
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);
@@ -318,7 +372,7 @@ public class RedStoneWireBlock extends Block {
@Override
protected void onPlace(BlockState state, Level world, BlockPos pos, BlockState oldState, boolean notify) {
if (!oldState.is(state.getBlock()) && !world.isClientSide) {
- this.updatePowerStrength(world, pos, state, null, true);
+ this.updateSurroundingRedstone(world, pos, state, null, true); // Paper - Optimize redstone
protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean isMoving) {
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) {
world.updateNeighborsAt(pos.relative(direction), this);
@@ -0,0 +0,0 @@ public class RedStoneWireBlock extends Block {
world.updateNeighborsAt(pos.relative(direction), this);
level.updateNeighborsAt(pos.relative(direction), this);
@@ -337,7 +391,7 @@ public class RedStoneWireBlock extends Block {
level.updateNeighborsAt(pos.relative(direction), this);
}
- this.updatePowerStrength(world, pos, state, null, false);
+ this.updateSurroundingRedstone(world, pos, state, null, false); // Paper - Optimize redstone
this.updateNeighborsOfNeighboringWires(world, pos);
- this.updatePowerStrength(level, pos, state, null, false);
+ this.updateSurroundingRedstone(level, pos, state, null, false); // Paper - Optimize redstone
this.updateNeighborsOfNeighboringWires(level, pos);
}
}
@@ -0,0 +0,0 @@ public class RedStoneWireBlock extends Block {
if (!world.isClientSide) {
if (sourceBlock != this || !useExperimentalEvaluator(world)) {
if (state.canSurvive(world, pos)) {
- this.updatePowerStrength(world, pos, state, wireOrientation, false);
+ this.updateSurroundingRedstone(world, pos, state, wireOrientation, false); // Paper - Optimize redstone
@@ -363,7 +417,7 @@ public class RedStoneWireBlock extends Block {
if (!level.isClientSide) {
if (neighborBlock != 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
} else {
dropResources(state, world, pos);
world.removeBlock(pos, false);
diff --git a/src/main/java/net/minecraft/world/level/redstone/DefaultRedstoneWireEvaluator.java b/src/main/java/net/minecraft/world/level/redstone/DefaultRedstoneWireEvaluator.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/redstone/DefaultRedstoneWireEvaluator.java
+++ b/src/main/java/net/minecraft/world/level/redstone/DefaultRedstoneWireEvaluator.java
@@ -0,0 +0,0 @@ public class DefaultRedstoneWireEvaluator extends RedstoneWireEvaluator {
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 380fc51a252022195e178daccd8aa53dd1d71a2e..2d77780b6727f82ffc3cb216ca5f2d6483496cfd 100644
--- a/net/minecraft/world/level/redstone/DefaultRedstoneWireEvaluator.java
+++ b/net/minecraft/world/level/redstone/DefaultRedstoneWireEvaluator.java
@@ -44,7 +44,7 @@ public class DefaultRedstoneWireEvaluator extends RedstoneWireEvaluator {
}
}
- private int calculateTargetStrength(Level world, BlockPos pos) {
+ public int calculateTargetStrength(Level world, BlockPos pos) { // Paper - Optimize redstone
int i = this.getBlockSignal(world, pos);
return i == 15 ? i : Math.max(i, this.getIncomingWireSignal(world, pos));
- private int calculateTargetStrength(Level level, BlockPos pos) {
+ public int calculateTargetStrength(Level level, BlockPos pos) { // Paper - Optimize redstone
int blockSignal = this.getBlockSignal(level, pos);
return blockSignal == 15 ? blockSignal : Math.max(blockSignal, this.getIncomingWireSignal(level, pos));
}

View file

@ -20,12 +20,12 @@ Alternate Current needs the following modifications:
* RedStoneWireBlock: Replace calls to vanilla's or Eigencraft's methods for handling power changes with calls to
Alternate Current's wire handler.
diff --git a/src/main/java/alternate/current/wire/LevelHelper.java b/src/main/java/alternate/current/wire/LevelHelper.java
diff --git a/alternate/current/wire/LevelHelper.java b/alternate/current/wire/LevelHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
index 0000000000000000000000000000000000000000..eda108e2df9bf7d1ddd89287b8d2c2d7f1637c96
--- /dev/null
+++ b/src/main/java/alternate/current/wire/LevelHelper.java
@@ -0,0 +0,0 @@
+++ b/alternate/current/wire/LevelHelper.java
@@ -0,0 +1,66 @@
+package alternate.current.wire;
+
+import org.bukkit.craftbukkit.block.CraftBlock;
@ -92,12 +92,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ return true;
+ }
+}
diff --git a/src/main/java/alternate/current/wire/Node.java b/src/main/java/alternate/current/wire/Node.java
diff --git a/alternate/current/wire/Node.java b/alternate/current/wire/Node.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
index 0000000000000000000000000000000000000000..8af6c69098e64945361d116b5fd6ac21e97fcd8d
--- /dev/null
+++ b/src/main/java/alternate/current/wire/Node.java
@@ -0,0 +0,0 @@
+++ b/alternate/current/wire/Node.java
@@ -0,0 +1,113 @@
+package alternate.current.wire;
+
+import java.util.Arrays;
@ -211,12 +211,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ throw new UnsupportedOperationException("Not a WireNode!");
+ }
+}
diff --git a/src/main/java/alternate/current/wire/PriorityQueue.java b/src/main/java/alternate/current/wire/PriorityQueue.java
diff --git a/alternate/current/wire/PriorityQueue.java b/alternate/current/wire/PriorityQueue.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
index 0000000000000000000000000000000000000000..d71b4d0e4c44a2620b41b89475412db53bea20ed
--- /dev/null
+++ b/src/main/java/alternate/current/wire/PriorityQueue.java
@@ -0,0 +0,0 @@
+++ b/alternate/current/wire/PriorityQueue.java
@@ -0,0 +1,211 @@
+package alternate.current.wire;
+
+import java.util.AbstractQueue;
@ -428,12 +428,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ return prev;
+ }
+}
diff --git a/src/main/java/alternate/current/wire/SimpleQueue.java b/src/main/java/alternate/current/wire/SimpleQueue.java
diff --git a/alternate/current/wire/SimpleQueue.java b/alternate/current/wire/SimpleQueue.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
index 0000000000000000000000000000000000000000..2b30074252551e1dc55d5be17d26fb4a2d8eb2e4
--- /dev/null
+++ b/src/main/java/alternate/current/wire/SimpleQueue.java
@@ -0,0 +0,0 @@
+++ b/alternate/current/wire/SimpleQueue.java
@@ -0,0 +1,112 @@
+package alternate.current.wire;
+
+import java.util.AbstractQueue;
@ -546,12 +546,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ }
+ }
+}
diff --git a/src/main/java/alternate/current/wire/UpdateOrder.java b/src/main/java/alternate/current/wire/UpdateOrder.java
diff --git a/alternate/current/wire/UpdateOrder.java b/alternate/current/wire/UpdateOrder.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
index 0000000000000000000000000000000000000000..29338efd16cf62bb49e81cce09fbafd9b4319e7c
--- /dev/null
+++ b/src/main/java/alternate/current/wire/UpdateOrder.java
@@ -0,0 +0,0 @@
+++ b/alternate/current/wire/UpdateOrder.java
@@ -0,0 +1,390 @@
+package alternate.current.wire;
+
+import java.util.Locale;
@ -942,12 +942,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ public abstract void forEachNeighbor(NodeProvider nodes, Node source, int forward, Consumer<Node> action);
+
+}
diff --git a/src/main/java/alternate/current/wire/WireConnection.java b/src/main/java/alternate/current/wire/WireConnection.java
diff --git a/alternate/current/wire/WireConnection.java b/alternate/current/wire/WireConnection.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
index 0000000000000000000000000000000000000000..4fd8cb29024330397cfe4cbc1f237d285bfb7b3e
--- /dev/null
+++ b/src/main/java/alternate/current/wire/WireConnection.java
@@ -0,0 +0,0 @@
+++ b/alternate/current/wire/WireConnection.java
@@ -0,0 +1,30 @@
+package alternate.current.wire;
+
+/**
@ -978,12 +978,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ this.accept = accept;
+ }
+}
diff --git a/src/main/java/alternate/current/wire/WireConnectionManager.java b/src/main/java/alternate/current/wire/WireConnectionManager.java
diff --git a/alternate/current/wire/WireConnectionManager.java b/alternate/current/wire/WireConnectionManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
index 0000000000000000000000000000000000000000..f03b313e58385d626490a9e64c9616fd08aa951e
--- /dev/null
+++ b/src/main/java/alternate/current/wire/WireConnectionManager.java
@@ -0,0 +0,0 @@
+++ b/alternate/current/wire/WireConnectionManager.java
@@ -0,0 +1,134 @@
+package alternate.current.wire;
+
+import java.util.Arrays;
@ -1118,12 +1118,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ }
+ }
+}
diff --git a/src/main/java/alternate/current/wire/WireHandler.java b/src/main/java/alternate/current/wire/WireHandler.java
diff --git a/alternate/current/wire/WireHandler.java b/alternate/current/wire/WireHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
index 0000000000000000000000000000000000000000..259b301b2c8b64cb7974a235afb260e0e991af54
--- /dev/null
+++ b/src/main/java/alternate/current/wire/WireHandler.java
@@ -0,0 +0,0 @@
+++ b/alternate/current/wire/WireHandler.java
@@ -0,0 +1,1073 @@
+package alternate.current.wire;
+
+import java.util.Iterator;
@ -2197,12 +2197,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+
+ }
+}
diff --git a/src/main/java/alternate/current/wire/WireNode.java b/src/main/java/alternate/current/wire/WireNode.java
diff --git a/alternate/current/wire/WireNode.java b/alternate/current/wire/WireNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
index 0000000000000000000000000000000000000000..298076a0db4e6ee6e4775ac43bf749d9f5689bdb
--- /dev/null
+++ b/src/main/java/alternate/current/wire/WireNode.java
@@ -0,0 +0,0 @@
+++ b/alternate/current/wire/WireNode.java
@@ -0,0 +1,122 @@
+package alternate.current.wire;
+
+import net.minecraft.core.BlockPos;
@ -2325,11 +2325,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ return LevelHelper.setWireState(level, pos, state, added);
+ }
+}
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -0,0 +0,0 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 95a4e37a3c93f9b3c56c7a7376ed521cd46fbb6f..46dfaed12c998c219a20c711a06531aed2c68012 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -214,6 +214,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
public final UUID uuid;
public boolean hasPhysicsEvent = true; // Paper - BlockPhysicsEvent
public boolean hasEntityMoveEvent; // Paper - Add EntityMoveEvent
@ -2337,7 +2337,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
public LevelChunk getChunkIfLoaded(int x, int z) {
return this.chunkSource.getChunkAtIfLoadedImmediately(x, z); // Paper - Use getChunkIfLoadedImmediately
@@ -0,0 +0,0 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -2555,6 +2556,13 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
return this.chunkSource.getGenerator().getSeaLevel();
}
@ -2348,14 +2348,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ }
+ // Paper end - optimize redstone (Alternate Current)
+
private final class EntityCallbacks implements LevelCallback<Entity> {
EntityCallbacks() {}
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -0,0 +0,0 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
final class EntityCallbacks implements LevelCallback<Entity> {
@Override
public void onCreated(Entity entity) {
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index 8331b49185500ab3b4307e9ae05126b4f83a318a..2bbebb4335d927f240abcac67a5b423e38dc33d7 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -2099,6 +2099,17 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
public abstract FuelValues fuelValues();
@ -2371,77 +2371,78 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper end - optimize redstone (Alternate Current)
+
public static enum ExplosionInteraction implements StringRepresentable {
NONE("none"), BLOCK("block"), MOB("mob"), TNT("tnt"), TRIGGER("trigger"), STANDARD("standard"); // CraftBukkit - Add STANDARD which will always use Explosion.Effect.DESTROY
diff --git a/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java b/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java
@@ -0,0 +0,0 @@ public class RedStoneWireBlock extends Block {
return floor.isFaceSturdy(world, pos, Direction.UP) || floor.is(Blocks.HOPPER);
NONE("none"),
BLOCK("block"),
diff --git a/net/minecraft/world/level/block/RedStoneWireBlock.java b/net/minecraft/world/level/block/RedStoneWireBlock.java
index f02232ce97779db0d12a5d5da1d767326d78ea4c..12c9d60314c99fb65e640d255a2d0c6b7790ad4d 100644
--- a/net/minecraft/world/level/block/RedStoneWireBlock.java
+++ b/net/minecraft/world/level/block/RedStoneWireBlock.java
@@ -290,7 +290,7 @@ public class RedStoneWireBlock extends Block {
return state.isFaceSturdy(level, pos, Direction.UP) || state.is(Blocks.HOPPER);
}
- // Paper start - Optimize redstone
+ // Paper start - Optimize redstone (Eigencraft)
// The bulk of the new functionality is found in RedstoneWireTurbo.java
com.destroystokyo.paper.util.RedstoneWireTurbo turbo = new com.destroystokyo.paper.util.RedstoneWireTurbo(this);
io.papermc.paper.redstone.RedstoneWireTurbo turbo = new io.papermc.paper.redstone.RedstoneWireTurbo(this);
@@ -0,0 +0,0 @@ public class RedStoneWireBlock extends Block {
@@ -372,7 +372,13 @@ public class RedStoneWireBlock extends Block {
@Override
protected void onPlace(BlockState state, Level world, BlockPos pos, BlockState oldState, boolean notify) {
if (!oldState.is(state.getBlock()) && !world.isClientSide) {
- this.updateSurroundingRedstone(world, pos, state, null, true); // Paper - Optimize redstone
protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean isMoving) {
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
+ if (world.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) {
+ world.getWireHandler().onWireAdded(pos, state); // Alternate Current
+ if (level.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) {
+ level.getWireHandler().onWireAdded(pos, state); // Alternate Current
+ } else {
+ this.updateSurroundingRedstone(world, pos, state, null, true); // Vanilla/Eigencraft
+ this.updateSurroundingRedstone(level, pos, state, null, true); // Vanilla/Eigencraft
+ }
+ // Paper end
+ // Paper end - optimize redstone
for (Direction direction : Direction.Plane.VERTICAL) {
world.updateNeighborsAt(pos.relative(direction), this);
@@ -0,0 +0,0 @@ public class RedStoneWireBlock extends Block {
world.updateNeighborsAt(pos.relative(direction), this);
level.updateNeighborsAt(pos.relative(direction), this);
@@ -391,7 +397,13 @@ public class RedStoneWireBlock extends Block {
level.updateNeighborsAt(pos.relative(direction), this);
}
- this.updateSurroundingRedstone(world, pos, state, null, false); // Paper - Optimize redstone
- this.updateSurroundingRedstone(level, pos, state, null, false); // Paper - Optimize redstone
+ // Paper start - optimize redstone - replace call to updatePowerStrength
+ if (world.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) {
+ world.getWireHandler().onWireRemoved(pos, state); // Alternate Current
+ if (level.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) {
+ level.getWireHandler().onWireRemoved(pos, state); // Alternate Current
+ } else {
+ this.updateSurroundingRedstone(world, pos, state, null, false); // Vanilla/Eigencraft
+ this.updateSurroundingRedstone(level, pos, state, null, false); // Vanilla/Eigencraft
+ }
this.updateNeighborsOfNeighboringWires(world, pos);
+ // Paper end - optimize redstone
this.updateNeighborsOfNeighboringWires(level, pos);
}
}
@@ -0,0 +0,0 @@ public class RedStoneWireBlock extends Block {
@@ -415,9 +427,15 @@ public class RedStoneWireBlock extends Block {
@Override
protected void neighborChanged(BlockState state, Level world, BlockPos pos, Block sourceBlock, @Nullable Orientation wireOrientation, boolean notify) {
if (!world.isClientSide) {
protected void neighborChanged(BlockState state, Level level, BlockPos pos, Block neighborBlock, @Nullable Orientation orientation, boolean movedByPiston) {
if (!level.isClientSide) {
+ // Paper start - optimize redstone (Alternate Current)
+ // Alternate Current handles breaking of redstone wires in the WireHandler.
+ if (world.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) {
+ world.getWireHandler().onWireUpdated(pos, state, wireOrientation);
+ if (level.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) {
+ level.getWireHandler().onWireUpdated(pos, state, orientation);
+ } else
+ // Paper end - optimize redstone (Alternate Current)
if (sourceBlock != this || !useExperimentalEvaluator(world)) {
if (state.canSurvive(world, pos)) {
- this.updateSurroundingRedstone(world, pos, state, wireOrientation, false); // Paper - Optimize redstone
+ this.updateSurroundingRedstone(world, pos, state, wireOrientation, false); // Paper - Optimize redstone (Eigencraft)
+ // Paper end - optimize redstone (Alternate Current)
if (neighborBlock != 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)
} else {
dropResources(state, world, pos);
world.removeBlock(pos, false);
diff --git a/src/main/java/net/minecraft/world/level/redstone/ExperimentalRedstoneUtils.java b/src/main/java/net/minecraft/world/level/redstone/ExperimentalRedstoneUtils.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/redstone/ExperimentalRedstoneUtils.java
+++ b/src/main/java/net/minecraft/world/level/redstone/ExperimentalRedstoneUtils.java
@@ -0,0 +0,0 @@ public class ExperimentalRedstoneUtils {
if (up != null) {
orientation = orientation.withFront(up);
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 83f5da3e24834882193b9d7e3a788517e4b12b55..0c50a0bbbef1229230712b7c04364fea06859674 100644
--- a/net/minecraft/world/level/redstone/ExperimentalRedstoneUtils.java
+++ b/net/minecraft/world/level/redstone/ExperimentalRedstoneUtils.java
@@ -17,6 +17,11 @@ public class ExperimentalRedstoneUtils {
if (front != null) {
orientation = orientation.withFront(front);
}
+ // Paper start - Optimize redstone (Alternate Current) - use default front instead of random
+ else if (world.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) {
+ else if (level.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) {
+ orientation = orientation.withFront(Direction.WEST);
+ }
+ // Paper end - Optimize redstone (Alternate Current)

View file

@ -6,15 +6,12 @@ Subject: [PATCH] Improve exact choice recipe ingredients
Fixes exact choices not working with recipe book clicks
and shapeless recipes.
== AT ==
public net.minecraft.world.item.ItemStackLinkedSet TYPE_AND_TAG
diff --git a/src/main/java/io/papermc/paper/inventory/recipe/ItemOrExact.java b/src/main/java/io/papermc/paper/inventory/recipe/ItemOrExact.java
diff --git a/io/papermc/paper/inventory/recipe/ItemOrExact.java b/io/papermc/paper/inventory/recipe/ItemOrExact.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
index 0000000000000000000000000000000000000000..ce745e49cd54fe3ae187785563a1bd311d14b5b2
--- /dev/null
+++ b/src/main/java/io/papermc/paper/inventory/recipe/ItemOrExact.java
@@ -0,0 +0,0 @@
+++ b/io/papermc/paper/inventory/recipe/ItemOrExact.java
@@ -0,0 +1,63 @@
+package io.papermc.paper.inventory.recipe;
+
+import net.minecraft.core.Holder;
@ -78,12 +75,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ }
+ }
+}
diff --git a/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtrasMap.java b/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtrasMap.java
diff --git a/io/papermc/paper/inventory/recipe/StackedContentsExtrasMap.java b/io/papermc/paper/inventory/recipe/StackedContentsExtrasMap.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
index 0000000000000000000000000000000000000000..f47c12e9dd6cfa857ca07a764edc22de372e25b6
--- /dev/null
+++ b/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtrasMap.java
@@ -0,0 +0,0 @@
+++ b/io/papermc/paper/inventory/recipe/StackedContentsExtrasMap.java
@@ -0,0 +1,68 @@
+package io.papermc.paper.inventory.recipe;
+
+import it.unimi.dsi.fastutil.objects.Object2IntMap;
@ -152,92 +149,94 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ return false;
+ }
+}
diff --git a/src/main/java/net/minecraft/recipebook/ServerPlaceRecipe.java b/src/main/java/net/minecraft/recipebook/ServerPlaceRecipe.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/recipebook/ServerPlaceRecipe.java
+++ b/src/main/java/net/minecraft/recipebook/ServerPlaceRecipe.java
@@ -0,0 +0,0 @@ public class ServerPlaceRecipe<R extends Recipe<?>> {
diff --git a/net/minecraft/recipebook/ServerPlaceRecipe.java b/net/minecraft/recipebook/ServerPlaceRecipe.java
index 6475509689439636275b06b9eac33f0922d8fcfd..6c398c91909f42e352e80d0781549df9d834a060 100644
--- a/net/minecraft/recipebook/ServerPlaceRecipe.java
+++ b/net/minecraft/recipebook/ServerPlaceRecipe.java
@@ -40,6 +40,7 @@ public class ServerPlaceRecipe<R extends Recipe<?>> {
return RecipeBookMenu.PostPlaceAction.NOTHING;
} else {
StackedItemContents stackedItemContents = new StackedItemContents();
+ stackedItemContents.initializeExtras(recipe.value(), null); // Paper - Improve exact choice recipe ingredients
inventory.fillStackedContents(stackedItemContents);
handler.fillCraftSlotsStackedContents(stackedItemContents);
menu.fillCraftSlotsStackedContents(stackedItemContents);
return serverPlaceRecipe.tryPlaceRecipe(recipe, stackedItemContents);
@@ -0,0 +0,0 @@ public class ServerPlaceRecipe<R extends Recipe<?>> {
@@ -99,7 +100,7 @@ public class ServerPlaceRecipe<R extends Recipe<?>> {
}
int j = this.calculateAmountToCraft(i, bl);
int i = this.calculateAmountToCraft(biggestCraftableStack, flag);
- List<Holder<Item>> list = new ArrayList<>();
+ List<io.papermc.paper.inventory.recipe.ItemOrExact> list = new ArrayList<>(); // Paper - Improve exact choice recipe ingredients
if (finder.canCraft(recipe.value(), j, list::add)) {
int k = clampToMaxStackSize(j, list);
if (k != j) {
@@ -0,0 +0,0 @@ public class ServerPlaceRecipe<R extends Recipe<?>> {
this.gridWidth, this.gridHeight, recipe.value(), recipe.value().placementInfo().slotsToIngredientIndex(), (slotx, index, x, y) -> {
if (slotx != -1) {
Slot slot2 = this.inputGridSlots.get(index);
- Holder<Item> holder = list.get(slotx);
+ io.papermc.paper.inventory.recipe.ItemOrExact holder = list.get(slotx); // Paper - Improve exact choice recipe ingredients
int jx = k;
if (stackedItemContents.canCraft(recipe.value(), i, list::add)) {
int i1 = clampToMaxStackSize(i, list);
if (i1 != i) {
@@ -114,7 +115,7 @@ public class ServerPlaceRecipe<R extends Recipe<?>> {
this.gridWidth, this.gridHeight, recipe.value(), recipe.value().placementInfo().slotsToIngredientIndex(), (item1, slot1, x, y) -> {
if (item1 != -1) {
Slot slot2 = this.inputGridSlots.get(slot1);
- Holder<Item> holder = list.get(item1);
+ io.papermc.paper.inventory.recipe.ItemOrExact holder = list.get(item1); // Paper - Improve exact choice recipe ingredients
int i2 = i1;
while (jx > 0) {
@@ -0,0 +0,0 @@ public class ServerPlaceRecipe<R extends Recipe<?>> {
while (i2 > 0) {
@@ -129,9 +130,11 @@ public class ServerPlaceRecipe<R extends Recipe<?>> {
}
}
- private static int clampToMaxStackSize(int count, List<Holder<Item>> entries) {
- for (Holder<Item> holder : entries) {
- count = Math.min(count, holder.value().getDefaultMaxStackSize());
- private static int clampToMaxStackSize(int amount, List<Holder<Item>> items) {
- for (Holder<Item> holder : items) {
- amount = Math.min(amount, holder.value().getDefaultMaxStackSize());
+ // Paper start - Improve exact choice recipe ingredients
+ private static int clampToMaxStackSize(int count, List<io.papermc.paper.inventory.recipe.ItemOrExact> entries) {
+ for (io.papermc.paper.inventory.recipe.ItemOrExact holder : entries) {
+ count = Math.min(count, holder.getMaxStackSize());
+ private static int clampToMaxStackSize(int amount, List<io.papermc.paper.inventory.recipe.ItemOrExact> items) {
+ for (io.papermc.paper.inventory.recipe.ItemOrExact holder : items) {
+ amount = Math.min(amount, holder.getMaxStackSize());
+ // Paper end - Improve exact choice recipe ingredients
}
return count;
@@ -0,0 +0,0 @@ public class ServerPlaceRecipe<R extends Recipe<?>> {
return amount;
@@ -160,7 +163,7 @@ public class ServerPlaceRecipe<R extends Recipe<?>> {
}
}
- private int moveItemToGrid(Slot slot, Holder<Item> item, int count) {
+ private int moveItemToGrid(Slot slot, io.papermc.paper.inventory.recipe.ItemOrExact item, int count) { // Paper - Improve exact choice recipe ingredients
ItemStack itemStack = slot.getItem();
int i = this.inventory.findSlotMatchingCraftingIngredient(item, itemStack);
ItemStack item1 = slot.getItem();
int i = this.inventory.findSlotMatchingCraftingIngredient(item, item1);
if (i == -1) {
diff --git a/src/main/java/net/minecraft/world/entity/player/Inventory.java b/src/main/java/net/minecraft/world/entity/player/Inventory.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/player/Inventory.java
+++ b/src/main/java/net/minecraft/world/entity/player/Inventory.java
@@ -0,0 +0,0 @@ public class Inventory implements Container, Nameable {
diff --git a/net/minecraft/world/entity/player/Inventory.java b/net/minecraft/world/entity/player/Inventory.java
index 8a29d771046667db22fba166fa5337de1896cd0d..839cbb67d3d38960d9114a4db5bab911b66a573c 100644
--- a/net/minecraft/world/entity/player/Inventory.java
+++ b/net/minecraft/world/entity/player/Inventory.java
@@ -185,12 +185,12 @@ public class Inventory implements Container, Nameable {
return !stack.isDamaged() && !stack.isEnchanted() && !stack.has(DataComponents.CUSTOM_NAME);
}
- public int findSlotMatchingCraftingIngredient(Holder<Item> item, ItemStack stack) {
+ public int findSlotMatchingCraftingIngredient(io.papermc.paper.inventory.recipe.ItemOrExact item, ItemStack stack) { // Paper - Improve exact choice recipe ingredients
for (int i = 0; i < this.items.size(); ++i) {
ItemStack itemstack1 = (ItemStack) this.items.get(i);
- if (!itemstack1.isEmpty() && itemstack1.is(item) && Inventory.isUsableForCrafting(itemstack1) && (stack.isEmpty() || ItemStack.isSameItemSameComponents(stack, itemstack1))) {
+ if (!itemstack1.isEmpty() && item.matches(itemstack1) && (!(item instanceof io.papermc.paper.inventory.recipe.ItemOrExact.Item) || Inventory.isUsableForCrafting(itemstack1)) && (stack.isEmpty() || ItemStack.isSameItemSameComponents(stack, itemstack1))) { // Paper - Improve exact choice recipe ingredients
for (int i = 0; i < this.items.size(); i++) {
ItemStack itemStack = this.items.get(i);
if (!itemStack.isEmpty()
- && itemStack.is(item)
- && isUsableForCrafting(itemStack)
+ && item.matches(itemStack) // Paper - Improve exact choice recipe ingredients
+ && (!(item instanceof io.papermc.paper.inventory.recipe.ItemOrExact.Item) || Inventory.isUsableForCrafting(itemStack)) // Paper - Improve exact choice recipe ingredients
&& (stack.isEmpty() || ItemStack.isSameItemSameComponents(stack, itemStack))) {
return i;
}
}
diff --git a/src/main/java/net/minecraft/world/entity/player/StackedContents.java b/src/main/java/net/minecraft/world/entity/player/StackedContents.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/player/StackedContents.java
+++ b/src/main/java/net/minecraft/world/entity/player/StackedContents.java
@@ -0,0 +0,0 @@ import java.util.List;
diff --git a/net/minecraft/world/entity/player/StackedContents.java b/net/minecraft/world/entity/player/StackedContents.java
index a4b528574ab371af94b0e07819e471cec94da244..a3fea6c8397046596afe3c8b5589f2ed37fcdfc3 100644
--- a/net/minecraft/world/entity/player/StackedContents.java
+++ b/net/minecraft/world/entity/player/StackedContents.java
@@ -13,7 +13,7 @@ import java.util.List;
import javax.annotation.Nullable;
public class StackedContents<T> {
- public final Reference2IntOpenHashMap<T> amounts = new Reference2IntOpenHashMap<>();
+ public final it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap<T> amounts = new it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap<>(); // Paper - Improve exact choice recipe ingredients (don't use "reference" map)
boolean hasAtLeast(T input, int minimum) {
return this.amounts.getInt(input) >= minimum;
@@ -0,0 +0,0 @@ public class StackedContents<T> {
boolean hasAtLeast(T item, int amount) {
return this.amounts.getInt(item) >= amount;
@@ -49,7 +49,7 @@ public class StackedContents<T> {
List<T> getUniqueAvailableIngredientItems(Iterable<? extends StackedContents.IngredientInfo<T>> ingredients) {
List<T> list = new ArrayList<>();
@ -246,7 +245,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
if (entry.getIntValue() > 0 && anyIngredientMatches(ingredients, entry.getKey())) {
list.add(entry.getKey());
}
@@ -0,0 +0,0 @@ public class StackedContents<T> {
@@ -71,13 +71,13 @@ public class StackedContents<T> {
@VisibleForTesting
public int getResultUpperBound(List<? extends StackedContents.IngredientInfo<T>> ingredients) {
int i = Integer.MAX_VALUE;
@ -255,18 +254,18 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
label31:
for (StackedContents.IngredientInfo<T> ingredientInfo : ingredients) {
int j = 0;
int i1 = 0;
- for (Entry<T> entry : objectIterable) {
+ for (it.unimi.dsi.fastutil.objects.Object2IntMap.Entry<T> entry : objectIterable) { // Paper - Improve exact choice recipe ingredients (don't use "reference" map)
int k = entry.getIntValue();
if (k > j) {
int intValue = entry.getIntValue();
if (intValue > i1) {
if (ingredientInfo.acceptsItem(entry.getKey())) {
diff --git a/src/main/java/net/minecraft/world/entity/player/StackedItemContents.java b/src/main/java/net/minecraft/world/entity/player/StackedItemContents.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/player/StackedItemContents.java
+++ b/src/main/java/net/minecraft/world/entity/player/StackedItemContents.java
@@ -0,0 +0,0 @@ import net.minecraft.world.item.crafting.PlacementInfo;
diff --git a/net/minecraft/world/entity/player/StackedItemContents.java b/net/minecraft/world/entity/player/StackedItemContents.java
index 6bbe2e51ef71d193e0a5d3cace2b0ad1760ce759..83ccde54c625d40dc595e000c533f60aa929bd5a 100644
--- a/net/minecraft/world/entity/player/StackedItemContents.java
+++ b/net/minecraft/world/entity/player/StackedItemContents.java
@@ -9,9 +9,14 @@ import net.minecraft.world.item.crafting.PlacementInfo;
import net.minecraft.world.item.crafting.Recipe;
public class StackedItemContents {
@ -274,25 +273,30 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper start - Improve exact choice recipe ingredients
+ private final StackedContents<io.papermc.paper.inventory.recipe.ItemOrExact> raw = new StackedContents<>();
+ @Nullable
+ private io.papermc.paper.inventory.recipe.StackedContentsExtrasMap extrasMap = null;
+ private io.papermc.paper.inventory.recipe.StackedContentsExtrasMap extrasMap;
+ // Paper start - Improve exact choice recipe ingredients
public void accountSimpleStack(ItemStack item) {
+ if (this.extrasMap != null && this.extrasMap.accountStack(item, Math.min(64, item.getCount()))) return; // Paper - Improve exact choice recipe ingredients; max of 64 due to accountStack method below
if (Inventory.isUsableForCrafting(item)) {
this.accountStack(item);
public void accountSimpleStack(ItemStack stack) {
+ if (this.extrasMap != null && this.extrasMap.accountStack(stack, Math.min(64, stack.getCount()))) return; // Paper - Improve exact choice recipe ingredients; max of 64 due to accountStack method below
if (Inventory.isUsableForCrafting(stack)) {
this.accountStack(stack);
}
@@ -0,0 +0,0 @@ public class StackedItemContents {
public void accountStack(ItemStack item, int maxCount) {
if (!item.isEmpty()) {
int i = Math.min(maxCount, item.getCount());
- this.raw.account(item.getItemHolder(), i);
+ if (this.extrasMap != null && !item.getComponentsPatch().isEmpty() && this.extrasMap.accountStack(item, i)) return; // Paper - Improve exact choice recipe ingredients; if an exact ingredient, don't include it
+ this.raw.account(new io.papermc.paper.inventory.recipe.ItemOrExact.Item(item.getItemHolder()), i);
@@ -24,34 +29,51 @@ public class StackedItemContents {
public void accountStack(ItemStack stack, int maxStackSize) {
if (!stack.isEmpty()) {
int min = Math.min(maxStackSize, stack.getCount());
- this.raw.account(stack.getItemHolder(), min);
+ if (this.extrasMap != null && !stack.getComponentsPatch().isEmpty() && this.extrasMap.accountStack(stack, min)) return; // Paper - Improve exact choice recipe ingredients; if an exact ingredient, don't include it
+ this.raw.account(new io.papermc.paper.inventory.recipe.ItemOrExact.Item(stack.getItemHolder()), min);
}
}
- public boolean canCraft(Recipe<?> recipe, @Nullable StackedContents.Output<Holder<Item>> itemCallback) {
- public boolean canCraft(Recipe<?> recipe, @Nullable StackedContents.Output<Holder<Item>> output) {
+ public boolean canCraft(Recipe<?> recipe, @Nullable StackedContents.Output<io.papermc.paper.inventory.recipe.ItemOrExact> output) { // Paper - Improve exact choice recipe ingredients
return this.canCraft(recipe, 1, output);
}
- public boolean canCraft(Recipe<?> recipe, int maxCount, @Nullable StackedContents.Output<Holder<Item>> output) {
+ // Paper start - Improve exact choice recipe ingredients
+ public void initializeExtras(final Recipe<?> recipe, @Nullable final net.minecraft.world.item.crafting.CraftingInput input) {
+ if (this.extrasMap == null) {
@ -309,70 +313,64 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ }
+ // Paper end - Improve exact choice recipe ingredients
+
+ public boolean canCraft(Recipe<?> recipe, @Nullable StackedContents.Output<io.papermc.paper.inventory.recipe.ItemOrExact> itemCallback) { // Paper - Improve exact choice recipe ingredients
return this.canCraft(recipe, 1, itemCallback);
}
- public boolean canCraft(Recipe<?> recipe, int quantity, @Nullable StackedContents.Output<Holder<Item>> itemCallback) {
+ public boolean canCraft(Recipe<?> recipe, int quantity, @Nullable StackedContents.Output<io.papermc.paper.inventory.recipe.ItemOrExact> itemCallback) { // Paper - Improve exact choice recipe ingredients
+ public boolean canCraft(Recipe<?> recipe, int maxCount, @Nullable StackedContents.Output<io.papermc.paper.inventory.recipe.ItemOrExact> output) { // Paper - Improve exact choice recipe ingredients
PlacementInfo placementInfo = recipe.placementInfo();
return !placementInfo.isImpossibleToPlace() && this.canCraft(placementInfo.ingredients(), quantity, itemCallback);
return !placementInfo.isImpossibleToPlace() && this.canCraft(placementInfo.ingredients(), maxCount, output);
}
public boolean canCraft(
- List<? extends StackedContents.IngredientInfo<Holder<Item>>> rawIngredients, @Nullable StackedContents.Output<Holder<Item>> itemCallback
+ List<? extends StackedContents.IngredientInfo<io.papermc.paper.inventory.recipe.ItemOrExact>> rawIngredients, @Nullable StackedContents.Output<io.papermc.paper.inventory.recipe.ItemOrExact> itemCallback // Paper - Improve exact choice recipe ingredients
) {
return this.canCraft(rawIngredients, 1, itemCallback);
- public boolean canCraft(List<? extends StackedContents.IngredientInfo<Holder<Item>>> ingredients, @Nullable StackedContents.Output<Holder<Item>> output) {
+ public boolean canCraft(List<? extends StackedContents.IngredientInfo<io.papermc.paper.inventory.recipe.ItemOrExact>> ingredients, @Nullable StackedContents.Output<io.papermc.paper.inventory.recipe.ItemOrExact> output) { // Paper - Improve exact choice recipe ingredients
return this.canCraft(ingredients, 1, output);
}
private boolean canCraft(
- List<? extends StackedContents.IngredientInfo<Holder<Item>>> rawIngredients, int quantity, @Nullable StackedContents.Output<Holder<Item>> itemCallback
+ List<? extends StackedContents.IngredientInfo<io.papermc.paper.inventory.recipe.ItemOrExact>> rawIngredients, int quantity, @Nullable StackedContents.Output<io.papermc.paper.inventory.recipe.ItemOrExact> itemCallback // Paper - Improve exact choice recipe ingredients
- List<? extends StackedContents.IngredientInfo<Holder<Item>>> ingredients, int maxCount, @Nullable StackedContents.Output<Holder<Item>> output
+ List<? extends StackedContents.IngredientInfo<io.papermc.paper.inventory.recipe.ItemOrExact>> ingredients, int maxCount, @Nullable StackedContents.Output<io.papermc.paper.inventory.recipe.ItemOrExact> output // Paper - Improve exact choice recipe ingredients
) {
return this.raw.tryPick(rawIngredients, quantity, itemCallback);
return this.raw.tryPick(ingredients, maxCount, output);
}
- public int getBiggestCraftableStack(Recipe<?> recipe, @Nullable StackedContents.Output<Holder<Item>> itemCallback) {
+ public int getBiggestCraftableStack(Recipe<?> recipe, @Nullable StackedContents.Output<io.papermc.paper.inventory.recipe.ItemOrExact> itemCallback) { // Paper - Improve exact choice recipe ingredients
return this.getBiggestCraftableStack(recipe, Integer.MAX_VALUE, itemCallback);
- public int getBiggestCraftableStack(Recipe<?> recipe, @Nullable StackedContents.Output<Holder<Item>> output) {
+ public int getBiggestCraftableStack(Recipe<?> recipe, @Nullable StackedContents.Output<io.papermc.paper.inventory.recipe.ItemOrExact> output) { // Paper - Improve exact choice recipe ingredients
return this.getBiggestCraftableStack(recipe, Integer.MAX_VALUE, output);
}
- public int getBiggestCraftableStack(Recipe<?> recipe, int max, @Nullable StackedContents.Output<Holder<Item>> itemCallback) {
+ public int getBiggestCraftableStack(Recipe<?> recipe, int max, @Nullable StackedContents.Output<io.papermc.paper.inventory.recipe.ItemOrExact> itemCallback) { // Paper - Improve exact choice recipe ingredients
return this.raw.tryPickAll(recipe.placementInfo().ingredients(), max, itemCallback);
- public int getBiggestCraftableStack(Recipe<?> recipe, int maxCount, @Nullable StackedContents.Output<Holder<Item>> output) {
+ public int getBiggestCraftableStack(Recipe<?> recipe, int maxCount, @Nullable StackedContents.Output<io.papermc.paper.inventory.recipe.ItemOrExact> output) { // Paper - Improve exact choice recipe ingredients
return this.raw.tryPickAll(recipe.placementInfo().ingredients(), maxCount, output);
}
diff --git a/src/main/java/net/minecraft/world/item/crafting/Ingredient.java b/src/main/java/net/minecraft/world/item/crafting/Ingredient.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/item/crafting/Ingredient.java
+++ b/src/main/java/net/minecraft/world/item/crafting/Ingredient.java
@@ -0,0 +0,0 @@ import java.util.List;
import javax.annotation.Nullable;
// CraftBukkit end
diff --git a/net/minecraft/world/item/crafting/Ingredient.java b/net/minecraft/world/item/crafting/Ingredient.java
index e43641650d66a62b5b7b58c43833ce504970ab1e..879c8fe1f20decc793cfa39e686b61d521bd76ba 100644
--- a/net/minecraft/world/item/crafting/Ingredient.java
+++ b/net/minecraft/world/item/crafting/Ingredient.java
@@ -21,7 +21,7 @@ import net.minecraft.world.item.Items;
import net.minecraft.world.item.crafting.display.SlotDisplay;
import net.minecraft.world.level.ItemLike;
-public final class Ingredient implements StackedContents.IngredientInfo<Holder<Item>>, Predicate<ItemStack> {
+public final class Ingredient implements StackedContents.IngredientInfo<io.papermc.paper.inventory.recipe.ItemOrExact>, Predicate<ItemStack> { // Paper - Improve exact choice recipe ingredients
public static final StreamCodec<RegistryFriendlyByteBuf, Ingredient> CONTENTS_STREAM_CODEC = ByteBufCodecs.holderSet(Registries.ITEM).map(Ingredient::new, (recipeitemstack) -> {
return recipeitemstack.values;
@@ -0,0 +0,0 @@ public final class Ingredient implements StackedContents.IngredientInfo<Holder<I
public static final StreamCodec<RegistryFriendlyByteBuf, Ingredient> CONTENTS_STREAM_CODEC = ByteBufCodecs.holderSet(Registries.ITEM)
.map(Ingredient::new, ingredient -> ingredient.values);
public static final StreamCodec<RegistryFriendlyByteBuf, Optional<Ingredient>> OPTIONAL_CONTENTS_STREAM_CODEC = ByteBufCodecs.holderSet(Registries.ITEM)
@@ -35,20 +35,24 @@ public final class Ingredient implements StackedContents.IngredientInfo<Holder<I
private final HolderSet<Item> values;
// CraftBukkit start
@Nullable
- private List<ItemStack> itemStacks;
@javax.annotation.Nullable
- private java.util.List<ItemStack> itemStacks;
+ private java.util.Set<ItemStack> itemStacks; // Paper - Improve exact choice recipe ingredients
public boolean isExact() {
return this.itemStacks != null;
}
- public List<ItemStack> itemStacks() {
@javax.annotation.Nullable
- public java.util.List<ItemStack> itemStacks() {
+ public java.util.Set<ItemStack> itemStacks() { // Paper - Improve exact choice recipe ingredients
return this.itemStacks;
}
public static Ingredient ofStacks(List<ItemStack> stacks) {
public static Ingredient ofStacks(java.util.List<ItemStack> stacks) {
Ingredient recipe = Ingredient.of(stacks.stream().map(ItemStack::getItem));
- recipe.itemStacks = stacks;
+ // Paper start - Improve exact choice recipe ingredients
@ -383,29 +381,29 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
return recipe;
}
// CraftBukkit end
@@ -0,0 +0,0 @@ public final class Ingredient implements StackedContents.IngredientInfo<Holder<I
public boolean test(ItemStack itemstack) {
@@ -81,21 +85,22 @@ public final class Ingredient implements StackedContents.IngredientInfo<Holder<I
public boolean test(ItemStack stack) {
// CraftBukkit start
if (this.isExact()) {
- for (ItemStack itemstack1 : this.itemStacks()) {
- if (itemstack1.getItem() == itemstack.getItem() && ItemStack.isSameItemSameComponents(itemstack, itemstack1)) {
- if (itemstack1.getItem() == stack.getItem() && ItemStack.isSameItemSameComponents(stack, itemstack1)) {
- return true;
- }
- }
-
- return false;
+ return this.itemStacks.contains(itemstack); // Paper - Improve exact choice recipe ingredients (hashing FTW!)
+ return this.itemStacks.contains(stack); // Paper - Improve exact choice recipe ingredients (hashing FTW!)
}
// CraftBukkit end
return itemstack.is(this.values);
return stack.is(this.values);
}
- public boolean acceptsItem(Holder<Item> holder) {
- return this.values.contains(holder);
+ // Paper start - Improve exact choice recipe ingredients
+ @Override
+ public boolean acceptsItem(final io.papermc.paper.inventory.recipe.ItemOrExact holder) {
+ return switch (holder) {
@Override
- public boolean acceptsItem(Holder<Item> item) {
- return this.values.contains(item);
+ public boolean acceptsItem(final io.papermc.paper.inventory.recipe.ItemOrExact itemOrExact) {
+ return switch (itemOrExact) {
+ case io.papermc.paper.inventory.recipe.ItemOrExact.Item(final Holder<Item> item) ->
+ !this.isExact() && this.values.contains(item);
+ case io.papermc.paper.inventory.recipe.ItemOrExact.Exact(final ItemStack exact) ->
@ -414,8 +412,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper end - Improve exact choice recipe ingredients
}
public boolean equals(Object object) {
@@ -0,0 +0,0 @@ public final class Ingredient implements StackedContents.IngredientInfo<Holder<I
@Override
@@ -120,6 +125,11 @@ public final class Ingredient implements StackedContents.IngredientInfo<Holder<I
}
public SlotDisplay display() {
@ -424,22 +422,25 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ return new SlotDisplay.Composite(this.itemStacks().stream().<SlotDisplay>map(SlotDisplay.ItemStackSlotDisplay::new).toList());
+ }
+ // Paper end - show exact ingredients in recipe book
return (SlotDisplay) this.values.unwrap().map(SlotDisplay.TagSlotDisplay::new, (list) -> {
return new SlotDisplay.Composite(list.stream().map(Ingredient::displayForSingleItem).toList());
});
diff --git a/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java b/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java
+++ b/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java
@@ -0,0 +0,0 @@ public class ShapelessRecipe implements CraftingRecipe {
}
return (SlotDisplay)this.values
.unwrap()
.map(SlotDisplay.TagSlotDisplay::new, list -> new SlotDisplay.Composite(list.stream().map(Ingredient::displayForSingleItem).toList()));
diff --git a/net/minecraft/world/item/crafting/ShapelessRecipe.java b/net/minecraft/world/item/crafting/ShapelessRecipe.java
index fb317eafeed39adff793bffa8f6b21c37a32086c..d601b54b1de2f2ae44fe2b20c8116c71a6340e45 100644
--- a/net/minecraft/world/item/crafting/ShapelessRecipe.java
+++ b/net/minecraft/world/item/crafting/ShapelessRecipe.java
@@ -72,13 +72,18 @@ public class ShapelessRecipe implements CraftingRecipe {
public boolean matches(CraftingInput input, Level world) {
- return input.ingredientCount() != this.ingredients.size() ? false : (input.size() == 1 && this.ingredients.size() == 1 ? ((Ingredient) this.ingredients.getFirst()).test(input.getItem(0)) : input.stackedContents().canCraft((Recipe) this, (StackedContents.Output) null));
@Override
public boolean matches(CraftingInput input, Level level) {
+ // Paper start - Improve exact choice recipe ingredients & unwrap ternary
+ if (input.ingredientCount() != this.ingredients.size()) {
+ return false;
+ }
if (input.ingredientCount() != this.ingredients.size()) {
return false;
- } else {
- return input.size() == 1 && this.ingredients.size() == 1
- ? this.ingredients.getFirst().test(input.getItem(0))
- : input.stackedContents().canCraft(this, null);
}
+ if (input.size() == 1 && this.ingredients.size() == 1) {
+ return this.ingredients.getFirst().test(input.getItem(0));
+ }
@ -450,4 +451,4 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper end - Improve exact choice recipe ingredients & unwrap ternary
}
public ItemStack assemble(CraftingInput input, HolderLookup.Provider registries) {
@Override

View file

@ -7,22 +7,22 @@ Subject: [PATCH] Only write chunk data to disk if it serializes without
This ensures at least a valid version of the chunk exists
on disk, even if outdated
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
diff --git a/net/minecraft/world/level/chunk/storage/RegionFile.java b/net/minecraft/world/level/chunk/storage/RegionFile.java
index 7da388ffab162c282cad0f297bb7304f3c2abbaf..ff4fc280409f680f3879a495e37cf1925b1a38f1 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;
}
// Paper end
public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patches.chunk_system.storage.ChunkSystemRegionFile { // Paper - rewrite chunk system
private static final Logger LOGGER = LogUtils.getLogger();
+ public static final int MAX_CHUNK_SIZE = 500 * 1024 * 1024; // Paper - don't write garbage data to disk if writing serialization fails
private class ChunkBuffer extends ByteArrayOutputStream implements ca.spottedleaf.moonrise.patches.chunk_system.storage.ChunkSystemChunkBuffer { // Paper - rewrite chunk system
private final ChunkPos pos;
@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
super.write(RegionFile.this.version.getId());
this.pos = chunkcoordintpair;
private static final int SECTOR_BYTES = 4096;
@VisibleForTesting
protected static final int SECTOR_INTS = 1024;
@@ -455,6 +456,24 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
this.pos = pos;
}
+ // Paper start - don't write garbage data to disk if writing serialization fails
+ @Override
+ public void write(final int b) {
@ -40,23 +40,23 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ super.write(b, off, len);
+ }
+ // Paper end - don't write garbage data to disk if writing serialization fails
+
@Override
public void close() throws IOException {
ByteBuffer bytebuffer = ByteBuffer.wrap(this.buf, 0, this.count);
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
@@ -0,0 +0,0 @@ import net.minecraft.world.level.ChunkPos;
ByteBuffer byteBuffer = 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 e35bb5534e2fbd2e30154a15ff6d39baa121608f..d263f78fa610ce6f6fb5a0f5e064e3d8335c2199 100644
--- a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+++ b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
@@ -15,6 +15,7 @@ import net.minecraft.util.ExceptionCollector;
import net.minecraft.world.level.ChunkPos;
public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise.patches.chunk_system.io.ChunkSystemRegionFileStorage { // Paper - rewrite chunk system
+ private static final org.slf4j.Logger LOGGER = com.mojang.logging.LogUtils.getLogger(); // Paper
+
public static final String ANVIL_EXTENSION = ".mca";
private static final int MAX_CACHE_SIZE = 256;
public final Long2ObjectLinkedOpenHashMap<RegionFile> regionCache = new Long2ObjectLinkedOpenHashMap();
@@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
public final Long2ObjectLinkedOpenHashMap<RegionFile> regionCache = new Long2ObjectLinkedOpenHashMap<>();
@@ -119,11 +120,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,39 +81,26 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
return writeData;
}
@@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
try {
NbtIo.write(nbt, (DataOutput) dataoutputstream);
regionfile.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
@@ -326,9 +340,17 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
if (chunkData == null) {
regionFile.clear(chunkPos);
} 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
+ dataoutputstream.close(); // Only write if successful
+ chunkDataOutputStream.close();
+ } catch (final RegionFileSizeException ex) {
+ regionfile.clear(pos);
+ regionFile.clear(chunkPos);
+ final int maxSize = RegionFile.MAX_CHUNK_SIZE / (1024 * 1024);
+ LOGGER.error("Chunk at (" + pos.x + "," + pos.z + ") in regionfile '" + regionfile.getPath().toString() + "' exceeds max size of " + maxSize + "MiB, it has been deleted from disk.");
+ return;
+ 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
} catch (Throwable throwable) {
if (dataoutputstream != null) {
try {
- dataoutputstream.close();
+ //dataoutputstream.close(); // Paper - don't write garbage data to disk if writing serialization fails
} catch (Throwable throwable1) {
throwable.addSuppressed(throwable1);
}
@@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
throw throwable;
}
-
- if (dataoutputstream != null) {
- dataoutputstream.close();
- }
+ // Paper - don't write garbage data to disk if writing serialization fails; move into try block to only write if successfully serialized
}
}
@@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
@@ -370,4 +392,13 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
public RegionStorageInfo info() {
return this.info;
}
@ -121,7 +108,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper start - don't write garbage data to disk if writing serialization fails
+ public static final class RegionFileSizeException extends RuntimeException {
+
+ public RegionFileSizeException(String message) {
+ public RegionFileSizeException(final String message) {
+ super(message);
+ }
+ }

View file

@ -8,11 +8,11 @@ to a chunk. The default values of -1 disable the limit. Although
defaults are only included for certain entites, this allows setting
limits for any entity type.
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java
@@ -0,0 +0,0 @@ public final class ChunkEntitySlices {
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 7aea4e343581b977d11af90f9f65eac3532eade1..d21ce54ebb5724c04eadf56a2cde701d5eeb5db2 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java
@@ -104,7 +104,18 @@ public final class ChunkEntitySlices {
}
final ListTag entitiesTag = new ListTag();
@ -31,21 +31,21 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
CompoundTag compoundTag = new CompoundTag();
if (entity.save(compoundTag)) {
entitiesTag.add(compoundTag);
diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/EntityType.java
+++ b/src/main/java/net/minecraft/world/entity/EntityType.java
@@ -0,0 +0,0 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT
final Spliterator<? extends Tag> spliterator = entityNbtList.spliterator();
diff --git a/net/minecraft/world/entity/EntityType.java b/net/minecraft/world/entity/EntityType.java
index 73cdfa5a315ed259b38dfa946a0b7955d9ac9f50..49201d6664656ebe34c84c1c84b5ea4878729062 100644
--- a/net/minecraft/world/entity/EntityType.java
+++ b/net/minecraft/world/entity/EntityType.java
@@ -1420,9 +1420,20 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT
public static Stream<Entity> loadEntitiesRecursive(final List<? extends Tag> entityTags, final Level level, final EntitySpawnReason spawnReason) {
final Spliterator<? extends Tag> spliterator = entityTags.spliterator();
return StreamSupport.stream(new Spliterator<Entity>() {
+ final java.util.Map<EntityType<?>, Integer> loadedEntityCounts = new java.util.HashMap<>(); // Paper - Entity load/save limit per chunk
@Override
public boolean tryAdvance(Consumer<? super Entity> consumer) {
return spliterator.tryAdvance((nbtbase) -> {
EntityType.loadEntityRecursive((CompoundTag) nbtbase, world, reason, (entity) -> {
return spliterator.tryAdvance(tag -> EntityType.loadEntityRecursive((CompoundTag)tag, level, spawnReason, entity -> {
+ // Paper start - Entity load/save limit per chunk
+ final EntityType<?> entityType = entity.getType();
+ final int saveLimit = world.paperConfig().chunks.entityPerChunkSaveLimit.getOrDefault(entityType, -1);
+ final int saveLimit = level.paperConfig().chunks.entityPerChunkSaveLimit.getOrDefault(entityType, -1);
+ if (saveLimit > -1) {
+ if (this.loadedEntityCounts.getOrDefault(entityType, 0) >= saveLimit) {
+ return null;
@ -53,19 +53,19 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ this.loadedEntityCounts.merge(entityType, 1, Integer::sum);
+ }
+ // Paper end - Entity load/save limit per chunk
consumer.accept(entity);
return entity;
});
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
@@ -0,0 +0,0 @@ public class EntityStorage implements EntityPersistentStorage<Entity> {
consumer.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 da05fb780c55381a7a08ced51d01764a645740b2..2856206eafddfcbcc1b65408deda40357f43a6f8 100644
--- a/net/minecraft/world/level/chunk/storage/EntityStorage.java
+++ b/net/minecraft/world/level/chunk/storage/EntityStorage.java
@@ -93,7 +93,18 @@ public class EntityStorage implements EntityPersistentStorage<Entity> {
}
} else {
ListTag listTag = new ListTag();
+ final java.util.Map<net.minecraft.world.entity.EntityType<?>, Integer> savedEntityCounts = new java.util.HashMap<>(); // Paper - Entity load/save limit per chunk
dataList.getEntities().forEach(entity -> {
entities.getEntities().forEach(entity -> {
+ // Paper start - Entity load/save limit per chunk
+ final EntityType<?> entityType = entity.getType();
+ final int saveLimit = this.level.paperConfig().chunks.entityPerChunkSaveLimit.getOrDefault(entityType, -1);
@ -76,6 +76,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ savedEntityCounts.merge(entityType, 1, Integer::sum);
+ }
+ // Paper end - Entity load/save limit per chunk
CompoundTag compoundTagx = new CompoundTag();
if (entity.save(compoundTagx)) {
listTag.add(compoundTagx);
CompoundTag compoundTag1 = new CompoundTag();
if (entity.save(compoundTag1)) {
listTag.add(compoundTag1);

View file

@ -9,11 +9,11 @@ we instead drop the current regionfile header and recalculate -
hoping that at least then we don't swap chunks, and maybe recover
them all.
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionBitmap.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionBitmap.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionBitmap.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionBitmap.java
@@ -0,0 +0,0 @@ import java.util.BitSet;
diff --git a/net/minecraft/world/level/chunk/storage/RegionBitmap.java b/net/minecraft/world/level/chunk/storage/RegionBitmap.java
index 64a718c98f799c62a5bb28e1e8e5f66cc96c915d..666f2e967c99f78422c83fb20e1a3bf3efa7845e 100644
--- a/net/minecraft/world/level/chunk/storage/RegionBitmap.java
+++ b/net/minecraft/world/level/chunk/storage/RegionBitmap.java
@@ -9,6 +9,27 @@ import java.util.BitSet;
public class RegionBitmap {
private final BitSet used = new BitSet();
@ -38,17 +38,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ }
+ // Paper end - Attempt to recalculate regionfile header if it is corrupt
+
public void force(int start, int size) {
this.used.set(start, start + size);
public void force(int sectorOffset, int sectorCount) {
this.used.set(sectorOffset, sectorOffset + sectorCount);
}
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
private final IntBuffer timestamps;
diff --git a/net/minecraft/world/level/chunk/storage/RegionFile.java b/net/minecraft/world/level/chunk/storage/RegionFile.java
index ff4fc280409f680f3879a495e37cf1925b1a38f1..a4621c96fd456c5cdd1b6847931806e677b26b30 100644
--- a/net/minecraft/world/level/chunk/storage/RegionFile.java
+++ b/net/minecraft/world/level/chunk/storage/RegionFile.java
@@ -46,6 +46,355 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
@VisibleForTesting
protected final RegionBitmap usedSectors;
protected final RegionBitmap usedSectors = new RegionBitmap();
+ // Paper start - Attempt to recalculate regionfile header if it is corrupt
+ private static long roundToSectors(long bytes) {
+ long sectors = bytes >>> 12; // 4096 = 2^12
@ -57,9 +57,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ return sectors + (sign >>> 63);
+ }
+
+ private static final CompoundTag OVERSIZED_COMPOUND = new CompoundTag();
+ private static final net.minecraft.nbt.CompoundTag OVERSIZED_COMPOUND = new net.minecraft.nbt.CompoundTag();
+
+ private CompoundTag attemptRead(long sector, int chunkDataLength, long fileLength) throws IOException {
+ private @Nullable net.minecraft.nbt.CompoundTag attemptRead(long sector, int chunkDataLength, long fileLength) throws IOException {
+ try {
+ if (chunkDataLength < 0) {
+ return null;
@ -91,7 +91,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+
+ InputStream input = compression.wrap(new ByteArrayInputStream(chunkData.array(), chunkData.position(), chunkDataLength - chunkData.position()));
+
+ return NbtIo.read(new DataInputStream(input));
+ return net.minecraft.nbt.NbtIo.read(new DataInputStream(input));
+ } catch (Exception ex) {
+ return null;
+ }
@ -142,7 +142,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // try to backup file so maybe it could be sent to us for further investigation
+
+ this.backupRegionFile();
+ CompoundTag[] compounds = new CompoundTag[32 * 32]; // only in the regionfile (i.e exclude mojang/aikar oversized data)
+ net.minecraft.nbt.CompoundTag[] compounds = new net.minecraft.nbt.CompoundTag[32 * 32]; // only in the regionfile (i.e exclude mojang/aikar oversized data)
+ int[] rawLengths = new int[32 * 32]; // length of chunk data including 4 byte length field, bytes
+ int[] sectorOffsets = new int[32 * 32]; // in sectors
+ boolean[] hasAikarOversized = new boolean[32 * 32];
@ -154,7 +154,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+
+ for (long i = 2, maxSector = Math.min((long)(Integer.MAX_VALUE >>> 8), totalSectors); i < maxSector; ++i) { // first two sectors are header, skip
+ int chunkDataLength = this.getLength(i);
+ CompoundTag compound = this.attemptRead(i, chunkDataLength, fileLength);
+ net.minecraft.nbt.CompoundTag compound = this.attemptRead(i, chunkDataLength, fileLength);
+ if (compound == null || compound == OVERSIZED_COMPOUND) {
+ continue;
+ }
@ -166,7 +166,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ }
+ int location = (chunkPos.x & 31) | ((chunkPos.z & 31) << 5);
+
+ CompoundTag otherCompound = compounds[location];
+ net.minecraft.nbt.CompoundTag otherCompound = compounds[location];
+
+ if (otherCompound != null && SerializableChunkData.getLastWorldSaveTime(otherCompound) > SerializableChunkData.getLastWorldSaveTime(compound)) {
+ continue; // don't overwrite newer data.
@ -177,7 +177,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ boolean isAikarOversized = false;
+ if (Files.exists(aikarOversizedFile)) {
+ try {
+ 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;
@ -236,14 +236,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ continue;
+ }
+
+ CompoundTag compound = null;
+ net.minecraft.nbt.CompoundTag compound = null;
+
+ // We do not know the compression type, as it's stored in the regionfile. So we need to try all of them
+ RegionFileVersion compression = null;
+ for (RegionFileVersion compressionType : RegionFileVersion.VERSIONS.values()) {
+ try {
+ DataInputStream in = new DataInputStream(compressionType.wrap(new ByteArrayInputStream(chunkData))); // typical java
+ compound = NbtIo.read((java.io.DataInput)in);
+ compound = net.minecraft.nbt.NbtIo.read((java.io.DataInput)in);
+ compression = compressionType;
+ break; // reaches here iff readNBT does not throw
+ } catch (Exception ex) {
@ -397,63 +397,55 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+
+ final boolean canRecalcHeader; // final forces compile fail on new constructor
+ // Paper end - Attempt to recalculate regionfile header if it is corrupt
+
// Paper start - rewrite chunk system
@Override
@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
throw new IllegalArgumentException("Expected directory, got " + String.valueOf(directory.toAbsolutePath()));
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 +423,7 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
throw new IllegalArgumentException("Expected directory, got " + externalFileDir.toAbsolutePath());
} else {
this.externalFileDir = directory;
this.externalFileDir = externalFileDir;
+ this.canRecalcHeader = RegionFileStorage.isChunkDataFolder(this.externalFileDir); // Paper - add can recalc flag
this.offsets = this.header.asIntBuffer();
((java.nio.Buffer) this.offsets).limit(1024); // CraftBukkit - decompile error
((java.nio.Buffer) this.header).position(4096); // CraftBukkit - decompile error
@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
RegionFile.LOGGER.warn("Region file {} has truncated header: {}", path, i);
}
this.offsets.limit(1024);
this.header.position(4096);
@@ -94,11 +444,13 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
- long j = Files.size(path);
+ final long j = Files.size(path); final long regionFileSize = j; // Paper - recalculate header on header corruption
long size = Files.size(path);
- for (int k = 0; k < 1024; ++k) {
- int l = this.offsets.get(k);
- for (int i1 = 0; i1 < 1024; i1++) {
+ boolean needsHeaderRecalc = false; // Paper - recalculate header on header corruption
+ boolean hasBackedUp = false; // Paper - recalculate header on header corruption
+ for (int k = 0; k < 1024; ++k) { final int headerLocation = k; // Paper - we expect this to be the header location
+ final int l = this.offsets.get(k);
if (l != 0) {
- int i1 = RegionFile.getSectorNumber(l);
- int j1 = RegionFile.getNumSectors(l);
+ final int i1 = RegionFile.getSectorNumber(l); final int offset = i1; // Paper - we expect this to be offset in file in sectors
+ int j1 = RegionFile.getNumSectors(l); final int sectorLength; // Paper - diff on change, we expect this to be sector length of region - watch out for reassignments
+ 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
// Spigot start
if (j1 == 255) {
if (numSectors == 255) {
// We're maxed out, so we need to read the proper length from the section
@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
j1 = (realLen.getInt(0) + 4) / 4096 + 1;
}
@@ -109,18 +461,62 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
// Spigot end
+ sectorLength = j1; // Paper - diff on change, we expect this to be sector length of region
if (i1 < 2) {
RegionFile.LOGGER.warn("Region file {} has invalid sector at index: {}; sector {} overlaps with header", new Object[]{path, k, i1});
- this.offsets.put(k, 0);
+ //this.offsets.put(k, 0); // Paper - we catch this, but need it in the header for the summary change
} else if (j1 == 0) {
RegionFile.LOGGER.warn("Region file {} has an invalid sector at index: {}; size has to be > 0", path, k);
- this.offsets.put(k, 0);
+ //this.offsets.put(k, 0); // Paper - we catch this, but need it in the header for the summary change
} else if ((long) i1 * 4096L > j) {
RegionFile.LOGGER.warn("Region file {} has an invalid sector at index: {}; sector {} is out of bounds", new Object[]{path, k, i1});
- this.offsets.put(k, 0);
+ //this.offsets.put(k, 0); // Paper - we catch this, but need it in the header for the summary change
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
} 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
} 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
} else {
- this.usedSectors.force(i1, j1);
+ //this.usedSectors.force(i1, j1); // Paper - move this down so we can check if it fails to allocate
- 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 (offset < 2 || sectorLength <= 0 || ((long)offset * 4096L) > regionFileSize) {
+ if (sectorNumber < 2 || numSectors <= 0 || ((long)sectorNumber * 4096L) > size) {
+ if (canRecalcHeader) {
+ LOGGER.error("Detected invalid header for regionfile " + this.path.toAbsolutePath() + "! Recalculating header...");
+ needsHeaderRecalc = true;
@ -471,7 +463,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ continue;
+ }
+ }
+ boolean failedToAllocate = !this.usedSectors.tryAllocate(offset, sectorLength);
+ 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());
}
@ -499,20 +491,19 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ }
+ // Paper end
}
}
@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
}
@@ -130,10 +526,35 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
}
private Path getExternalChunkPath(ChunkPos chunkPos) {
- String s = "c." + chunkPos.x + "." + chunkPos.z + ".mcc";
+ String s = "c." + chunkPos.x + "." + chunkPos.z + ".mcc"; // Paper - diff on change
return this.externalFileDir.resolve(s);
- 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);
}
+ // Paper start
+ private static ChunkPos getOversizedChunkPair(Path file) {
+ private static @Nullable ChunkPos getOversizedChunkPair(Path file) {
+ String fileName = file.getFileName().toString();
+
+ if (!fileName.startsWith("c.") || !fileName.endsWith(".mcc")) {
@ -537,81 +528,79 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper end
+
@Nullable
public synchronized DataInputStream getChunkDataInputStream(ChunkPos pos) throws IOException {
int i = this.getOffset(pos);
@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
((java.nio.Buffer) bytebuffer).flip(); // CraftBukkit - decompile error
if (bytebuffer.remaining() < 5) {
RegionFile.LOGGER.error("Chunk {} header is truncated: expected {} but read {}", new Object[]{pos, l, bytebuffer.remaining()});
public synchronized DataInputStream getChunkDataInputStream(ChunkPos chunkPos) throws IOException {
int offset = this.getOffset(chunkPos);
@@ -155,30 +576,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());
+ // Paper start - recalculate header on regionfile corruption
+ if (this.canRecalcHeader && this.recalculateHeader()) {
+ return this.getChunkDataInputStream(pos);
+ return this.getChunkDataInputStream(chunkPos);
+ }
+ // Paper end - recalculate header on regionfile corruption
return null;
} else {
int i1 = bytebuffer.getInt();
@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
if (i1 == 0) {
RegionFile.LOGGER.warn("Chunk {} is allocated, but stream is missing", pos);
int _int = byteBuffer.getInt();
byte b = byteBuffer.get();
if (_int == 0) {
LOGGER.warn("Chunk {} is allocated, but stream is missing", chunkPos);
+ // Paper start - recalculate header on regionfile corruption
+ if (this.canRecalcHeader && this.recalculateHeader()) {
+ return this.getChunkDataInputStream(pos);
+ return this.getChunkDataInputStream(chunkPos);
+ }
+ // Paper end - recalculate header on regionfile corruption
return null;
} else {
int j1 = i1 - 1;
@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
if (RegionFile.isExternalStreamChunk(b0)) {
if (j1 != 0) {
RegionFile.LOGGER.warn("Chunk has both internal and external streams");
int i1 = _int - 1;
if (isExternalStreamChunk(b)) {
if (i1 != 0) {
LOGGER.warn("Chunk has both internal and external streams");
+ // Paper start - recalculate header on regionfile corruption
+ if (this.canRecalcHeader && this.recalculateHeader()) {
+ return this.getChunkDataInputStream(pos);
+ return this.getChunkDataInputStream(chunkPos);
+ }
+ // Paper end - recalculate header on regionfile corruption
}
- return this.createExternalChunkInputStream(pos, RegionFile.getExternalChunkVersion(b0));
- return this.createExternalChunkInputStream(chunkPos, getExternalChunkVersion(b));
+ // Paper start - recalculate header on regionfile corruption
+ final DataInputStream ret = this.createExternalChunkInputStream(pos, RegionFile.getExternalChunkVersion(b0));
+ final DataInputStream ret = this.createExternalChunkInputStream(chunkPos, getExternalChunkVersion(b));
+ if (ret == null && this.canRecalcHeader && this.recalculateHeader()) {
+ return this.getChunkDataInputStream(pos);
+ return this.getChunkDataInputStream(chunkPos);
+ }
+ return ret;
+ // Paper end - recalculate header on regionfile corruption
} else if (j1 > bytebuffer.remaining()) {
RegionFile.LOGGER.error("Chunk {} stream is truncated: expected {} but read {}", new Object[]{pos, j1, bytebuffer.remaining()});
} else if (i1 > byteBuffer.remaining()) {
LOGGER.error("Chunk {} stream is truncated: expected {} but read {}", chunkPos, i1, byteBuffer.remaining());
+ // Paper start - recalculate header on regionfile corruption
+ if (this.canRecalcHeader && this.recalculateHeader()) {
+ return this.getChunkDataInputStream(pos);
+ return this.getChunkDataInputStream(chunkPos);
+ }
+ // Paper end - recalculate header on regionfile corruption
return null;
} else if (j1 < 0) {
RegionFile.LOGGER.error("Declared size {} of chunk {} is negative", i1, pos);
} else if (i1 < 0) {
LOGGER.error("Declared size {} of chunk {} is negative", _int, chunkPos);
+ // Paper start - recalculate header on regionfile corruption
+ if (this.canRecalcHeader && this.recalculateHeader()) {
+ return this.getChunkDataInputStream(pos);
+ return this.getChunkDataInputStream(chunkPos);
+ }
+ // Paper end - recalculate header on regionfile corruption
return null;
} else {
JvmProfiler.INSTANCE.onRegionFileRead(this.info, pos, this.version, j1);
- return this.createChunkInputStream(pos, b0, RegionFile.createStream(bytebuffer, j1));
JvmProfiler.INSTANCE.onRegionFileRead(this.info, chunkPos, this.version, i1);
- return this.createChunkInputStream(chunkPos, b, createStream(byteBuffer, i1));
+ // Paper start - recalculate header on regionfile corruption
+ final DataInputStream ret = this.createChunkInputStream(pos, b0, RegionFile.createStream(bytebuffer, j1));
+ final DataInputStream ret = this.createChunkInputStream(chunkPos, b, createStream(byteBuffer, i1));
+ if (ret == null && this.canRecalcHeader && this.recalculateHeader()) {
+ return this.getChunkDataInputStream(pos);
+ return this.getChunkDataInputStream(chunkPos);
+ }
+ return ret;
+ // Paper end - recalculate header on regionfile corruption
}
}
}
@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
@@ -361,9 +819,14 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
}
private ByteBuffer createExternalStub() {
@ -620,22 +609,21 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ }
+ private ByteBuffer createExternalStub(RegionFileVersion version) {
+ // Paper end - add version param
ByteBuffer bytebuffer = ByteBuffer.allocate(5);
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;
}
diff --git a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
index d263f78fa610ce6f6fb5a0f5e064e3d8335c2199..dad7f94b611cf0fc68b1a3878c458233f6bb6d61 100644
--- a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+++ b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
@@ -23,6 +23,36 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
private final Path folder;
private final boolean sync;
bytebuffer.putInt(1);
- bytebuffer.put((byte) (this.version.getId() | 128));
+ bytebuffer.put((byte) (version.getId() | 128)); // Paper - replace with version param
((java.nio.Buffer) bytebuffer).flip(); // CraftBukkit - decompile error
return bytebuffer;
}
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
@@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
}
}
// Paper end - rewrite chunk system
+ // Paper start - recalculate region file headers
+ private final boolean isChunkData;
+
@ -666,40 +654,42 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ }
+ }
+ // Paper end
protected RegionFileStorage(RegionStorageInfo storageKey, Path directory, boolean dsync) { // Paper - protected
this.folder = directory;
this.sync = dsync;
this.info = storageKey;
// Paper start - rewrite chunk system
private static final int REGION_SHIFT = 5;
private static final int MAX_NON_EXISTING_CACHE = 1024 * 4;
@@ -216,6 +246,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
}
// Paper start - rewrite chunk system
@@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
try {
if (datainputstream != null) {
nbttagcompound = NbtIo.read((DataInput) datainputstream);
+ // Paper start - recover from corrupt regionfile header
+ if (this.isChunkData) {
+ ChunkPos chunkPos = SerializableChunkData.getChunkCoordinate(nbttagcompound);
+ if (!chunkPos.equals(pos)) {
+ net.minecraft.server.MinecraftServer.LOGGER.error("Attempting to read chunk data at " + pos + " but got chunk data for " + chunkPos + " instead! Attempting regionfile recalculation for regionfile " + regionfile.getPath().toAbsolutePath());
+ if (regionfile.recalculateHeader()) {
+ return this.read(pos);
+ }
+ net.minecraft.server.MinecraftServer.LOGGER.error("Can't recalculate regionfile header, regenerating chunk " + pos + " for " + regionfile.getPath().toAbsolutePath());
+ return null;
+ }
+ }
+ // Paper end - recover from corrupt regionfile header
break label43;
}
@org.jetbrains.annotations.Contract("_, false -> !null") @Nullable private RegionFile getRegionFile(ChunkPos chunkPos, boolean existingOnly) throws IOException { // CraftBukkit
@@ -309,6 +340,19 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
}
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileVersion.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileVersion.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileVersion.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileVersion.java
@@ -0,0 +0,0 @@ import org.slf4j.Logger;
var4 = NbtIo.read(chunkDataInputStream);
+ // 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);
+ }
+ net.minecraft.server.MinecraftServer.LOGGER.error("Can't recalculate regionfile header, regenerating chunk " + chunkPos + " for " + regionFile.getPath().toAbsolutePath());
+ return null;
+ }
+ }
+ // Paper end - recover from corrupt regionfile header
}
return var4;
diff --git a/net/minecraft/world/level/chunk/storage/RegionFileVersion.java b/net/minecraft/world/level/chunk/storage/RegionFileVersion.java
index 0c739ca5b01ac0ec35a11fd01c5fc65de97c2852..de7deee4b79c969a7797bd57b657a16404c15303 100644
--- a/net/minecraft/world/level/chunk/storage/RegionFileVersion.java
+++ b/net/minecraft/world/level/chunk/storage/RegionFileVersion.java
@@ -21,7 +21,7 @@ import org.slf4j.Logger;
public class RegionFileVersion {
private static final Logger LOGGER = LogUtils.getLogger();
@ -708,11 +698,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
private static final Object2ObjectMap<String, RegionFileVersion> VERSIONS_BY_NAME = new Object2ObjectOpenHashMap<>();
public static final RegionFileVersion VERSION_GZIP = register(
new RegionFileVersion(
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/src/main/java/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
@@ -0,0 +0,0 @@ public record SerializableChunkData(Registry<Biome> biomeRegistry, ChunkPos chun
diff --git a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
index 70a9972252576e039ac126f6057a6ed66b80cdfc..d783c3580ea274a0a9cb07449eb8037bc5a04d76 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(
}
}
// Paper end - guard against serializing mismatching coordinates
@ -731,12 +721,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
// Paper start - Do not let the server load chunks from newer versions
private static final int CURRENT_DATA_VERSION = net.minecraft.SharedConstants.getCurrentVersion().getDataVersion().getVersion();
@@ -0,0 +0,0 @@ public record SerializableChunkData(Registry<Biome> biomeRegistry, ChunkPos chun
nbttagcompound.putInt("xPos", this.chunkPos.x);
nbttagcompound.putInt("yPos", this.minSectionY);
nbttagcompound.putInt("zPos", this.chunkPos.z);
- nbttagcompound.putLong("LastUpdate", this.lastUpdateTime);
+ nbttagcompound.putLong("LastUpdate", this.lastUpdateTime); // Paper - Diff on change
nbttagcompound.putLong("InhabitedTime", this.inhabitedTime);
nbttagcompound.putString("Status", BuiltInRegistries.CHUNK_STATUS.getKey(this.chunkStatus).toString());
DataResult<Tag> dataresult; // CraftBukkit - decompile error
@@ -604,7 +616,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());
if (this.blendingData != null) {

View file

@ -4,23 +4,23 @@ Date: Sun, 9 Jun 2019 03:53:22 +0100
Subject: [PATCH] Incremental chunk and player saving
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index d967d605c2e4227ae980c30f1c8b86edbc680d6d..6dbae12bbfd47cd4e75bc3089561e8e226e9e604 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -960,7 +960,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
boolean var4;
try {
this.isSaving = true;
- this.getPlayerList().saveAll();
+ this.getPlayerList().saveAll(); // Paper - Incremental chunk and player saving; diff on change
flag3 = this.saveAllChunks(suppressLogs, flush, force);
var4 = this.saveAllChunks(suppressLog, flush, forced);
} finally {
this.isSaving = false;
@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1533,9 +1533,29 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
--this.ticksUntilAutosave;
this.ticksUntilAutosave--;
- if (this.autosavePeriod > 0 && this.ticksUntilAutosave <= 0) { // CraftBukkit
- this.autoSave();
+ // Paper start - Incremental chunk and player saving
@ -47,22 +47,20 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ profiler.pop();
+ // Paper end - Incremental chunk and player saving
ProfilerFiller gameprofilerfiller = Profiler.get();
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -0,0 +0,0 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
ProfilerFiller profilerFiller = Profiler.get();
this.runAllTasks(); // Paper - move runAllTasks() into full server tick (previously for timings)
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 46dfaed12c998c219a20c711a06531aed2c68012..ebeeb63c3dca505a3ce8b88feaa5d2ca20ec24a2 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -1316,6 +1316,28 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
return !this.server.isUnderSpawnProtection(this, pos, player) && this.getWorldBorder().isWithinBounds(pos);
}
+ // Paper start - Incremental chunk and player saving
+ public void saveIncrementally(boolean doFull) {
+ ServerChunkCache chunkproviderserver = this.getChunkSource();
+
+ if (doFull) {
+ org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(getWorld()));
+ org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(this.getWorld()));
+ }
+
+ if (doFull) {
@ -72,43 +70,43 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Copied from save()
+ // CraftBukkit start - moved from MinecraftServer.saveChunks
+ if (doFull) { // Paper
+ ServerLevel worldserver1 = this;
+ this.serverLevelData.setWorldBorder(worldserver1.getWorldBorder().createSettings());
+ ServerLevel serverLevel1 = this;
+ this.serverLevelData.setWorldBorder(serverLevel1.getWorldBorder().createSettings());
+ this.serverLevelData.setCustomBossEvents(this.server.getCustomBossEvents().save(this.registryAccess()));
+ this.convertable.saveDataTag(this.server.registryAccess(), this.serverLevelData, this.server.getPlayerList().getSingleplayerData());
+ 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 progressListener, boolean flush, boolean savingDisabled) {
public void save(@Nullable ProgressListener progress, boolean flush, boolean skipSave) {
// Paper start - add close param
this.save(progressListener, flush, savingDisabled, false);
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -0,0 +0,0 @@ import org.bukkit.inventory.MainHand;
public class ServerPlayer extends net.minecraft.world.entity.player.Player implements ca.spottedleaf.moonrise.patches.chunk_system.player.ChunkSystemServerPlayer { // Paper - rewrite chunk system
this.save(progress, flush, skipSave, false);
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index e61fe83479f095e8addbd3e8f1d5179c998ae1eb..0a7e5106a1d39150326e7c323030df5d32ecef1e 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -180,6 +180,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();
+ public long lastSave = MinecraftServer.currentTick; // Paper - Incremental chunk and player saving
private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_XZ = 32;
private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_Y = 10;
private static final int FLY_STAT_RECORDING_SPEED = 25;
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -0,0 +0,0 @@ public abstract class PlayerList {
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index 5e94dd9e26aa4fd6545dbaae2ae0cb51cb6f13e0..03feaf0adb8ee87e33744a4615dc2507a02f92d7 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -482,6 +482,7 @@ public abstract class PlayerList {
protected void save(ServerPlayer player) {
if (!player.getBukkitEntity().isPersistent()) return; // CraftBukkit
+ player.lastSave = MinecraftServer.currentTick; // Paper - Incremental chunk and player saving
this.playerIo.save(player);
ServerStatsCounter serverstatisticmanager = (ServerStatsCounter) player.getStats(); // CraftBukkit
@@ -0,0 +0,0 @@ public abstract class PlayerList {
ServerStatsCounter serverStatsCounter = player.getStats(); // CraftBukkit
if (serverStatsCounter != null) {
@@ -1064,9 +1065,23 @@ public abstract class PlayerList {
}
public void saveAll() {
@ -116,18 +114,20 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ this.saveAll(-1);
+ }
+
+ public void saveAll(int interval) {
+ public void saveAll(final int interval) {
io.papermc.paper.util.MCUtil.ensureMain("Save Players" , () -> { // Paper - Ensure main
+ int numSaved = 0;
+ long now = MinecraftServer.currentTick;
for (int i = 0; i < this.players.size(); ++i) {
- this.save((ServerPlayer) this.players.get(i));
+ final long now = MinecraftServer.currentTick;
for (int i = 0; i < this.players.size(); i++) {
- this.save(this.players.get(i));
+ final ServerPlayer player = this.players.get(i);
+ if (interval == -1 || now - player.lastSave >= interval) {
+ this.save(player);
+ if (interval != -1 && ++numSaved >= io.papermc.paper.configuration.GlobalConfiguration.get().playerAutoSave.maxPerTick()) { break; }
+ if (interval != -1 && ++numSaved >= io.papermc.paper.configuration.GlobalConfiguration.get().playerAutoSave.maxPerTick()) {
+ break;
+ }
+ }
+ // Paper end - Incremental chunk and player saving
}
return null; }); // Paper - ensure main
}

View file

@ -30,12 +30,12 @@ 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/src/main/java/io/papermc/paper/util/PoiAccess.java b/src/main/java/io/papermc/paper/util/PoiAccess.java
diff --git a/io/papermc/paper/util/PoiAccess.java b/io/papermc/paper/util/PoiAccess.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
index 0000000000000000000000000000000000000000..f39294b1f83c4022be5ced4da781103a1eee2daf
--- /dev/null
+++ b/src/main/java/io/papermc/paper/util/PoiAccess.java
@@ -0,0 +0,0 @@
+++ b/io/papermc/paper/util/PoiAccess.java
@@ -0,0 +1,806 @@
+package io.papermc.paper.util;
+
+import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
@ -842,38 +842,38 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ throw new RuntimeException();
+ }
+}
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java b/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java
@@ -0,0 +0,0 @@ public class AcquirePoi {
return true;
}
};
- Set<Pair<Holder<PoiType>, BlockPos>> set = poiManager.findAllClosestFirstWithType(
- poiPredicate, predicate2, entity.blockPosition(), 48, PoiManager.Occupancy.HAS_SPACE
- )
- .limit(5L)
- .filter(pairx -> worldPosBiPredicate.test(world, (BlockPos)pairx.getSecond()))
- .collect(Collectors.toSet());
+ // Paper start - optimise POI access
+ final java.util.List<Pair<Holder<PoiType>, BlockPos>> poiposes = new java.util.ArrayList<>();
+ io.papermc.paper.util.PoiAccess.findNearestPoiPositions(poiManager, poiPredicate, predicate2, entity.blockPosition(), 48, 48*48, PoiManager.Occupancy.HAS_SPACE, false, 5, poiposes);
+ final Set<Pair<Holder<PoiType>, BlockPos>> set = new java.util.HashSet<>(poiposes.size());
+ for (final Pair<Holder<PoiType>, BlockPos> poiPose : poiposes) {
+ if (worldPosBiPredicate.test(world, poiPose.getSecond())) {
+ set.add(poiPose);
+ }
+ }
+ // Paper end - optimise POI access
Path path = findPathToPois(entity, set);
if (path != null && path.canReach()) {
BlockPos blockPos = path.getTarget();
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java
@@ -0,0 +0,0 @@ public class NearestBedSensor extends Sensor<Mob> {
diff --git a/net/minecraft/world/entity/ai/behavior/AcquirePoi.java b/net/minecraft/world/entity/ai/behavior/AcquirePoi.java
index 9de13a78b2a8be181c02ab330bfa9abb936a83db..b9174ae7e3a3e2de2d570b95ab5012ac3c3a2eda 100644
--- a/net/minecraft/world/entity/ai/behavior/AcquirePoi.java
+++ b/net/minecraft/world/entity/ai/behavior/AcquirePoi.java
@@ -84,12 +84,16 @@ public class AcquirePoi {
return true;
}
};
- Set<Pair<Holder<PoiType>, 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<Pair<Holder<PoiType>, 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<Pair<Holder<PoiType>, BlockPos>> set = new java.util.HashSet<>(poiposes.size());
+ for (final Pair<Holder<PoiType>, 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<Mob> {
return true;
}
};
@ -889,82 +889,82 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ Path path = AcquirePoi.findPathToPois(entity, new java.util.HashSet<>(poiposes));
+ // Paper end - optimise POI access
if (path != null && path.canReach()) {
BlockPos blockPos = path.getTarget();
Optional<Holder<PoiType>> optional = poiManager.getType(blockPos);
diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java
+++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java
@@ -0,0 +0,0 @@ public class PoiManager extends SectionStorage<PoiSection, PoiSection.Packed> im
BlockPos target = path.getTarget();
Optional<Holder<PoiType>> 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 5c5724f5e3ad640f55aecbc1d8f71d1f59ecdc62..618fc0eb4fe70e46e55f3aa28e8eac1d2d01b6d9 100644
--- a/net/minecraft/world/entity/ai/village/poi/PoiManager.java
+++ b/net/minecraft/world/entity/ai/village/poi/PoiManager.java
@@ -254,36 +254,47 @@ public class PoiManager extends SectionStorage<PoiSection, PoiSection.Packed> im
public Optional<BlockPos> find(
Predicate<Holder<PoiType>> typePredicate, Predicate<BlockPos> posPredicate, BlockPos pos, int radius, PoiManager.Occupancy occupationStatus
Predicate<Holder<PoiType>> typePredicate, Predicate<BlockPos> posPredicate, BlockPos pos, int distance, PoiManager.Occupancy status
) {
- return this.findAll(typePredicate, posPredicate, pos, radius, occupationStatus).findFirst();
- 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, radius, occupationStatus, false);
+ BlockPos ret = io.papermc.paper.util.PoiAccess.findAnyPoiPosition(this, typePredicate, posPredicate, pos, distance, status, false);
+ return Optional.ofNullable(ret);
+ // Paper end
}
public Optional<BlockPos> findClosest(Predicate<Holder<PoiType>> typePredicate, BlockPos pos, int radius, PoiManager.Occupancy occupationStatus) {
- return this.getInRange(typePredicate, pos, radius, occupationStatus)
- .map(PoiRecord::getPos)
- .min(Comparator.comparingDouble(poiPos -> poiPos.distSqr(pos)));
public Optional<BlockPos> findClosest(Predicate<Holder<PoiType>> 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, radius, radius * radius, occupationStatus, false);
+ 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<Pair<Holder<PoiType>, BlockPos>> findClosestWithType(
Predicate<Holder<PoiType>> typePredicate, BlockPos pos, int radius, PoiManager.Occupancy occupationStatus
Predicate<Holder<PoiType>> typePredicate, BlockPos pos, int distance, PoiManager.Occupancy status
) {
- return this.getInRange(typePredicate, pos, radius, occupationStatus)
- .min(Comparator.comparingDouble(poi -> poi.getPos().distSqr(pos)))
- .map(poi -> Pair.of(poi.getPoiType(), poi.getPos()));
- 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, radius, radius * radius, occupationStatus, false
+ this, typePredicate, null, pos, distance, distance * distance, status, false
+ ));
+ // Paper end - re-route to faster logic
}
public Optional<BlockPos> findClosest(
Predicate<Holder<PoiType>> typePredicate, Predicate<BlockPos> posPredicate, BlockPos pos, int radius, PoiManager.Occupancy occupationStatus
Predicate<Holder<PoiType>> typePredicate, Predicate<BlockPos> posPredicate, BlockPos pos, int distance, PoiManager.Occupancy status
) {
- return this.getInRange(typePredicate, pos, radius, occupationStatus)
- return this.getInRange(typePredicate, pos, distance, status)
- .map(PoiRecord::getPos)
- .filter(posPredicate)
- .min(Comparator.comparingDouble(poiPos -> poiPos.distSqr(pos)));
- .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, radius, radius * radius, occupationStatus, false);
+ 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<BlockPos> take(Predicate<Holder<PoiType>> typePredicate, BiPredicate<Holder<PoiType>, BlockPos> posPredicate, BlockPos pos, int radius) {
- return this.getInRange(typePredicate, pos, radius, PoiManager.Occupancy.HAS_SPACE)
- .filter(poi -> posPredicate.test(poi.getPoiType(), poi.getPos()))
public Optional<BlockPos> take(
Predicate<Holder<PoiType>> typePredicate, BiPredicate<Holder<PoiType>, 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, posPredicate, pos, radius, radius * radius, Occupancy.HAS_SPACE, false
+ this, typePredicate, combinedTypePosPredicate, pos, distance, distance * distance, Occupancy.HAS_SPACE, false
+ );
+ return Optional.ofNullable(closest)
+ // Paper end - re-route to faster logic
.map(poi -> {
poi.acquireTicket();
return poi.getPos();
@@ -0,0 +0,0 @@ public class PoiManager extends SectionStorage<PoiSection, PoiSection.Packed> im
int radius,
.map(poiRecord -> {
poiRecord.acquireTicket();
return poiRecord.getPos();
@@ -298,8 +309,21 @@ public class PoiManager extends SectionStorage<PoiSection, PoiSection.Packed> im
int distance,
RandomSource random
) {
- List<PoiRecord> list = Util.toShuffledList(this.getInRange(typePredicate, pos, radius, occupationStatus), random);
- return list.stream().filter(poi -> positionPredicate.test(poi.getPos())).findFirst().map(PoiRecord::getPos);
- List<PoiRecord> 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<PoiRecord> list = new java.util.ArrayList<>();
+ io.papermc.paper.util.PoiAccess.findAnyPoiRecords(
+ this, typePredicate, positionPredicate, pos, radius, occupationStatus, false, Integer.MAX_VALUE, list
+ 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
@ -979,11 +979,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
}
public boolean release(BlockPos pos) {
diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java
+++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java
@@ -0,0 +0,0 @@ import org.slf4j.Logger;
diff --git a/net/minecraft/world/entity/ai/village/poi/PoiSection.java b/net/minecraft/world/entity/ai/village/poi/PoiSection.java
index 39cd1e3d8192d7077d6b7864d33933097cc6b986..b92ba4d194fd3af94c7af5d8e150fc4297c73ab8 100644
--- a/net/minecraft/world/entity/ai/village/poi/PoiSection.java
+++ b/net/minecraft/world/entity/ai/village/poi/PoiSection.java
@@ -26,7 +26,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<PoiRecord> records = new Short2ObjectOpenHashMap<>();
@ -992,48 +992,43 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
private final Runnable setDirty;
private boolean isValid;
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java
@@ -0,0 +0,0 @@ public class SectionStorage<R, P> implements AutoCloseable, ca.spottedleaf.moonr
diff --git a/net/minecraft/world/level/chunk/storage/SectionStorage.java b/net/minecraft/world/level/chunk/storage/SectionStorage.java
index 778bd73a938c94ecb85ca0f8b686ff4e1baee040..79d4ce7712f16995b0de3be86477fb43ab3961d7 100644
--- a/net/minecraft/world/level/chunk/storage/SectionStorage.java
+++ b/net/minecraft/world/level/chunk/storage/SectionStorage.java
@@ -131,11 +131,11 @@ public class SectionStorage<R, P> implements AutoCloseable, ca.spottedleaf.moonr
}
@Nullable
- protected Optional<R> get(long pos) {
+ public Optional<R> get(long pos) { // Paper - public
return this.storage.get(pos);
- protected Optional<R> get(long sectionKey) {
+ public Optional<R> get(long sectionKey) { // Paper - public
return this.storage.get(sectionKey);
}
- protected Optional<R> getOrLoad(long pos) {
+ public Optional<R> getOrLoad(long pos) { // Paper - public
if (this.outsideStoredRange(pos)) {
- protected Optional<R> getOrLoad(long sectionKey) {
+ public Optional<R> getOrLoad(long sectionKey) { // Paper - public
if (this.outsideStoredRange(sectionKey)) {
return Optional.empty();
} else {
diff --git a/src/main/java/net/minecraft/world/level/portal/PortalForcer.java b/src/main/java/net/minecraft/world/level/portal/PortalForcer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/portal/PortalForcer.java
+++ b/src/main/java/net/minecraft/world/level/portal/PortalForcer.java
@@ -0,0 +0,0 @@ public class PortalForcer {
// int i = flag ? 16 : 128;
diff --git a/net/minecraft/world/level/portal/PortalForcer.java b/net/minecraft/world/level/portal/PortalForcer.java
index ada2da62d3a40d67e64f5f8d7299f78b5c6f53cb..90ae71eb8cc7f925eb212f39731d70f3bff5ef0a 100644
--- a/net/minecraft/world/level/portal/PortalForcer.java
+++ b/net/minecraft/world/level/portal/PortalForcer.java
@@ -48,13 +48,38 @@ public class PortalForcer {
PoiManager poiManager = this.level.getPoiManager();
// int i = isNether ? 16 : 128;
// CraftBukkit end
- villageplace.ensureLoadedAndValid(this.level, blockposition, i);
- Stream<BlockPos> stream = villageplace.getInSquare((holder) -> { // CraftBukkit - decompile error
- return holder.is(PoiTypes.NETHER_PORTAL);
- }, blockposition, i, PoiManager.Occupancy.ANY).map(PoiRecord::getPos);
-
- Objects.requireNonNull(worldborder);
- return stream.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))).filter((blockposition1) -> { // Paper - Configurable nether ceiling damage
- return this.level.getBlockState(blockposition1).hasProperty(BlockStateProperties.HORIZONTAL_AXIS);
- }).min(Comparator.comparingDouble((BlockPos blockposition1) -> { // CraftBukkit - decompile error
- return blockposition1.distSqr(blockposition);
- }).thenComparingInt(Vec3i::getY));
- 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.<BlockPos>comparingDouble(blockPos -> blockPos.distSqr(exitPos)).thenComparingInt(Vec3i::getY));
+ // Paper start - optimise portals
+ Optional<PoiRecord> optional;
+ java.util.List<PoiRecord> records = new java.util.ArrayList<>();
+ io.papermc.paper.util.PoiAccess.findClosestPoiDataRecords(
+ villageplace,
+ 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);
@ -1042,12 +1037,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // 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
+ 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);
+ },
+ blockposition, i, Double.MAX_VALUE, PoiManager.Occupancy.ANY, true, records
+ 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.

View file

@ -0,0 +1,236 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: kickash32 <kickash32@gmail.com>
Date: Mon, 19 Aug 2019 01:27:58 +0500
Subject: [PATCH] Optional per player mob spawns
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
index ff6503bf8eb88d1264c3d848a89d0255b4b3ae68..9eed24939fc09f00a9dbce1be2ab9c34d024fd29 100644
--- a/net/minecraft/server/level/ChunkMap.java
+++ b/net/minecraft/server/level/ChunkMap.java
@@ -236,11 +236,29 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// Paper - rewrite chunk system
}
- // Paper start
- public int getMobCountNear(final ServerPlayer player, final net.minecraft.world.entity.MobCategory mobCategory) {
- return -1;
+ // Paper start - Optional per player mob spawns
+ public void updatePlayerMobTypeMap(final Entity entity) {
+ if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) {
+ return;
+ }
+
+ final int index = entity.getType().getCategory().ordinal();
+ final ca.spottedleaf.moonrise.common.list.ReferenceList<ServerPlayer> inRange =
+ this.level.moonrise$getNearbyPlayers().getPlayers(entity.chunkPosition(), ca.spottedleaf.moonrise.common.misc.NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE);
+ if (inRange == null) {
+ return;
+ }
+
+ final ServerPlayer[] backingSet = inRange.getRawDataUnchecked();
+ for (int i = 0, len = inRange.size(); i < len; i++) {
+ ++(backingSet[i].mobCounts[index]);
+ }
}
- // Paper end
+
+ public int getMobCountNear(final ServerPlayer player, final net.minecraft.world.entity.MobCategory mobCategory) {
+ return player.mobCounts[mobCategory.ordinal()];
+ }
+ // Paper end - Optional per player mob spawns
protected ChunkGenerator generator() {
return this.worldGenContext.generator();
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
index 87d4291a3944f706a694536da6de0f28c548ab8d..5576bf1d1d70ab7a010653d3207909b5de867e70 100644
--- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -517,7 +517,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
profilerFiller.popPush("shuffleChunks");
// Paper start - chunk tick iteration optimisation
this.shuffleRandom.setSeed(this.level.random.nextLong());
- Util.shuffle(list, this.shuffleRandom);
+ if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) Util.shuffle(list, this.shuffleRandom); // Paper - Optional per player mob spawns; do not need this when per-player is enabled
// Paper end - chunk tick iteration optimisation
this.tickChunks(profilerFiller, l, list);
profilerFiller.pop();
@@ -571,9 +571,18 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
private void tickChunks(ProfilerFiller profiler, long timeInhabited, List<LevelChunk> chunks) {
profiler.popPush("naturalSpawnCount");
int naturalSpawnChunkCount = this.distanceManager.getNaturalSpawnChunkCount();
- NaturalSpawner.SpawnState spawnState = NaturalSpawner.createState(
- naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, new LocalMobCapCalculator(this.chunkMap)
- );
+ // Paper start - Optional per player mob spawns
+ NaturalSpawner.SpawnState spawnState;
+ if ((this.spawnFriendlies || this.spawnEnemies) && this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { // don't count mobs when animals and monsters are disabled
+ // re-set mob counts
+ for (ServerPlayer player : this.level.players) {
+ Arrays.fill(player.mobCounts, 0);
+ }
+ spawnState = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, null, true);
+ } else {
+ spawnState = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, !this.level.paperConfig().entities.spawning.perPlayerMobSpawns ? new LocalMobCapCalculator(this.chunkMap) : null, false);
+ }
+ // Paper end - Optional per player mob spawns
this.lastSpawnState = spawnState;
profiler.popPush("spawnAndTick");
boolean _boolean = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index 0a7e5106a1d39150326e7c323030df5d32ecef1e..a63702dd7e86fc8b9f78c2ae23e23b65b6b2ee24 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -368,6 +368,10 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
public boolean queueHealthUpdatePacket;
public net.minecraft.network.protocol.game.ClientboundSetHealthPacket queuedHealthUpdatePacket;
// Paper end - cancellable death event
+ // Paper start - Optional per player mob spawns
+ public static final int MOBCATEGORY_TOTAL_ENUMS = net.minecraft.world.entity.MobCategory.values().length;
+ public final int[] mobCounts = new int[MOBCATEGORY_TOTAL_ENUMS];
+ // Paper end - Optional per player mob spawns
// CraftBukkit start
public org.bukkit.craftbukkit.entity.CraftPlayer.TransferCookieConnection transferCookieConnection;
public String displayName;
diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java
index 913ea92ace9d610c25bf28f703a3b227044aea63..ef8bacbbb43a9b80281a313ca43b7efff5a93e03 100644
--- a/net/minecraft/world/level/NaturalSpawner.java
+++ b/net/minecraft/world/level/NaturalSpawner.java
@@ -72,6 +72,14 @@ public final class NaturalSpawner {
public static NaturalSpawner.SpawnState createState(
int spawnableChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkGetter, LocalMobCapCalculator calculator
) {
+ // Paper start - Optional per player mob spawns
+ return createState(spawnableChunkCount, entities, chunkGetter, calculator, false);
+ }
+
+ public static NaturalSpawner.SpawnState createState(
+ int spawnableChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkGetter, LocalMobCapCalculator calculator, final boolean countMobs
+ ) {
+ // Paper end - Optional per player mob spawns
PotentialCalculator potentialCalculator = new PotentialCalculator();
Object2IntOpenHashMap<MobCategory> map = new Object2IntOpenHashMap<>();
@@ -93,11 +101,16 @@ public final class NaturalSpawner {
potentialCalculator.addCharge(entity.blockPosition(), mobSpawnCost.charge());
}
- if (entity instanceof Mob) {
+ if (calculator != null && entity instanceof Mob) { // Paper - Optional per player mob spawns
calculator.addMob(chunk.getPos(), category);
}
map.addTo(category, 1);
+ // Paper start - Optional per player mob spawns
+ if (countMobs) {
+ chunk.level.getChunkSource().chunkMap.updatePlayerMobTypeMap(entity);
+ }
+ // Paper end - Optional per player mob spawns
});
}
}
@@ -135,7 +148,7 @@ public final class NaturalSpawner {
if ((spawnFriendlies || !mobCategory.isFriendly())
&& (spawnEnemies || mobCategory.isFriendly())
&& (spawnPassives || !mobCategory.isPersistent())
- && spawnState.canSpawnForCategoryGlobal(mobCategory, limit)) { // Paper - Optional per player mob spawns; remove global check, check later during the local one
+ && (level.paperConfig().entities.spawning.perPlayerMobSpawns || spawnState.canSpawnForCategoryGlobal(mobCategory, limit))) { // Paper - Optional per player mob spawns; remove global check, check later during the local one
list.add(mobCategory);
// CraftBukkit end
}
@@ -149,8 +162,37 @@ public final class NaturalSpawner {
profilerFiller.push("spawner");
for (MobCategory mobCategory : categories) {
- if (spawnState.canSpawnForCategoryLocal(mobCategory, chunk.getPos())) {
- spawnCategoryForChunk(mobCategory, level, chunk, spawnState::canSpawn, spawnState::afterSpawn);
+ // Paper start - Optional per player mob spawns
+ final boolean canSpawn;
+ int maxSpawns = Integer.MAX_VALUE;
+ if (level.paperConfig().entities.spawning.perPlayerMobSpawns) {
+ // Copied from getFilteredSpawningCategories
+ int limit = mobCategory.getMaxInstancesPerChunk();
+ SpawnCategory spawnCategory = CraftSpawnCategory.toBukkit(mobCategory);
+ if (CraftSpawnCategory.isValidForLimits(spawnCategory)) {
+ limit = level.getWorld().getSpawnLimit(spawnCategory);
+ }
+
+ // Apply per-player limit
+ int minDiff = Integer.MAX_VALUE;
+ final ca.spottedleaf.moonrise.common.list.ReferenceList<net.minecraft.server.level.ServerPlayer> inRange =
+ level.moonrise$getNearbyPlayers().getPlayers(chunk.getPos(), ca.spottedleaf.moonrise.common.misc.NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE);
+ if (inRange != null) {
+ final net.minecraft.server.level.ServerPlayer[] backingSet = inRange.getRawDataUnchecked();
+ for (int k = 0, len = inRange.size(); k < len; k++) {
+ minDiff = Math.min(limit - level.getChunkSource().chunkMap.getMobCountNear(backingSet[k], mobCategory), minDiff);
+ }
+ }
+
+ maxSpawns = (minDiff == Integer.MAX_VALUE) ? 0 : minDiff;
+ canSpawn = maxSpawns > 0;
+ } else {
+ canSpawn = spawnState.canSpawnForCategoryLocal(mobCategory, chunk.getPos());
+ }
+ if (canSpawn) {
+ spawnCategoryForChunk(mobCategory, level, chunk, spawnState::canSpawn, spawnState::afterSpawn,
+ maxSpawns, level.paperConfig().entities.spawning.perPlayerMobSpawns ? level.getChunkSource().chunkMap::updatePlayerMobTypeMap : null);
+ // Paper end - Optional per player mob spawns
}
}
@@ -170,9 +212,16 @@ public final class NaturalSpawner {
public static void spawnCategoryForChunk(
MobCategory category, ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback
) {
+ // Paper start - Optional per player mob spawns
+ spawnCategoryForChunk(category, level, chunk, filter, callback, Integer.MAX_VALUE, null);
+ }
+ public static void spawnCategoryForChunk(
+ MobCategory category, ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback, final int maxSpawns, final Consumer<Entity> trackEntity
+ ) {
+ // Paper end - Optional per player mob spawns
BlockPos randomPosWithin = getRandomPosWithin(level, chunk);
if (randomPosWithin.getY() >= level.getMinY() + 1) {
- spawnCategoryForPosition(category, level, chunk, randomPosWithin, filter, callback);
+ spawnCategoryForPosition(category, level, chunk, randomPosWithin, filter, callback, maxSpawns, trackEntity); // Paper - Optional per player mob spawns
}
}
@@ -189,6 +238,12 @@ public final class NaturalSpawner {
NaturalSpawner.SpawnPredicate filter,
NaturalSpawner.AfterSpawnCallback callback
) {
+ spawnCategoryForPosition(category, level, chunk, pos, filter, callback, Integer.MAX_VALUE, null);
+ }
+ public static void spawnCategoryForPosition(
+ MobCategory category, ServerLevel level, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback, final int maxSpawns, final @Nullable Consumer<Entity> trackEntity
+ ) {
+ // Paper end - Optional per player mob spawns
StructureManager structureManager = level.structureManager();
ChunkGenerator generator = level.getChunkSource().getGenerator();
int y = pos.getY();
@@ -252,9 +307,14 @@ public final class NaturalSpawner {
++i;
++i3;
callback.run(mobForSpawn, chunk);
+ // Paper start - Optional per player mob spawns
+ if (trackEntity != null) {
+ trackEntity.accept(mobForSpawn);
+ }
+ // Paper end - Optional per player mob spawns
}
// CraftBukkit end
- if (i >= mobForSpawn.getMaxSpawnClusterSize()) {
+ if (i >= mobForSpawn.getMaxSpawnClusterSize() || i >= maxSpawns) { // Paper - Optional per player mob spawns
return;
}
@@ -565,7 +625,7 @@ public final class NaturalSpawner {
this.spawnPotential.addCharge(blockPos, d);
MobCategory category = type.getCategory();
this.mobCategoryCounts.addTo(category, 1);
- this.localMobCapCalculator.addMob(new ChunkPos(blockPos), category);
+ if (this.localMobCapCalculator != null) this.localMobCapCalculator.addMob(new ChunkPos(blockPos), category); // Paper - Optional per player mob spawns
}
public int getSpawnableChunkCount() {

View file

@ -0,0 +1,89 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: kickash32 <kickash32@gmail.com>
Date: Mon, 5 Apr 2021 01:42:35 -0400
Subject: [PATCH] Improve cancelling PreCreatureSpawnEvent with per player mob
spawns
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
index 9eed24939fc09f00a9dbce1be2ab9c34d024fd29..b3f498558614243cf633dcd71e3c49c2c55e6e0f 100644
--- a/net/minecraft/server/level/ChunkMap.java
+++ b/net/minecraft/server/level/ChunkMap.java
@@ -255,8 +255,25 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
}
+ // Paper start - per player mob count backoff
+ public void updateFailurePlayerMobTypeMap(int chunkX, int chunkZ, net.minecraft.world.entity.MobCategory mobCategory) {
+ if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) {
+ return;
+ }
+ int idx = mobCategory.ordinal();
+ final ca.spottedleaf.moonrise.common.list.ReferenceList<ServerPlayer> inRange =
+ this.level.moonrise$getNearbyPlayers().getPlayersByChunk(chunkX, chunkZ, ca.spottedleaf.moonrise.common.misc.NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE);
+ if (inRange == null) {
+ return;
+ }
+ final ServerPlayer[] backingSet = inRange.getRawDataUnchecked();
+ for (int i = 0, len = inRange.size(); i < len; i++) {
+ ++(backingSet[i].mobBackoffCounts[idx]);
+ }
+ }
+ // Paper end - per player mob count backoff
public int getMobCountNear(final ServerPlayer player, final net.minecraft.world.entity.MobCategory mobCategory) {
- return player.mobCounts[mobCategory.ordinal()];
+ return player.mobCounts[mobCategory.ordinal()] + player.mobBackoffCounts[mobCategory.ordinal()]; // Paper - per player mob count backoff
}
// Paper end - Optional per player mob spawns
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
index 5576bf1d1d70ab7a010653d3207909b5de867e70..6540b2d6a1062d883811ce240c49d30d1925b291 100644
--- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -576,7 +576,17 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
if ((this.spawnFriendlies || this.spawnEnemies) && this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { // don't count mobs when animals and monsters are disabled
// re-set mob counts
for (ServerPlayer player : this.level.players) {
- Arrays.fill(player.mobCounts, 0);
+ // Paper start - per player mob spawning backoff
+ for (int ii = 0; ii < ServerPlayer.MOBCATEGORY_TOTAL_ENUMS; ii++) {
+ player.mobCounts[ii] = 0;
+
+ int newBackoff = player.mobBackoffCounts[ii] - 1; // TODO make configurable bleed // TODO use nonlinear algorithm?
+ if (newBackoff < 0) {
+ newBackoff = 0;
+ }
+ player.mobBackoffCounts[ii] = newBackoff;
+ }
+ // Paper end - per player mob spawning backoff
}
spawnState = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, null, true);
} else {
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index a63702dd7e86fc8b9f78c2ae23e23b65b6b2ee24..6d75a641431c7deb7e8ddbf02cdc919015a3a7dc 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -372,6 +372,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
public static final int MOBCATEGORY_TOTAL_ENUMS = net.minecraft.world.entity.MobCategory.values().length;
public final int[] mobCounts = new int[MOBCATEGORY_TOTAL_ENUMS];
// Paper end - Optional per player mob spawns
+ public final int[] mobBackoffCounts = new int[MOBCATEGORY_TOTAL_ENUMS]; // Paper - per player mob count backoff
// CraftBukkit start
public org.bukkit.craftbukkit.entity.CraftPlayer.TransferCookieConnection transferCookieConnection;
public String displayName;
diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java
index ef8bacbbb43a9b80281a313ca43b7efff5a93e03..17ce115e887cbbb06ad02ab7ddb488e27342c0e4 100644
--- a/net/minecraft/world/level/NaturalSpawner.java
+++ b/net/minecraft/world/level/NaturalSpawner.java
@@ -285,6 +285,11 @@ public final class NaturalSpawner {
// Paper start - PreCreatureSpawnEvent
PreSpawnStatus doSpawning = isValidSpawnPostitionForType(level, category, structureManager, generator, spawnerData, mutableBlockPos, d2);
+ // Paper start - per player mob count backoff
+ if (doSpawning == PreSpawnStatus.ABORT || doSpawning == PreSpawnStatus.CANCELLED) {
+ level.getChunkSource().chunkMap.updateFailurePlayerMobTypeMap(mutableBlockPos.getX() >> 4, mutableBlockPos.getZ() >> 4, category);
+ }
+ // Paper end - per player mob count backoff
if (doSpawning == PreSpawnStatus.ABORT) {
return;
}

View file

@ -0,0 +1,683 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Wed, 27 Apr 2016 22:09:52 -0400
Subject: [PATCH] Optimize Hoppers
* Removes unnecessary extra calls to .update() that are very expensive
* Lots of itemstack cloning removed. Only clone if the item is actually moved
* Return true when a plugin cancels inventory move item event instead of false, as false causes pulls to cycle through all items.
However, pushes do not exhibit the same behavior, so this is not something plugins could of been relying on.
* Add option (Default on) to cooldown hoppers when they fail to move an item due to full inventory
* Skip subsequent InventoryMoveItemEvents if a plugin does not use the item after first event fire for an iteration by tracking changes to the event via an internal event implementation.
* Don't check for Entities with Inventories if the block above us is also occluding (not just Inventoried)
* Remove Streams from Item Suck In and restore restore 1.12 AABB checks which is simpler and no voxel allocations (was doing TWO Item Suck ins)
diff --git a/io/papermc/paper/event/inventory/PaperInventoryMoveItemEvent.java b/io/papermc/paper/event/inventory/PaperInventoryMoveItemEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..24a2090e068ad3c0d08705050944abdfe19136a2
--- /dev/null
+++ b/io/papermc/paper/event/inventory/PaperInventoryMoveItemEvent.java
@@ -0,0 +1,29 @@
+package io.papermc.paper.event.inventory;
+
+import org.bukkit.event.inventory.InventoryMoveItemEvent;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.ItemStack;
+import org.jspecify.annotations.NullMarked;
+
+@NullMarked
+public class PaperInventoryMoveItemEvent extends InventoryMoveItemEvent {
+
+ public boolean calledSetItem;
+ public boolean calledGetItem;
+
+ public PaperInventoryMoveItemEvent(final Inventory sourceInventory, final ItemStack itemStack, final Inventory destinationInventory, final boolean didSourceInitiate) {
+ super(sourceInventory, itemStack, destinationInventory, didSourceInitiate);
+ }
+
+ @Override
+ public ItemStack getItem() {
+ this.calledGetItem = true;
+ return super.getItem();
+ }
+
+ @Override
+ public void setItem(final ItemStack itemStack) {
+ super.setItem(itemStack);
+ this.calledSetItem = true;
+ }
+}
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 6dbae12bbfd47cd4e75bc3089561e8e226e9e604..9c859025302ddb2c20cf6457fa4e4eaf7fbafdd7 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1707,6 +1707,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
serverLevel.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - BlockPhysicsEvent
serverLevel.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent
serverLevel.updateLagCompensationTick(); // Paper - lag compensation
+ net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = serverLevel.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - Perf: Optimize Hoppers
profilerFiller.push(() -> serverLevel + " " + serverLevel.dimension().location());
/* Drop global time updates
if (this.tickCount % 20 == 0) {
diff --git a/net/minecraft/world/item/ItemStack.java b/net/minecraft/world/item/ItemStack.java
index c255e11cb0981bd7e0456d4fd401beb5257be597..d6361863d6a1e364de262d6199373cbd68d1c699 100644
--- a/net/minecraft/world/item/ItemStack.java
+++ b/net/minecraft/world/item/ItemStack.java
@@ -808,10 +808,16 @@ public final class ItemStack implements DataComponentHolder {
}
public ItemStack copy() {
- if (this.isEmpty()) {
+ // Paper start - Perf: Optimize Hoppers
+ return this.copy(false);
+ }
+
+ public ItemStack copy(final boolean originalItem) {
+ if (!originalItem && this.isEmpty()) {
+ // Paper end - Perf: Optimize Hoppers
return EMPTY;
} else {
- ItemStack itemStack = new ItemStack(this.getItem(), this.count, this.components.copy());
+ ItemStack itemStack = new ItemStack(originalItem ? this.item : this.getItem(), this.count, this.components.copy()); // Paper - Perf: Optimize Hoppers
itemStack.setPopTime(this.getPopTime());
return itemStack;
}
diff --git a/net/minecraft/world/level/block/entity/BlockEntity.java b/net/minecraft/world/level/block/entity/BlockEntity.java
index 2ebdf1ad323bb53dfe9eed319e25856b35a1443c..77618757c0e678532dbab814aceed83f7f1cd892 100644
--- a/net/minecraft/world/level/block/entity/BlockEntity.java
+++ b/net/minecraft/world/level/block/entity/BlockEntity.java
@@ -26,6 +26,7 @@ import net.minecraft.world.level.block.state.BlockState;
import org.slf4j.Logger;
public abstract class BlockEntity {
+ static boolean ignoreBlockEntityUpdates; // Paper - Perf: Optimize Hoppers
// CraftBukkit start - data containers
private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry();
public org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer persistentDataContainer;
@@ -196,6 +197,7 @@ public abstract class BlockEntity {
public void setChanged() {
if (this.level != null) {
+ if (ignoreBlockEntityUpdates) return; // Paper - Perf: Optimize Hoppers
setChanged(this.level, this.worldPosition, this.blockState);
}
}
diff --git a/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/net/minecraft/world/level/block/entity/HopperBlockEntity.java
index e58a32593e8b42bfc534d13457240860293dd3f4..5cd1326ad5d046c88b2b3449d610a78fa880b4cd 100644
--- a/net/minecraft/world/level/block/entity/HopperBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/HopperBlockEntity.java
@@ -139,18 +139,56 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
}
+ // Paper start - Perf: Optimize Hoppers
+ private static final int HOPPER_EMPTY = 0;
+ private static final int HOPPER_HAS_ITEMS = 1;
+ private static final int HOPPER_IS_FULL = 2;
+
+ private static int getFullState(final HopperBlockEntity hopper) {
+ hopper.unpackLootTable(null);
+
+ final List<ItemStack> hopperItems = hopper.items;
+
+ boolean empty = true;
+ boolean full = true;
+
+ for (int i = 0, len = hopperItems.size(); i < len; ++i) {
+ final ItemStack stack = hopperItems.get(i);
+ if (stack.isEmpty()) {
+ full = false;
+ continue;
+ }
+
+ if (!full) {
+ // can't be full
+ return HOPPER_HAS_ITEMS;
+ }
+
+ empty = false;
+
+ if (stack.getCount() != stack.getMaxStackSize()) {
+ // can't be full or empty
+ return HOPPER_HAS_ITEMS;
+ }
+ }
+
+ return empty ? HOPPER_EMPTY : (full ? HOPPER_IS_FULL : HOPPER_HAS_ITEMS);
+ }
+ // Paper end - Perf: Optimize Hoppers
+
private static boolean tryMoveItems(Level level, BlockPos pos, BlockState state, HopperBlockEntity blockEntity, BooleanSupplier validator) {
if (level.isClientSide) {
return false;
} else {
if (!blockEntity.isOnCooldown() && state.getValue(HopperBlock.ENABLED)) {
boolean flag = false;
- if (!blockEntity.isEmpty()) {
+ final int fullState = getFullState(blockEntity); // Paper - Perf: Optimize Hoppers
+ if (fullState != HOPPER_EMPTY) { // Paper - Perf: Optimize Hoppers
flag = ejectItems(level, pos, blockEntity);
}
- if (!blockEntity.inventoryFull()) {
- flag |= validator.getAsBoolean();
+ if (fullState != HOPPER_IS_FULL || flag) { // Paper - Perf: Optimize Hoppers
+ flag |= validator.getAsBoolean(); // Paper - note: this is not a validator, it's what adds/sucks in items
}
if (flag) {
@@ -174,6 +212,206 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
return true;
}
+ // Paper start - Perf: Optimize Hoppers
+ public static boolean skipHopperEvents;
+ private static boolean skipPullModeEventFire;
+ private static boolean skipPushModeEventFire;
+
+ private static boolean hopperPush(final Level level, final Container destination, final Direction direction, final HopperBlockEntity hopper) {
+ skipPushModeEventFire = skipHopperEvents;
+ boolean foundItem = false;
+ for (int i = 0; i < hopper.getContainerSize(); ++i) {
+ final ItemStack item = hopper.getItem(i);
+ if (!item.isEmpty()) {
+ foundItem = true;
+ ItemStack origItemStack = item;
+ ItemStack movedItem = origItemStack;
+
+ final int originalItemCount = origItemStack.getCount();
+ final int movedItemCount = Math.min(level.spigotConfig.hopperAmount, originalItemCount);
+ origItemStack.setCount(movedItemCount);
+
+ // We only need to fire the event once to give protection plugins a chance to cancel this event
+ // Because nothing uses getItem, every event call should end up the same result.
+ if (!skipPushModeEventFire) {
+ movedItem = callPushMoveEvent(destination, movedItem, hopper);
+ if (movedItem == null) { // cancelled
+ origItemStack.setCount(originalItemCount);
+ return false;
+ }
+ }
+
+ final ItemStack remainingItem = addItem(hopper, destination, movedItem, direction);
+ final int remainingItemCount = remainingItem.getCount();
+ if (remainingItemCount != movedItemCount) {
+ origItemStack = origItemStack.copy(true);
+ origItemStack.setCount(originalItemCount);
+ if (!origItemStack.isEmpty()) {
+ origItemStack.setCount(originalItemCount - movedItemCount + remainingItemCount);
+ }
+ hopper.setItem(i, origItemStack);
+ destination.setChanged();
+ return true;
+ }
+ origItemStack.setCount(originalItemCount);
+ }
+ }
+ if (foundItem && level.paperConfig().hopper.cooldownWhenFull) { // Inventory was full - cooldown
+ hopper.setCooldown(level.spigotConfig.hopperTransfer);
+ }
+ return false;
+ }
+
+ private static boolean hopperPull(final Level level, final Hopper hopper, final Container container, ItemStack origItemStack, final int i) {
+ ItemStack movedItem = origItemStack;
+ final int originalItemCount = origItemStack.getCount();
+ final int movedItemCount = Math.min(level.spigotConfig.hopperAmount, originalItemCount);
+ container.setChanged(); // original logic always marks source inv as changed even if no move happens.
+ movedItem.setCount(movedItemCount);
+
+ if (!skipPullModeEventFire) {
+ movedItem = callPullMoveEvent(hopper, container, movedItem);
+ if (movedItem == null) { // cancelled
+ origItemStack.setCount(originalItemCount);
+ // Drastically improve performance by returning true.
+ // No plugin could have relied on the behavior of false as the other call
+ // site for IMIE did not exhibit the same behavior
+ return true;
+ }
+ }
+
+ final ItemStack remainingItem = addItem(container, hopper, movedItem, null);
+ final int remainingItemCount = remainingItem.getCount();
+ if (remainingItemCount != movedItemCount) {
+ origItemStack = origItemStack.copy(true);
+ origItemStack.setCount(originalItemCount);
+ if (!origItemStack.isEmpty()) {
+ origItemStack.setCount(originalItemCount - movedItemCount + remainingItemCount);
+ }
+
+ ignoreBlockEntityUpdates = true;
+ container.setItem(i, origItemStack);
+ ignoreBlockEntityUpdates = false;
+ container.setChanged();
+ return true;
+ }
+ origItemStack.setCount(originalItemCount);
+
+ if (level.paperConfig().hopper.cooldownWhenFull) {
+ applyCooldown(hopper);
+ }
+
+ return false;
+ }
+
+ @Nullable
+ private static ItemStack callPushMoveEvent(Container destination, ItemStack itemStack, HopperBlockEntity hopper) {
+ final org.bukkit.inventory.Inventory destinationInventory = getInventory(destination);
+ final io.papermc.paper.event.inventory.PaperInventoryMoveItemEvent event = new io.papermc.paper.event.inventory.PaperInventoryMoveItemEvent(
+ hopper.getOwner(false).getInventory(),
+ org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack),
+ destinationInventory,
+ true
+ );
+ final boolean result = event.callEvent();
+ if (!event.calledGetItem && !event.calledSetItem) {
+ skipPushModeEventFire = true;
+ }
+ if (!result) {
+ applyCooldown(hopper);
+ return null;
+ }
+
+ if (event.calledSetItem) {
+ return org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
+ } else {
+ return itemStack;
+ }
+ }
+
+ @Nullable
+ private static ItemStack callPullMoveEvent(final Hopper hopper, final Container container, final ItemStack itemstack) {
+ final org.bukkit.inventory.Inventory sourceInventory = getInventory(container);
+ final org.bukkit.inventory.Inventory destination = getInventory(hopper);
+
+ // Mirror is safe as no plugins ever use this item
+ final io.papermc.paper.event.inventory.PaperInventoryMoveItemEvent event = new io.papermc.paper.event.inventory.PaperInventoryMoveItemEvent(sourceInventory, org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack), destination, false);
+ final boolean result = event.callEvent();
+ if (!event.calledGetItem && !event.calledSetItem) {
+ skipPullModeEventFire = true;
+ }
+ if (!result) {
+ applyCooldown(hopper);
+ return null;
+ }
+
+ if (event.calledSetItem) {
+ return org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
+ } else {
+ return itemstack;
+ }
+ }
+
+ private static org.bukkit.inventory.Inventory getInventory(final Container container) {
+ final org.bukkit.inventory.Inventory sourceInventory;
+ if (container instanceof net.minecraft.world.CompoundContainer compoundContainer) {
+ // Have to special-case large chests as they work oddly
+ sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer);
+ } else if (container instanceof BlockEntity blockEntity) {
+ sourceInventory = blockEntity.getOwner(false).getInventory();
+ } else if (container.getOwner() != null) {
+ sourceInventory = container.getOwner().getInventory();
+ } else {
+ sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventory(container);
+ }
+ return sourceInventory;
+ }
+
+ private static void applyCooldown(final Hopper hopper) {
+ if (hopper instanceof HopperBlockEntity blockEntity && blockEntity.getLevel() != null) {
+ blockEntity.setCooldown(blockEntity.getLevel().spigotConfig.hopperTransfer);
+ }
+ }
+
+ private static boolean allMatch(Container container, Direction direction, java.util.function.BiPredicate<ItemStack, Integer> test) {
+ if (container instanceof WorldlyContainer) {
+ for (int slot : ((WorldlyContainer) container).getSlotsForFace(direction)) {
+ if (!test.test(container.getItem(slot), slot)) {
+ return false;
+ }
+ }
+ } else {
+ int size = container.getContainerSize();
+ for (int slot = 0; slot < size; slot++) {
+ if (!test.test(container.getItem(slot), slot)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private static boolean anyMatch(Container container, Direction direction, java.util.function.BiPredicate<ItemStack, Integer> test) {
+ if (container instanceof WorldlyContainer) {
+ for (int slot : ((WorldlyContainer) container).getSlotsForFace(direction)) {
+ if (test.test(container.getItem(slot), slot)) {
+ return true;
+ }
+ }
+ } else {
+ int size = container.getContainerSize();
+ for (int slot = 0; slot < size; slot++) {
+ if (test.test(container.getItem(slot), slot)) {
+ return true;
+ }
+ }
+ }
+ return true;
+ }
+ private static final java.util.function.BiPredicate<ItemStack, Integer> STACK_SIZE_TEST = (itemStack, i) -> itemStack.getCount() >= itemStack.getMaxStackSize();
+ private static final java.util.function.BiPredicate<ItemStack, Integer> IS_EMPTY_TEST = (itemStack, i) -> itemStack.isEmpty();
+ // Paper end - Perf: Optimize Hoppers
+
private static boolean ejectItems(Level level, BlockPos pos, HopperBlockEntity blockEntity) {
Container attachedContainer = getAttachedContainer(level, pos, blockEntity);
if (attachedContainer == null) {
@@ -183,57 +421,60 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
if (isFullContainer(attachedContainer, opposite)) {
return false;
} else {
- for (int i = 0; i < blockEntity.getContainerSize(); i++) {
- ItemStack item = blockEntity.getItem(i);
- if (!item.isEmpty()) {
- int count = item.getCount();
- // CraftBukkit start - Call event when pushing items into other inventories
- ItemStack original = item.copy();
- org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(
- blockEntity.removeItem(i, 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) {
- destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer);
- } else if (attachedContainer.getOwner() != null) {
- destinationInventory = attachedContainer.getOwner().getInventory();
- } else {
- destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventory(attachedContainer);
- }
-
- org.bukkit.event.inventory.InventoryMoveItemEvent event = new org.bukkit.event.inventory.InventoryMoveItemEvent(
- blockEntity.getOwner().getInventory(),
- oitemstack,
- destinationInventory,
- true
- );
- if (!event.callEvent()) {
- blockEntity.setItem(i, original);
- blockEntity.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);
- // CraftBukkit end
-
- if (itemStack.isEmpty()) {
- attachedContainer.setChanged();
- return true;
- }
-
- item.setCount(count);
- // Spigot start
- item.shrink(origCount - itemStack.getCount());
- if (count <= level.spigotConfig.hopperAmount) {
- // Spigot end
- blockEntity.setItem(i, item);
- }
- }
- }
-
- return false;
+ // Paper start - Perf: Optimize Hoppers
+ return hopperPush(level, attachedContainer, opposite, blockEntity);
+ //for (int i = 0; i < blockEntity.getContainerSize(); i++) {
+ // ItemStack item = blockEntity.getItem(i);
+ // if (!item.isEmpty()) {
+ // int count = item.getCount();
+ // // CraftBukkit start - Call event when pushing items into other inventories
+ // ItemStack original = item.copy();
+ // org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(
+ // blockEntity.removeItem(i, 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) {
+ // destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer);
+ // } else if (attachedContainer.getOwner() != null) {
+ // destinationInventory = attachedContainer.getOwner().getInventory();
+ // } else {
+ // destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventory(attachedContainer);
+ // }
+
+ // org.bukkit.event.inventory.InventoryMoveItemEvent event = new org.bukkit.event.inventory.InventoryMoveItemEvent(
+ // blockEntity.getOwner().getInventory(),
+ // oitemstack,
+ // destinationInventory,
+ // true
+ // );
+ // if (!event.callEvent()) {
+ // blockEntity.setItem(i, original);
+ // blockEntity.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);
+ // // CraftBukkit end
+
+ // if (itemStack.isEmpty()) {
+ // attachedContainer.setChanged();
+ // return true;
+ // }
+
+ // item.setCount(count);
+ // // Spigot start
+ // item.shrink(origCount - itemStack.getCount());
+ // if (count <= level.spigotConfig.hopperAmount) {
+ // // Spigot end
+ // blockEntity.setItem(i, item);
+ // }
+ // }
+ //}
+
+ //return false;
+ // Paper end - Perf: Optimize Hoppers
}
}
}
@@ -288,6 +529,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
Container sourceContainer = getSourceContainer(level, hopper, blockPos, blockState);
if (sourceContainer != null) {
Direction direction = Direction.DOWN;
+ skipPullModeEventFire = skipHopperEvents; // Paper - Perf: Optimize Hoppers
for (int i : getSlots(sourceContainer, direction)) {
if (tryTakeInItemFromSlot(hopper, sourceContainer, i, direction, level)) { // Spigot
@@ -313,55 +555,58 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
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();
- // CraftBukkit start - Call event on collection of items from inventories into the hopper
- ItemStack original = item.copy();
- org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(
- container.removeItem(slot, level.spigotConfig.hopperAmount) // Spigot
- );
-
- org.bukkit.inventory.Inventory sourceInventory;
- // Have to special case large chests as they work oddly
- if (container instanceof final net.minecraft.world.CompoundContainer compoundContainer) {
- sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer);
- } else if (container.getOwner() != null) {
- sourceInventory = container.getOwner().getInventory();
- } else {
- sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventory(container);
- }
-
- org.bukkit.event.inventory.InventoryMoveItemEvent event = new org.bukkit.event.inventory.InventoryMoveItemEvent(
- sourceInventory,
- oitemstack,
- hopper.getOwner().getInventory(),
- false
- );
-
- if (!event.callEvent()) {
- container.setItem(slot, original);
-
- if (hopper instanceof final HopperBlockEntity hopperBlockEntity) {
- hopperBlockEntity.setCooldown(level.spigotConfig.hopperTransfer); // Spigot
- }
-
- return false;
- }
- int origCount = event.getItem().getAmount(); // Spigot
- ItemStack itemStack = HopperBlockEntity.addItem(container, hopper, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()), null);
- // CraftBukkit end
-
- if (itemStack.isEmpty()) {
- container.setChanged();
- return true;
- }
-
- item.setCount(count);
- // Spigot start
- item.shrink(origCount - itemStack.getCount());
- if (count <= level.spigotConfig.hopperAmount) {
- // Spigot end
- container.setItem(slot, item);
- }
+ // Paper start - Perf: Optimize Hoppers
+ return hopperPull(level, hopper, container, item, slot);
+ //int count = item.getCount();
+ //// CraftBukkit start - Call event on collection of items from inventories into the hopper
+ //ItemStack original = item.copy();
+ //org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(
+ // container.removeItem(slot, level.spigotConfig.hopperAmount) // Spigot
+ //);
+
+ //org.bukkit.inventory.Inventory sourceInventory;
+ //// Have to special case large chests as they work oddly
+ //if (container instanceof final net.minecraft.world.CompoundContainer compoundContainer) {
+ // sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer);
+ //} else if (container.getOwner() != null) {
+ // sourceInventory = container.getOwner().getInventory();
+ //} else {
+ // sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventory(container);
+ //}
+
+ //org.bukkit.event.inventory.InventoryMoveItemEvent event = new org.bukkit.event.inventory.InventoryMoveItemEvent(
+ // sourceInventory,
+ // oitemstack,
+ // hopper.getOwner().getInventory(),
+ // false
+ //);
+
+ //if (!event.callEvent()) {
+ // container.setItem(slot, original);
+
+ // if (hopper instanceof final HopperBlockEntity hopperBlockEntity) {
+ // hopperBlockEntity.setCooldown(level.spigotConfig.hopperTransfer); // Spigot
+ // }
+
+ // return false;
+ //}
+ //int origCount = event.getItem().getAmount(); // Spigot
+ //ItemStack itemStack = HopperBlockEntity.addItem(container, hopper, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()), null);
+ //// CraftBukkit end
+
+ //if (itemStack.isEmpty()) {
+ // container.setChanged();
+ // return true;
+ //}
+
+ //item.setCount(count);
+ //// Spigot start
+ //item.shrink(origCount - itemStack.getCount());
+ //if (count <= level.spigotConfig.hopperAmount) {
+ // // Spigot end
+ // container.setItem(slot, item);
+ //}
+ // Paper end - Perf: Optimize Hoppers
}
return false;
@@ -370,13 +615,15 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
public static boolean addItem(Container container, ItemEntity item) {
boolean flag = false;
// CraftBukkit start
+ if (org.bukkit.event.inventory.InventoryPickupItemEvent.getHandlerList().getRegisteredListeners().length > 0) { // Paper - optimize hoppers
org.bukkit.event.inventory.InventoryPickupItemEvent event = new org.bukkit.event.inventory.InventoryPickupItemEvent(
- container.getOwner().getInventory(), (org.bukkit.entity.Item) item.getBukkitEntity()
+ getInventory(container), (org.bukkit.entity.Item) item.getBukkitEntity() // Paper - Perf: Optimize Hoppers; use getInventory() to avoid snapshot creation
);
if (!event.callEvent()) {
return false;
}
// CraftBukkit end
+ } // Paper - Perf: Optimize Hoppers
ItemStack itemStack = item.getItem().copy();
ItemStack itemStack1 = addItem(null, container, itemStack, null);
if (itemStack1.isEmpty()) {
@@ -431,7 +678,9 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
stack = stack.split(destination.getMaxStackSize());
}
// Spigot end
+ ignoreBlockEntityUpdates = true; // Paper - Perf: Optimize Hoppers
destination.setItem(slot, stack);
+ ignoreBlockEntityUpdates = false; // Paper - Perf: Optimize Hoppers
stack = leftover; // Paper - Make hoppers respect inventory max stack size
flag = true;
} else if (canMergeItems(item, stack)) {
@@ -519,13 +768,19 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@Nullable
public static Container getContainerAt(Level level, BlockPos pos) {
- return getContainerAt(level, pos, level.getBlockState(pos), pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5);
+ return getContainerAt(level, pos, level.getBlockState(pos), pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, true); // Paper - Optimize hoppers
}
@Nullable
private static Container getContainerAt(Level level, BlockPos pos, BlockState state, double x, double y, double z) {
+ // Paper start - Perf: Optimize Hoppers
+ return HopperBlockEntity.getContainerAt(level, pos, state, x, y, z, false);
+ }
+ @Nullable
+ private static Container getContainerAt(Level level, BlockPos pos, BlockState state, double x, double y, double z, final boolean optimizeEntities) {
+ // Paper end - Perf: Optimize Hoppers
Container blockContainer = getBlockContainer(level, pos, state);
- if (blockContainer == null) {
+ if (blockContainer == null && (!optimizeEntities || !level.paperConfig().hopper.ignoreOccludingBlocks || !state.getBukkitMaterial().isOccluding())) { // Paper - Perf: Optimize Hoppers
blockContainer = getEntityContainer(level, x, y, z);
}
@@ -551,14 +806,14 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@Nullable
private static Container getEntityContainer(Level level, double x, double y, double z) {
- List<Entity> entities = level.getEntities(
- (Entity)null, new AABB(x - 0.5, y - 0.5, z - 0.5, x + 0.5, y + 0.5, z + 0.5), EntitySelector.CONTAINER_ENTITY_SELECTOR
+ List<Entity> entities = level.getEntitiesOfClass(
+ (Class) Container.class, new AABB(x - 0.5, y - 0.5, z - 0.5, x + 0.5, y + 0.5, z + 0.5), EntitySelector.CONTAINER_ENTITY_SELECTOR // Paper - Perf: Optimize hoppers
);
return !entities.isEmpty() ? (Container)entities.get(level.random.nextInt(entities.size())) : null;
}
private static boolean canMergeItems(ItemStack stack1, ItemStack stack2) {
- return stack1.getCount() <= stack1.getMaxStackSize() && ItemStack.isSameItemSameComponents(stack1, stack2);
+ return stack1.getCount() < stack1.getMaxStackSize() && ItemStack.isSameItemSameComponents(stack1, stack2); // Paper - Perf: Optimize Hoppers; used to return true for full itemstacks?!
}
@Override
diff --git a/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java b/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
index 73b3ddb120d6b6f89e478960e78bed415baea205..f9c31da81d84033abfc1179fc643bceffe35da17 100644
--- a/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
@@ -53,7 +53,7 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc
@Override
public ItemStack getItem(int index) {
- this.unpackLootTable(null);
+ if (index == 0) this.unpackLootTable(null); // Paper - Perf: Optimize Hoppers
return super.getItem(index);
}

View file

@ -5,61 +5,62 @@ 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/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 650126e6445c0458c6a6649c235908bfeea428cd..d248671b2e1c6256fc4d74320bdb29ca078bad0b 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -561,7 +561,7 @@ public class ServerGamePacketListenerImpl
return;
}
- boolean flag = worldserver.noCollision(entity, entity.getBoundingBox().deflate(0.0625D));
+ AABB oldBox = entity.getBoundingBox(); // Paper - copy from player movement packet
d6 = d3 - this.vehicleLastGoodX; // Paper - diff on change, used for checking large move vectors above
d7 = d4 - this.vehicleLastGoodY; // Paper - diff on change, used for checking large move vectors above
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
- boolean flag = serverLevel.noCollision(rootVehicle, rootVehicle.getBoundingBox().deflate(0.0625));
+ AABB oldBox = rootVehicle.getBoundingBox(); // Paper - copy from player movement packet
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
@@ -571,6 +571,7 @@ public class ServerGamePacketListenerImpl
}
entity.move(MoverType.PLAYER, new Vec3(d6, d7, d8));
+ boolean didCollide = toX != entity.getX() || toY != entity.getY() || toZ != entity.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 d11 = d7;
d6 = d3 - entity.getX();
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
rootVehicle.move(MoverType.PLAYER, new Vec3(d3, d4, d5));
+ 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; // Paper - Decompile fix, was named d11 previously, is now gone in the source
d3 = d - rootVehicle.getX();
d4 = d1 - rootVehicle.getY();
@@ -582,14 +583,22 @@ public class ServerGamePacketListenerImpl
d7 = d3 * d3 + d4 * d4 + d5 * d5;
boolean flag2 = false;
if (d10 > org.spigotmc.SpigotConfig.movedWronglyThreshold) { // Spigot
if (d7 > org.spigotmc.SpigotConfig.movedWronglyThreshold) { // Spigot
- flag2 = true;
+ flag2 = true; // Paper - diff on change, this should be moved wrongly
ServerGamePacketListenerImpl.LOGGER.warn("{} (vehicle of {}) moved wrongly! {}", new Object[]{entity.getName().getString(), this.player.getName().getString(), Math.sqrt(d10)});
LOGGER.warn("{} (vehicle of {}) moved wrongly! {}", rootVehicle.getName().getString(), this.player.getName().getString(), Math.sqrt(d7));
}
entity.absMoveTo(d3, d4, d5, f, f1);
this.player.absMoveTo(d3, d4, d5, this.player.getYRot(), this.player.getXRot()); // CraftBukkit
- boolean flag3 = worldserver.noCollision(entity, entity.getBoundingBox().deflate(0.0625D));
rootVehicle.absMoveTo(d, d1, d2, f, f1);
this.player.absMoveTo(d, d1, d2, this.player.getYRot(), this.player.getXRot()); // CraftBukkit
- boolean flag3 = serverLevel.noCollision(rootVehicle, rootVehicle.getBoundingBox().deflate(0.0625));
- if (flag && (flag2 || !flag3)) {
+ // Paper start - optimise out extra getCubes
+ boolean teleportBack = flag2; // violating this is always a fail
+ if (!teleportBack) {
+ // note: only call after setLocation, or else getBoundingBox is wrong
+ AABB newBox = entity.getBoundingBox();
+ AABB newBox = rootVehicle.getBoundingBox();
+ if (didCollide || !oldBox.equals(newBox)) {
+ teleportBack = this.hasNewCollision(worldserver, entity, oldBox, newBox);
+ teleportBack = this.hasNewCollision(serverLevel, rootVehicle, oldBox, newBox);
+ } // else: no collision at all detected, why do we care?
+ }
+ if (teleportBack) { // Paper end - optimise out extra getCubes
entity.absMoveTo(d0, d1, d2, f, f1);
this.player.absMoveTo(d0, d1, d2, this.player.getYRot(), this.player.getXRot()); // CraftBukkit
this.send(ClientboundMoveVehiclePacket.fromEntity(entity));
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
rootVehicle.absMoveTo(x, y, z, f, f1);
this.player.absMoveTo(x, y, z, this.player.getYRot(), this.player.getXRot()); // CraftBukkit
this.send(ClientboundMoveVehiclePacket.fromEntity(rootVehicle));
@@ -667,9 +676,32 @@ public class ServerGamePacketListenerImpl
}
private boolean noBlocksAround(Entity entity) {
- return entity.level().getBlockStates(entity.getBoundingBox().inflate(0.0625D).expandTowards(0.0D, -0.55D, 0.0D)).allMatch(BlockBehaviour.BlockStateBase::isAir);
- return entity.level()
- .getBlockStates(entity.getBoundingBox().inflate(0.0625).expandTowards(0.0, -0.55, 0.0))
- .allMatch(BlockBehaviour.BlockStateBase::isAir);
+ // Paper start - stop using streams, this is already a known fixed problem in Entity#move
+ AABB box = entity.getBoundingBox().inflate(0.0625D).expandTowards(0.0D, -0.55D, 0.0D);
+ AABB box = entity.getBoundingBox().inflate(0.0625).expandTowards(0.0, -0.55, 0.0);
+ int minX = Mth.floor(box.minX);
+ int minY = Mth.floor(box.minY);
+ int minZ = Mth.floor(box.minZ);
@ -67,15 +68,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ int maxY = Mth.floor(box.maxY);
+ int maxZ = Mth.floor(box.maxZ);
+
+ Level world = entity.level();
+ Level level = entity.level();
+ BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
+
+ for (int y = minY; y <= maxY; ++y) {
+ for (int z = minZ; z <= maxZ; ++z) {
+ for (int x = minX; x <= maxX; ++x) {
+ pos.set(x, y, z);
+ BlockState type = world.getBlockStateIfLoaded(pos);
+ if (type != null && !type.isAir()) {
+ BlockState blockState = level.getBlockStateIfLoaded(pos);
+ if (blockState != null && !blockState.isAir()) {
+ return false;
+ }
+ }
@ -87,61 +88,61 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
}
@Override
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
@@ -1361,7 +1393,7 @@ public class ServerGamePacketListenerImpl
}
}
- AABB axisalignedbb = this.player.getBoundingBox();
+ AABB axisalignedbb = this.player.getBoundingBox(); // Paper - diff on change, should be old AABB
d6 = d0 - this.lastGoodX; // Paper - diff on change, used for checking large move vectors above
d7 = d1 - this.lastGoodY; // Paper - diff on change, used for checking large move vectors above
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
this.player.move(MoverType.PLAYER, new Vec3(d6, d7, d8));
- 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
@@ -1400,6 +1432,7 @@ public class ServerGamePacketListenerImpl
boolean flag1 = this.player.verticalCollisionBelow;
this.player.move(MoverType.PLAYER, new Vec3(d3, d4, d5));
this.player.onGround = packet.isOnGround(); // CraftBukkit - SPIGOT-5810, SPIGOT-5835, SPIGOT-6828: reset by this.player.move
+ 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...
// Paper start - prevent position desync
if (this.awaitingPositionFromClient != null) {
return; // ... thanks Mojang for letting move calls teleport across dimensions.
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
@@ -1432,7 +1465,17 @@ public class ServerGamePacketListenerImpl
}
// Paper start - Add fail move event
- boolean teleportBack = !this.player.noPhysics && !this.player.isSleeping() && (movedWrongly && worldserver.noCollision(this.player, axisalignedbb) || this.isPlayerCollidingWithAnythingNew(worldserver, axisalignedbb, d0, d1, d2));
- boolean teleportBack = !this.player.noPhysics && !this.player.isSleeping() && (movedWrongly && serverLevel.noCollision(this.player, boundingBox) || this.isPlayerCollidingWithAnythingNew(serverLevel, boundingBox, d, d1, d2));
+ // Paper start - optimise out extra getCubes
+ boolean teleportBack = !this.player.noPhysics && !this.player.isSleeping() && movedWrongly;
+ this.player.absMoveTo(d0, d1, d2, f, f1); // prevent desync by tping to the set position, dropped for unknown reasons by mojang
+ this.player.absMoveTo(d, d1, d2, f, f1); // prevent desync by tping to the set position, dropped for unknown reasons by mojang
+ if (!this.player.noPhysics && !this.player.isSleeping() && !teleportBack) {
+ AABB newBox = this.player.getBoundingBox();
+ if (didCollide || !axisalignedbb.equals(newBox)) {
+ if (didCollide || !boundingBox.equals(newBox)) {
+ // note: only call after setLocation, or else getBoundingBox is wrong
+ teleportBack = this.hasNewCollision(worldserver, this.player, axisalignedbb, newBox);
+ teleportBack = this.hasNewCollision(serverLevel, this.player, boundingBox, newBox);
+ } // else: no collision at all detected, why do we care?
+ }
+ // Paper end - optimise out extra getCubes
if (teleportBack) {
io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.CLIPPED_INTO_BLOCK,
toX, toY, toZ, toYaw, toPitch, false);
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
@@ -1568,7 +1611,7 @@ public class ServerGamePacketListenerImpl
private boolean updateAwaitingTeleport() {
if (this.awaitingPositionFromClient != null) {
- if (this.tickCount - this.awaitingTeleportTime > 20) {
+ if (false && this.tickCount - this.awaitingTeleportTime > 20) { // Paper - this will greatly screw with clients with > 1000ms RTT
this.awaitingTeleportTime = this.tickCount;
this.teleport(this.awaitingPositionFromClient.x, this.awaitingPositionFromClient.y, this.awaitingPositionFromClient.z, this.player.getYRot(), this.player.getXRot());
}
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
this.teleport(
this.awaitingPositionFromClient.x,
@@ -1587,6 +1630,33 @@ public class ServerGamePacketListenerImpl
}
}
+ // Paper start - optimise out extra getCubes
+ private boolean hasNewCollision(final ServerLevel world, final Entity entity, final AABB oldBox, final AABB newBox) {
+ private boolean hasNewCollision(final ServerLevel level, final Entity entity, final AABB oldBox, final AABB newBox) {
+ final List<AABB> collisionsBB = new java.util.ArrayList<>();
+ final List<VoxelShape> collisionsVoxel = new java.util.ArrayList<>();
+ ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.getCollisions(
+ world, entity, newBox, collisionsVoxel, collisionsBB,
+ 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,
+ null, null
+ );
@ -163,6 +164,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ return false;
+ }
+ // Paper end - optimise out extra getCubes
private boolean isPlayerCollidingWithAnythingNew(LevelReader world, AABB box, double newX, double newY, double newZ) {
AABB axisalignedbb1 = this.player.getBoundingBox().move(newX - this.player.getX(), newY - this.player.getY(), newZ - this.player.getZ());
Iterable<VoxelShape> iterable = world.getCollisions(this.player, axisalignedbb1.deflate(9.999999747378752E-6D));
private boolean isPlayerCollidingWithAnythingNew(LevelReader level, AABB box, double x, double y, double z) {
AABB aabb = this.player.getBoundingBox().move(x - this.player.getX(), y - this.player.getY(), z - this.player.getZ());
Iterable<VoxelShape> collisions = level.getCollisions(this.player, aabb.deflate(1.0E-5F));

View file

@ -1,6 +1,6 @@
--- a/data/minecraft/loot_table/chests/trial_chambers/intersection_barrel.json
+++ b/data/minecraft/loot_table/chests/trial_chambers/intersection_barrel.json
@@ -70,15 +70,6 @@
@@ -70,15 +_,6 @@
"add": false,
"count": 1.0,
"function": "minecraft:set_count"

View file

@ -0,0 +1,12 @@
--- a/data/minecraft/worldgen/noise_settings/amplified.json
+++ b/data/minecraft/worldgen/noise_settings/amplified.json
@@ -389,7 +_,8 @@
{
"type": "minecraft:condition",
"if_true": {
- "type": "minecraft:vertical_gradient",
+ "type": "paper:optionally_flat_bedrock_condition_source",
+ "is_roof": false,
"false_at_and_above": {
"above_bottom": 5
},

View file

@ -0,0 +1,22 @@
--- a/data/minecraft/worldgen/noise_settings/caves.json
+++ b/data/minecraft/worldgen/noise_settings/caves.json
@@ -110,7 +_,8 @@
"if_true": {
"type": "minecraft:not",
"invert": {
- "type": "minecraft:vertical_gradient",
+ "type": "paper:optionally_flat_bedrock_condition_source",
+ "is_roof": true,
"false_at_and_above": {
"below_top": 0
},
@@ -130,7 +_,8 @@
{
"type": "minecraft:condition",
"if_true": {
- "type": "minecraft:vertical_gradient",
+ "type": "paper:optionally_flat_bedrock_condition_source",
+ "is_roof": false,
"false_at_and_above": {
"above_bottom": 5
},

View file

@ -0,0 +1,12 @@
--- a/data/minecraft/worldgen/noise_settings/large_biomes.json
+++ b/data/minecraft/worldgen/noise_settings/large_biomes.json
@@ -389,7 +_,8 @@
{
"type": "minecraft:condition",
"if_true": {
- "type": "minecraft:vertical_gradient",
+ "type": "paper:optionally_flat_bedrock_condition_source",
+ "is_roof": false,
"false_at_and_above": {
"above_bottom": 5
},

View file

@ -0,0 +1,22 @@
--- a/data/minecraft/worldgen/noise_settings/nether.json
+++ b/data/minecraft/worldgen/noise_settings/nether.json
@@ -108,7 +_,8 @@
{
"type": "minecraft:condition",
"if_true": {
- "type": "minecraft:vertical_gradient",
+ "type": "paper:optionally_flat_bedrock_condition_source",
+ "is_roof": false,
"false_at_and_above": {
"above_bottom": 5
},
@@ -129,7 +_,8 @@
"if_true": {
"type": "minecraft:not",
"invert": {
- "type": "minecraft:vertical_gradient",
+ "type": "paper:optionally_flat_bedrock_condition_source",
+ "is_roof": true,
"false_at_and_above": {
"below_top": 0
},

View file

@ -0,0 +1,12 @@
--- a/data/minecraft/worldgen/noise_settings/overworld.json
+++ b/data/minecraft/worldgen/noise_settings/overworld.json
@@ -389,7 +_,8 @@
{
"type": "minecraft:condition",
"if_true": {
- "type": "minecraft:vertical_gradient",
+ "type": "paper:optionally_flat_bedrock_condition_source",
+ "is_roof": false,
"false_at_and_above": {
"above_bottom": 5
},

View file

@ -0,0 +1,244 @@
--- /dev/null
+++ b/ca/spottedleaf/moonrise/paper/PaperHooks.java
@@ -1,0 +_,241 @@
+package ca.spottedleaf.moonrise.paper;
+
+import ca.spottedleaf.moonrise.common.PlatformHooks;
+import ca.spottedleaf.moonrise.paper.util.BaseChunkSystemHooks;
+import com.mojang.datafixers.DSL;
+import com.mojang.datafixers.DataFixer;
+import com.mojang.serialization.Dynamic;
+import java.util.Collection;
+import net.minecraft.core.BlockPos;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.NbtOps;
+import net.minecraft.server.level.ChunkHolder;
+import net.minecraft.server.level.GenerationChunkHolder;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.entity.boss.EnderDragonPart;
+import net.minecraft.world.level.BlockGetter;
+import net.minecraft.world.level.ChunkPos;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.level.chunk.ChunkAccess;
+import net.minecraft.world.level.chunk.LevelChunk;
+import net.minecraft.world.level.chunk.ProtoChunk;
+import net.minecraft.world.level.chunk.storage.SerializableChunkData;
+import net.minecraft.world.level.entity.EntityTypeTest;
+import net.minecraft.world.phys.AABB;
+import java.util.List;
+import java.util.function.Predicate;
+
+public final class PaperHooks extends BaseChunkSystemHooks implements PlatformHooks {
+
+ @Override
+ public String getBrand() {
+ return "Paper";
+ }
+
+ @Override
+ public int getLightEmission(final BlockState blockState, final BlockGetter world, final BlockPos pos) {
+ return blockState.getLightEmission();
+ }
+
+ @Override
+ public Predicate<BlockState> maybeHasLightEmission() {
+ return (final BlockState state) -> {
+ return state.getLightEmission() != 0;
+ };
+ }
+
+ @Override
+ public boolean hasCurrentlyLoadingChunk() {
+ return false;
+ }
+
+ @Override
+ public LevelChunk getCurrentlyLoadingChunk(final GenerationChunkHolder holder) {
+ return null;
+ }
+
+ @Override
+ public void setCurrentlyLoading(final GenerationChunkHolder holder, final LevelChunk levelChunk) {
+
+ }
+
+ @Override
+ public void chunkFullStatusComplete(final LevelChunk newChunk, final ProtoChunk original) {
+
+ }
+
+ @Override
+ public boolean allowAsyncTicketUpdates() {
+ return true;
+ }
+
+ @Override
+ public void onChunkHolderTicketChange(final ServerLevel world, final ChunkHolder holder, final int oldLevel, final int newLevel) {
+
+ }
+
+ @Override
+ public void chunkUnloadFromWorld(final LevelChunk chunk) {
+
+ }
+
+ @Override
+ public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final SerializableChunkData data) {
+
+ }
+
+ @Override
+ public void onChunkWatch(final ServerLevel world, final LevelChunk chunk, final ServerPlayer player) {
+
+ }
+
+ @Override
+ public void onChunkUnWatch(final ServerLevel world, final ChunkPos chunk, final ServerPlayer player) {
+
+ }
+
+ @Override
+ public void addToGetEntities(final Level world, final Entity entity, final AABB boundingBox, final Predicate<? super Entity> predicate, final List<Entity> into) {
+ final Collection<EnderDragonPart> parts = world.dragonParts();
+ if (parts.isEmpty()) {
+ return;
+ }
+
+ for (final EnderDragonPart part : parts) {
+ if (part != entity && part.getBoundingBox().intersects(boundingBox) && (predicate == null || predicate.test(part))) {
+ into.add(part);
+ }
+ }
+ }
+
+ @Override
+ public <T extends Entity> void addToGetEntities(final Level world, final EntityTypeTest<Entity, T> entityTypeTest, final AABB boundingBox, final Predicate<? super T> predicate, final List<? super T> into, final int maxCount) {
+ if (into.size() >= maxCount) {
+ // fix neoforge issue: do not add if list is already full
+ return;
+ }
+
+ final Collection<EnderDragonPart> parts = world.dragonParts();
+ if (parts.isEmpty()) {
+ return;
+ }
+ for (final EnderDragonPart part : parts) {
+ if (!part.getBoundingBox().intersects(boundingBox)) {
+ continue;
+ }
+ final T casted = (T)entityTypeTest.tryCast(part);
+ if (casted != null && (predicate == null || predicate.test(casted))) {
+ into.add(casted);
+ if (into.size() >= maxCount) {
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void entityMove(final Entity entity, final long oldSection, final long newSection) {
+
+ }
+
+ @Override
+ public boolean screenEntity(final ServerLevel world, final Entity entity, final boolean fromDisk, final boolean event) {
+ return true;
+ }
+
+ @Override
+ public boolean configFixMC224294() {
+ return true;
+ }
+
+ @Override
+ public boolean configAutoConfigSendDistance() {
+ return io.papermc.paper.configuration.GlobalConfiguration.get().chunkLoadingAdvanced.autoConfigSendDistance;
+ }
+
+ @Override
+ public double configPlayerMaxLoadRate() {
+ return io.papermc.paper.configuration.GlobalConfiguration.get().chunkLoadingBasic.playerMaxChunkLoadRate;
+ }
+
+ @Override
+ public double configPlayerMaxGenRate() {
+ return io.papermc.paper.configuration.GlobalConfiguration.get().chunkLoadingBasic.playerMaxChunkGenerateRate;
+ }
+
+ @Override
+ public double configPlayerMaxSendRate() {
+ return io.papermc.paper.configuration.GlobalConfiguration.get().chunkLoadingBasic.playerMaxChunkSendRate;
+ }
+
+ @Override
+ public int configPlayerMaxConcurrentLoads() {
+ return io.papermc.paper.configuration.GlobalConfiguration.get().chunkLoadingAdvanced.playerMaxConcurrentChunkLoads;
+ }
+
+ @Override
+ public int configPlayerMaxConcurrentGens() {
+ return io.papermc.paper.configuration.GlobalConfiguration.get().chunkLoadingAdvanced.playerMaxConcurrentChunkGenerates;
+ }
+
+ @Override
+ public long configAutoSaveInterval(final ServerLevel world) {
+ return world.paperConfig().chunks.autoSaveInterval.value();
+ }
+
+ @Override
+ public int configMaxAutoSavePerTick(final ServerLevel world) {
+ return world.paperConfig().chunks.maxAutoSaveChunksPerTick;
+ }
+
+ @Override
+ public boolean configFixMC159283() {
+ return true;
+ }
+
+ @Override
+ public boolean forceNoSave(final ChunkAccess chunk) {
+ return chunk instanceof LevelChunk levelChunk && levelChunk.mustNotSave;
+ }
+
+ @Override
+ public CompoundTag convertNBT(final DSL.TypeReference type, final DataFixer dataFixer, final CompoundTag nbt,
+ final int fromVersion, final int toVersion) {
+ return (CompoundTag)dataFixer.update(
+ type, new Dynamic<>(NbtOps.INSTANCE, nbt), fromVersion, toVersion
+ ).getValue();
+ }
+
+ @Override
+ public boolean hasMainChunkLoadHook() {
+ return false;
+ }
+
+ @Override
+ public void mainChunkLoad(final ChunkAccess chunk, final SerializableChunkData chunkData) {
+
+ }
+
+ @Override
+ public List<Entity> modifySavedEntities(final ServerLevel world, final int chunkX, final int chunkZ, final List<Entity> entities) {
+ return entities;
+ }
+
+ @Override
+ public void unloadEntity(final Entity entity) {
+ entity.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK, org.bukkit.event.entity.EntityRemoveEvent.Cause.UNLOAD);
+ }
+
+ @Override
+ public void postLoadProtoChunk(final ServerLevel world, final ProtoChunk chunk) {
+ net.minecraft.world.level.chunk.status.ChunkStatusTasks.postLoadProtoChunk(world, chunk.getEntities());
+ }
+
+ @Override
+ public int modifyEntityTrackingRange(final Entity entity, final int currentRange) {
+ return org.spigotmc.TrackingRange.getEntityTrackingRange(entity, currentRange);
+ }
+}

View file

@ -0,0 +1,335 @@
--- /dev/null
+++ b/ca/spottedleaf/moonrise/paper/util/BaseChunkSystemHooks.java
@@ -1,0 +_,332 @@
+package ca.spottedleaf.moonrise.paper.util;
+
+import ca.spottedleaf.concurrentutil.util.Priority;
+import com.mojang.logging.LogUtils;
+import net.minecraft.server.level.ChunkHolder;
+import net.minecraft.server.level.ChunkResult;
+import net.minecraft.server.level.FullChunkStatus;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.server.level.TicketType;
+import net.minecraft.world.level.ChunkPos;
+import net.minecraft.world.level.chunk.ChunkAccess;
+import net.minecraft.world.level.chunk.LevelChunk;
+import net.minecraft.world.level.chunk.status.ChunkPyramid;
+import net.minecraft.world.level.chunk.status.ChunkStatus;
+import net.minecraft.world.level.chunk.status.ChunkStep;
+import org.bukkit.Bukkit;
+import org.slf4j.Logger;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
+
+public abstract class BaseChunkSystemHooks implements ca.spottedleaf.moonrise.common.util.ChunkSystemHooks {
+
+ private static final Logger LOGGER = LogUtils.getLogger();
+ private static final ChunkStep FULL_CHUNK_STEP = ChunkPyramid.GENERATION_PYRAMID.getStepTo(ChunkStatus.FULL);
+ private static final TicketType<Long> CHUNK_LOAD = TicketType.create("chunk_load", Long::compareTo);
+
+ private long chunkLoadCounter = 0L;
+
+ private static int getDistance(final ChunkStatus status) {
+ return FULL_CHUNK_STEP.getAccumulatedRadiusOf(status);
+ }
+
+ @Override
+ public void scheduleChunkTask(final ServerLevel level, final int chunkX, final int chunkZ, final Runnable run) {
+ this.scheduleChunkTask(level, chunkX, chunkZ, run, Priority.NORMAL);
+ }
+
+ @Override
+ public void scheduleChunkTask(final ServerLevel level, final int chunkX, final int chunkZ, final Runnable run, final Priority priority) {
+ level.chunkSource.mainThreadProcessor.execute(run);
+ }
+
+ @Override
+ public void scheduleChunkLoad(final ServerLevel level, final int chunkX, final int chunkZ, final boolean gen,
+ final ChunkStatus toStatus, final boolean addTicket, final Priority priority,
+ final Consumer<ChunkAccess> onComplete) {
+ if (gen) {
+ this.scheduleChunkLoad(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
+ return;
+ }
+ this.scheduleChunkLoad(level, chunkX, chunkZ, ChunkStatus.EMPTY, addTicket, priority, (final ChunkAccess chunk) -> {
+ if (chunk == null) {
+ if (onComplete != null) {
+ onComplete.accept(null);
+ }
+ } else {
+ if (chunk.getPersistedStatus().isOrAfter(toStatus)) {
+ BaseChunkSystemHooks.this.scheduleChunkLoad(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
+ } else {
+ if (onComplete != null) {
+ onComplete.accept(null);
+ }
+ }
+ }
+ });
+ }
+
+ @Override
+ public void scheduleChunkLoad(final ServerLevel level, final int chunkX, final int chunkZ, final ChunkStatus toStatus,
+ final boolean addTicket, final Priority priority, final Consumer<ChunkAccess> onComplete) {
+ if (!Bukkit.isOwnedByCurrentRegion(level.getWorld(), chunkX, chunkZ)) {
+ this.scheduleChunkTask(level, chunkX, chunkZ, () -> {
+ BaseChunkSystemHooks.this.scheduleChunkLoad(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
+ }, priority);
+ return;
+ }
+
+ final int minLevel = 33 + getDistance(toStatus);
+ final Long chunkReference = addTicket ? Long.valueOf(++this.chunkLoadCounter) : null;
+ final ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
+
+ if (addTicket) {
+ level.chunkSource.addTicketAtLevel(CHUNK_LOAD, chunkPos, minLevel, chunkReference);
+ }
+ level.chunkSource.runDistanceManagerUpdates();
+
+ final Consumer<ChunkAccess> loadCallback = (final ChunkAccess chunk) -> {
+ try {
+ if (onComplete != null) {
+ onComplete.accept(chunk);
+ }
+ } catch (final Throwable thr) {
+ LOGGER.error("Exception handling chunk load callback", thr);
+ com.destroystokyo.paper.util.SneakyThrow.sneaky(thr);
+ } finally {
+ if (addTicket) {
+ level.chunkSource.addTicketAtLevel(net.minecraft.server.level.TicketType.UNKNOWN, chunkPos, minLevel, chunkPos);
+ level.chunkSource.removeTicketAtLevel(CHUNK_LOAD, chunkPos, minLevel, chunkReference);
+ }
+ }
+ };
+
+ final ChunkHolder holder = level.chunkSource.chunkMap.updatingChunkMap.get(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(chunkX, chunkZ));
+
+ if (holder == null || holder.getTicketLevel() > minLevel) {
+ loadCallback.accept(null);
+ return;
+ }
+
+ final CompletableFuture<ChunkResult<ChunkAccess>> loadFuture = holder.scheduleChunkGenerationTask(toStatus, level.chunkSource.chunkMap);
+
+ if (loadFuture.isDone()) {
+ loadCallback.accept(loadFuture.join().orElse(null));
+ return;
+ }
+
+ loadFuture.whenCompleteAsync((final ChunkResult<ChunkAccess> result, final Throwable thr) -> {
+ if (thr != null) {
+ loadCallback.accept(null);
+ return;
+ }
+ loadCallback.accept(result.orElse(null));
+ }, (final Runnable r) -> {
+ BaseChunkSystemHooks.this.scheduleChunkTask(level, chunkX, chunkZ, r, Priority.HIGHEST);
+ });
+ }
+
+ @Override
+ public void scheduleTickingState(final ServerLevel level, final int chunkX, final int chunkZ,
+ final FullChunkStatus toStatus, final boolean addTicket,
+ final Priority priority, final Consumer<LevelChunk> onComplete) {
+ // This method goes unused until the chunk system rewrite
+ if (toStatus == FullChunkStatus.INACCESSIBLE) {
+ throw new IllegalArgumentException("Cannot wait for INACCESSIBLE status");
+ }
+
+ if (!Bukkit.isOwnedByCurrentRegion(level.getWorld(), chunkX, chunkZ)) {
+ this.scheduleChunkTask(level, chunkX, chunkZ, () -> {
+ BaseChunkSystemHooks.this.scheduleTickingState(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
+ }, priority);
+ return;
+ }
+
+ final int minLevel = 33 - (toStatus.ordinal() - 1);
+ final int radius = toStatus.ordinal() - 1;
+ final Long chunkReference = addTicket ? Long.valueOf(++this.chunkLoadCounter) : null;
+ final ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
+
+ if (addTicket) {
+ level.chunkSource.addTicketAtLevel(CHUNK_LOAD, chunkPos, minLevel, chunkReference);
+ }
+ level.chunkSource.runDistanceManagerUpdates();
+
+ final Consumer<LevelChunk> loadCallback = (final LevelChunk chunk) -> {
+ try {
+ if (onComplete != null) {
+ onComplete.accept(chunk);
+ }
+ } catch (final Throwable thr) {
+ LOGGER.error("Exception handling chunk load callback", thr);
+ com.destroystokyo.paper.util.SneakyThrow.sneaky(thr);
+ } finally {
+ if (addTicket) {
+ level.chunkSource.addTicketAtLevel(TicketType.UNKNOWN, chunkPos, minLevel, chunkPos);
+ level.chunkSource.removeTicketAtLevel(CHUNK_LOAD, chunkPos, minLevel, chunkReference);
+ }
+ }
+ };
+
+ final ChunkHolder holder = level.chunkSource.chunkMap.updatingChunkMap.get(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(chunkX, chunkZ));
+
+ if (holder == null || holder.getTicketLevel() > minLevel) {
+ loadCallback.accept(null);
+ return;
+ }
+
+ final CompletableFuture<ChunkResult<LevelChunk>> tickingState;
+ switch (toStatus) {
+ case FULL: {
+ tickingState = holder.getFullChunkFuture();
+ break;
+ }
+ case BLOCK_TICKING: {
+ tickingState = holder.getTickingChunkFuture();
+ break;
+ }
+ case ENTITY_TICKING: {
+ tickingState = holder.getEntityTickingChunkFuture();
+ break;
+ }
+ default: {
+ throw new IllegalStateException("Cannot reach here");
+ }
+ }
+
+ if (tickingState.isDone()) {
+ loadCallback.accept(tickingState.join().orElse(null));
+ return;
+ }
+
+ tickingState.whenCompleteAsync((final ChunkResult<LevelChunk> result, final Throwable thr) -> {
+ if (thr != null) {
+ loadCallback.accept(null);
+ return;
+ }
+ loadCallback.accept(result.orElse(null));
+ }, (final Runnable r) -> {
+ BaseChunkSystemHooks.this.scheduleChunkTask(level, chunkX, chunkZ, r, Priority.HIGHEST);
+ });
+ }
+
+ @Override
+ public List<ChunkHolder> getVisibleChunkHolders(final ServerLevel level) {
+ return new ArrayList<>(level.chunkSource.chunkMap.visibleChunkMap.values());
+ }
+
+ @Override
+ public List<ChunkHolder> getUpdatingChunkHolders(final ServerLevel level) {
+ return new ArrayList<>(level.chunkSource.chunkMap.updatingChunkMap.values());
+ }
+
+ @Override
+ public int getVisibleChunkHolderCount(final ServerLevel level) {
+ return level.chunkSource.chunkMap.visibleChunkMap.size();
+ }
+
+ @Override
+ public int getUpdatingChunkHolderCount(final ServerLevel level) {
+ return level.chunkSource.chunkMap.updatingChunkMap.size();
+ }
+
+ @Override
+ public boolean hasAnyChunkHolders(final ServerLevel level) {
+ return this.getUpdatingChunkHolderCount(level) != 0;
+ }
+
+ @Override
+ public void onChunkHolderCreate(final ServerLevel level, final ChunkHolder holder) {
+
+ }
+
+ @Override
+ public void onChunkHolderDelete(final ServerLevel level, final ChunkHolder holder) {
+
+ }
+
+ @Override
+ public void onChunkPreBorder(final LevelChunk chunk, final ChunkHolder holder) {
+
+ }
+
+ @Override
+ public void onChunkBorder(final LevelChunk chunk, final ChunkHolder holder) {
+
+ }
+
+ @Override
+ public void onChunkNotBorder(final LevelChunk chunk, final ChunkHolder holder) {
+
+ }
+
+ @Override
+ public void onChunkPostNotBorder(final LevelChunk chunk, final ChunkHolder holder) {
+
+ }
+
+ @Override
+ public void onChunkTicking(final LevelChunk chunk, final ChunkHolder holder) {
+
+ }
+
+ @Override
+ public void onChunkNotTicking(final LevelChunk chunk, final ChunkHolder holder) {
+
+ }
+
+ @Override
+ public void onChunkEntityTicking(final LevelChunk chunk, final ChunkHolder holder) {
+
+ }
+
+ @Override
+ public void onChunkNotEntityTicking(final LevelChunk chunk, final ChunkHolder holder) {
+
+ }
+
+ @Override
+ public ChunkHolder getUnloadingChunkHolder(final ServerLevel level, final int chunkX, final int chunkZ) {
+ return level.chunkSource.chunkMap.getUnloadingChunkHolder(chunkX, chunkZ);
+ }
+
+ @Override
+ public int getSendViewDistance(final ServerPlayer player) {
+ return this.getViewDistance(player);
+ }
+
+ @Override
+ public int getViewDistance(final ServerPlayer player) {
+ final ServerLevel level = player.serverLevel();
+ if (level == null) {
+ return Bukkit.getViewDistance();
+ }
+ return level.chunkSource.chunkMap.serverViewDistance;
+ }
+
+ @Override
+ public int getTickViewDistance(final ServerPlayer player) {
+ final ServerLevel level = player.serverLevel();
+ if (level == null) {
+ return Bukkit.getSimulationDistance();
+ }
+ return level.chunkSource.chunkMap.distanceManager.simulationDistance;
+ }
+
+ @Override
+ public void addPlayerToDistanceMaps(final ServerLevel world, final ServerPlayer player) {
+
+ }
+
+ @Override
+ public void removePlayerFromDistanceMaps(final ServerLevel world, final ServerPlayer player) {
+
+ }
+
+ @Override
+ public void updateMaps(final ServerLevel world, final ServerPlayer player) {
+
+ }
+}

View file

@ -1,6 +1,6 @@
--- a/com/mojang/authlib/yggdrasil/YggdrasilGameProfileRepository.java
+++ b/com/mojang/authlib/yggdrasil/YggdrasilGameProfileRepository.java
@@ -44,6 +44,7 @@
@@ -44,6 +_,7 @@
.collect(Collectors.toSet());
final int page = 0;
@ -8,7 +8,7 @@
for (final List<String> request : Iterables.partition(criteria, ENTRIES_PER_PAGE)) {
final List<String> normalizedRequest = request.stream().map(YggdrasilGameProfileRepository::normalizeName).toList();
@@ -75,6 +76,12 @@
@@ -75,6 +_,12 @@
LOGGER.debug("Couldn't find profile {}", name);
callback.onProfileLookupFailed(name, new ProfileNotFoundException("Server did not find the requested profile"));
}

View file

@ -1,14 +1,6 @@
--- a/com/mojang/brigadier/CommandDispatcher.java
+++ b/com/mojang/brigadier/CommandDispatcher.java
@@ -3,6 +3,7 @@
package com.mojang.brigadier;
+// CHECKSTYLE:OFF
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.context.CommandContextBuilder;
@@ -297,15 +298,21 @@
@@ -297,15 +_,21 @@
List<ParseResults<S>> potentials = null;
final int cursor = originalReader.getCursor();
@ -31,7 +23,7 @@
} catch (final RuntimeException ex) {
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherParseException().createWithContext(reader, ex.getMessage());
}
@@ -320,6 +327,7 @@
@@ -320,6 +_,7 @@
}
errors.put(child, ex);
reader.setCursor(cursor);
@ -39,7 +31,7 @@
continue;
}
@@ -451,7 +459,7 @@
@@ -451,7 +_,7 @@
}
private String getSmartUsage(final CommandNode<S> node, final S source, final boolean optional, final boolean deep) {
@ -48,16 +40,16 @@
return null;
}
@@ -465,7 +473,7 @@
final String redirect = node.getRedirect() == this.root ? "..." : "-> " + node.getRedirect().getUsageText();
return self + CommandDispatcher.ARGUMENT_SEPARATOR + redirect;
@@ -465,7 +_,7 @@
final String redirect = node.getRedirect() == root ? "..." : "-> " + node.getRedirect().getUsageText();
return self + ARGUMENT_SEPARATOR + redirect;
} else {
- final Collection<CommandNode<S>> children = node.getChildren().stream().filter(c -> c.canUse(source)).collect(Collectors.toList());
+ final Collection<CommandNode<S>> children = node.getChildren().stream().filter(c -> source == null || c.canUse(source)).collect(Collectors.toList()); // Paper
if (children.size() == 1) {
final String usage = this.getSmartUsage(children.iterator().next(), source, childOptional, childOptional);
final String usage = getSmartUsage(children.iterator().next(), source, childOptional, childOptional);
if (usage != null) {
@@ -537,10 +545,14 @@
@@ -537,10 +_,14 @@
int i = 0;
for (final CommandNode<S> node : parent.getChildren()) {
CompletableFuture<Suggestions> future = Suggestions.empty();

Some files were not shown because too many files have changed in this diff Show more