Compare commits

..
Author SHA1 Message Date
99-i 76d2ac758c [ci skip] Fix AreaEffectCloud doc comments (#13926) 2026-06-03 19:31:16 +02:00
Pedro 20938c7217 Set Obsolete the use of PotionMeta in Potion entities (#13892) 2026-06-03 19:31:07 +02:00
Pedro ca5e948ace Fix override of new item for Firework entity (#13891) 2026-06-03 19:30:57 +02:00
Nassim Jahnke e2c7e5cff2 Limit projectiles size 2026-06-03 19:30:24 +02:00
Bjarne Koll d4fe85375a [ci skip] Move paperweight and mache to non snapshots
Paper was previously depending on snapshots of paperweight and,
indirectly through the mache build, sculptor and codebook.

As we prepare work on 26.2, a new snapshot cycle for some of these tools
is required and 26.1.x cannot keep depending on them.
2026-06-02 20:08:53 +02:00
Martijn 3580fa4066 Use internal block to item conversion for flower pots (#13880) 2026-06-02 13:55:17 +02:00
Warrior 10a73fe40f Add and clarify Attribute#getDefaultValue (#13345) 2026-05-31 19:07:56 +02:00
roro1506HD 34cf3ab0f9 Fix loot tables being reset before death event (#13886) 2026-05-26 17:00:28 +02:00
Bjarne KollandLulu13022002 fd45f4b2e3 Upgrade to vineflower 12.0.0 (#13882)
Co-authored-by: Lulu13022002 <41980282+Lulu13022002@users.noreply.github.com>
2026-05-22 10:45:39 +01:00
Shane Freeder 5c917dabab [ci skip] Fixed accidently test plugin changes 2026-05-16 23:34:09 +01:00
Shane Freeder 2186e1e407 Fix BlockRecieveGameEvent destination block 2026-05-16 23:31:38 +01:00
Nassim Jahnke 711c5de2b0 Add counting ops 2026-05-11 08:56:48 +02:00
Pedro 687ba1e391 Adds missing deprecation for Player#boostElytra (#13088) 2026-05-10 14:34:55 -04:00
Nassim Jahnke 8dea6f1761 Fork friendly byte buf correctly 2026-05-08 19:19:07 +02:00
Alejandro Nieto b4682bfef6 Fix clicked items disappearing when player dies (#13002) 2026-05-04 08:23:40 +02:00
Warrior b685281d74 [ci skip] Mention documenting new config options in CONTRIBUTING.md (#13664) 2026-05-03 16:43:03 -04:00
Lulu13022002 b8b4a1c7cd Update Player#getClientViewDistance (#13530) 2026-05-03 16:40:33 -04:00
Lulu13022002 11dbf11014 Call BlockRedstoneEvent for more interactions (#12206) 2026-05-03 16:38:16 -04:00
Nassim Jahnke 39bd1bac06 [ci skip] Rebuild patches 2026-05-03 21:23:19 +02:00
Harry afea65c2d2 Fix leash event for boats and elytra (#13697) 2026-05-03 12:47:36 -04:00
masmc05 1835b0468f Call AsyncPlayerPreLoginEvent/AsyncPlayerConnectionConfigureEvent on virtual threads (#13816) 2026-05-03 12:46:14 -04:00
Owen b80b604636 fix: Attack strength ticker cooldown incorrectly reset (#13856) 2026-05-03 12:38:13 -04:00
Pedro 6048c2d97c Fix LivingEntity#knockback ignored when its Player (#13842) 2026-05-03 12:38:02 -04:00
Lulu13022002 2e7bb166b1 Deprecate dyes custom tag in favour of vanilla tag (#13855) 2026-05-03 12:14:01 -04:00
Nassim Jahnke 39a1aa5c7f Promote build channel to stable 2026-04-30 18:21:45 +02:00
𝑩𝒊𝒒𝒖𝒂𝒕𝒆𝒓𝒏𝒊𝒐𝒏𝒔 174eab0192 Update Improve exact choice recipe ingredients (#13788) 2026-04-29 08:08:28 +00:00
masmc05 5d62ec0ed9 Prevent spawn location getters from allowing making the location invalid (#13831) 2026-04-27 17:52:49 +01:00
masmc05 17524d0ee0 Add precondition to spawn location setter in spawn events (#13830) 2026-04-27 16:56:47 +01:00
Jason Penilla 7799bf28ae Update Paperclip to v3.0.4
Updates Java runtime version check
2026-04-26 13:58:31 -07:00
Coredex 55be6c0ef5 Fix boat and minecart inventories on region switch [ci skip] (#13826) 2026-04-26 16:01:07 -04:00
Nassim Jahnke 5dc4afc1dd Promote build channel to BETA 2026-04-26 20:20:48 +02:00
PedroandLulu13022002 f4e769a74d Improvements checks for arguments numbers (#13290)
Co-authored-by: Lulu13022002 <41980282+Lulu13022002@users.noreply.github.com>
2026-04-26 17:55:35 +02:00
Lulu13022002 651d8481b7 Finish world key initial migration (#13824) 2026-04-26 17:26:40 +02:00
Nassim Jahnke acfe105b9d Add options to disable chunk and block entity ticking (#13821) 2026-04-26 17:09:05 +02:00
Nassim Jahnke a64ea13e8e Re-implement per-world and per-player time (#13814) 2026-04-26 13:37:46 +02:00
Warrior 1f8197327f Allow ServerboundChangeGameModePacket if player has /gamemode permissions (#13507) 2026-04-26 01:00:30 -04:00
Owen 8021488ef4 Disconnect State Fixes (#13616)
Replaces the processedDisconnect flag and executeBlocking cleanup with a force kill flag processed by the connection listener tick. executeBlocking(connection::handleDisconnection) did not actually Work, as the the channel is still open at that point because the disconnect packet hasn't been flushed. Replaced with handleConnectionDisconnectOnNextTick, which the connection listener processes on the next tick.

processedDisconnect was an old flag preventing double disconnect.

The same protection is now provided by vanilla's disconnectionHandled check on Connection.

Drops the SPIGOT-924 player.doTick() call on quit. This is an ancient spigot bug fix that causes issues and weird event fires whilst the player is disconnected.

Removes the login side disconnecting flag and the matching tick guard, since the connection now closes properly on its own. The login disconnect path moves to disconnectAsync so it doesn't block on IO.
2026-04-25 18:06:21 -04:00
Miles 182b2f8a2a Add Point Of Interest API (#12117) 2026-04-25 16:27:55 -04:00
Owen a8c05a35c2 Add an option to WorldCreator to avoid spawn location computation on world creation (#13407) 2026-04-25 15:31:47 -04:00
Pedro 945aea9961 Fix Damager for HangingBreakByEntityEvent and minor improvements (#12615) 2026-04-25 14:52:57 -04:00
Pedro 4aef3f0a67 Remove already fixed vanilla bug MC-244739 (#13787) 2026-04-25 14:26:37 -04:00
Pedro 60073714ba Update MOJIRA reference in DeOp command response for multiple targets [ci skip] (#13812) 2026-04-25 14:20:54 -04:00
Rothes 4f184db3c9 Fix memory leak on constantly damage (#13455) 2026-04-25 14:19:52 -04:00
Illia Bondar 57d84f68a3 Prevent NPE on ThrownPotion#splash (#13683) 2026-04-25 14:16:47 -04:00
Warrior 49daadd25d Fix insomniaStartTicks option being ignored for phantom spawning (#13609) 2026-04-25 13:57:03 -04:00
Pedro b2b1e177cb Improvement to Vehicle damage events (#13572) 2026-04-25 13:46:17 -04:00
PedroandLulu13022002 90aee8e007 Update world methods to take in account environment attributes (#13543)
Co-authored-by: Lulu13022002 <41980282+Lulu13022002@users.noreply.github.com>
2026-04-25 19:20:49 +02:00
Pedro d74f6bc77a Support for Kill damageable entities (#13667) 2026-04-25 13:12:18 -04:00
Kezz dd203a2716 chore: Don't log errors for accepting the EULA via the system property (#13681) 2026-04-25 13:02:42 -04:00
David Lüdtke dcf5577aae Fix PlayerProfile applySkinToPlayerHeadContents (#13692) 2026-04-25 12:53:39 -04:00
Harold c39822ae08 reset pending keepalive state on listener handoff during reconfiguration (#13712) 2026-04-25 12:50:10 -04:00
David 4af86ff3e0 Handle null ServerLevel in fromGlobalPos (#13703) 2026-04-25 12:41:23 -04:00
ac15CR 1a8417b12b [ci skip] Add Javadocs for PotionEffect withX methods (#13723) 2026-04-25 12:39:33 -04:00
personalized-advertising 65461225ef Update health after setPlayerProfile() (#13717) 2026-04-25 12:32:53 -04:00
PedroandLulu13022002 7b31cc2b6b Make leaveMessage in PlayerKickEvent nullable (#13804)
Co-authored-by: Lulu13022002 <41980282+Lulu13022002@users.noreply.github.com>
2026-04-25 12:30:26 -04:00
sovdeeandLulu13022002 aa6f09b098 Prevent Happy Ghast from playing mount/dismount noises when silenced (#13807)
Co-authored-by: Lulu13022002 <41980282+Lulu13022002@users.noreply.github.com>
2026-04-25 12:01:24 -04:00
Nassim Jahnke 944ce44a4a Fix CraftWorld identifier passing 2026-04-25 11:59:27 +02:00
Pedro 6fe7eb5aae Load World Key when its instance is created (#13818) 2026-04-25 09:58:13 +02:00
Spottedleaf 948acae8c2 Raise existing level.dat data size quota
It appears that custom data may push it to be higher than
what the default quota allows for.

See https://mojira.dev/MC-307144

Fixes https://github.com/PaperMC/Paper/issues/13820
2026-04-24 19:25:14 -07:00
Nassim Jahnke ef4e85d6db Replace remaining relevant getWorld(String) use 2026-04-24 19:16:43 +02:00
Shane Freeder 3611a815e6 Fix String#toLower/UpperCase usages 2026-04-23 16:37:29 +01:00
Jason Penilla 1d14197790 fix setSpawnLocation changing respawn dimension
it was carrying some leftover intermediary shape from before PaperLevelOverrides design was finalized - revert to original shape for correct behavior

also revert ordering change in setRespawnData
2026-04-21 09:49:28 -07:00
Spottedleaf 6bac3c95bf Delay open/close callbacks for chests
The logic for chests may invoke block updates, which may not
be safe to perform. For example, inventory closing due to
player logout (which restricts chunk loading due to being
inside an entity status callback) or during shutdown (which has
chunk loading restrictions due to the chunk system being halted).

This also fixes the recheckOpeners function not firing
the redstone event if the open count changes.
2026-04-20 03:05:27 -07:00
Spottedleaf 9b1dd57135 Do not empty beehives from worldgen threads
The level is not set here, so emptying would cause a crash
due to NPE, and of course the logic is not safe to run from
the worldgen thread even if the level was set.
2026-04-20 02:48:53 -07:00
Spottedleaf 4be1a310c2 Add EntityScheduler#scheduleOrExecute
This is useful on Folia where we may want to try to avoid
scheduling a delayed task.
2026-04-20 02:15:28 -07:00
Nassim Jahnke 66d3bbed38 Update frosted ice patch for 26.1 2026-04-19 18:41:44 +02:00
Jonathan d0be5e20fd Call BlockFadeEvent for FrostedIce (#13764) 2026-04-19 16:14:54 +02:00
Asish Kumar dd0810c4af Skip inactive AI ticks for unaware mobs (#13781) 2026-04-19 16:14:21 +02:00
Pedro 04ee8eac9b Remove Paper config unsupported-settings.compression-format (#13800) 2026-04-19 16:08:48 +02:00
Warrior c58cba39fb Port blockstate validation patch (#13786) 2026-04-19 16:08:32 +02:00
526 changed files with 10162 additions and 9358 deletions

No files matched your search

+12 -3
View File
@@ -420,13 +420,19 @@ int maxPlayers = GlobalConfiguration.get().misc.maxNumOfPlayers;
Generally for global config values you will use the fully qualified class name,
`io.papermc.paper.configuration.GlobalConfiguration` since it's not imported in
most places.
---
If you are adding a new world config value, you must have access to an instance
of the `net.minecraft.world.level.Level` which you can then access the config by doing
```java
int maxPlayers = level.paperConfig().misc.maxNumOfPlayers;
```
#### Documentation
When adding or removing a config option, you should open a pull request in our [documentation repository](https://github.com/PaperMC/docs)
to keep it in sync and accurate. If you're unsure whether your original change will be accepted, then it's fine to wait with making the documentation
pull request until a maintainer has reviewed your changes. Once everything is done, the documentation pull request will be merged at the same time
that your original pull request is.
## Testing API changes
### Using the Paper Test Plugin
@@ -470,10 +476,13 @@ If you use Maven to build your plugin:
### My commit doesn't need a build, what do I do?
Quite simple: You add `[ci skip]` to the start of your commit subject.
Quite simple: You add `[ci skip]` to the start of your pull request title.
This case most often applies to changes to files like `README.md`, this very
file (`CONTRIBUTING.md`), the `LICENSE.md` file, and so forth.
file (`CONTRIBUTING.md`), the `LICENSE.md` file, and also small updates to Javadocs or other documentation.
Do *not* add [ci skip] to the start of your individual commit subjects, as your
pull request will not be mergeable until a new commit is added due to status check requirements.
### Patching and building is *really* slow, what can I do?
+1
View File
@@ -122,6 +122,7 @@ 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.AbstractListBuilder
public net.minecraft.util.Util onThreadException(Ljava/lang/Thread;Ljava/lang/Throwable;)V
public net.minecraft.util.datafix.fixes.BlockStateData register(ILcom/mojang/serialization/Dynamic;[Lcom/mojang/serialization/Dynamic;)V
public net.minecraft.util.datafix.fixes.ItemIdFix ITEM_NAMES
+1 -1
View File
@@ -2,7 +2,7 @@ import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent
plugins {
id("io.papermc.paperweight.core") version "2.0.0-SNAPSHOT" apply false
id("io.papermc.paperweight.core") version "2.0.0-beta.21" apply false
}
subprojects {
+1 -1
View File
@@ -3,7 +3,7 @@ mcVersion=26.1.2
# This is the current API version for use in (paper-)plugin.yml files
# During snapshot cycles this should be the anticipated version of the release target
apiVersion=26.1.2
channel=ALPHA
channel=STABLE
# Set to true while updating Minecraft version
updatingMinecraft=false
@@ -0,0 +1,181 @@
package io.papermc.paper.registry.keys;
import static net.kyori.adventure.key.Key.key;
import io.papermc.paper.annotation.GeneratedClass;
import io.papermc.paper.entity.poi.PoiType;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.TypedKey;
import net.kyori.adventure.key.Key;
import org.jspecify.annotations.NullMarked;
/**
* Vanilla keys for {@link RegistryKey#POINT_OF_INTEREST_TYPE}.
*
* @apiNote The fields provided here are a direct representation of
* what is available from the vanilla game source. They may be
* changed (including removals) on any Minecraft version
* bump, so cross-version compatibility is not provided on the
* same level as it is on most of the other API.
*/
@SuppressWarnings({
"unused",
"SpellCheckingInspection"
})
@NullMarked
@GeneratedClass
public final class PoiTypeKeys {
/**
* {@code minecraft:armorer}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<PoiType> ARMORER = create(key("armorer"));
/**
* {@code minecraft:bee_nest}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<PoiType> BEE_NEST = create(key("bee_nest"));
/**
* {@code minecraft:beehive}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<PoiType> BEEHIVE = create(key("beehive"));
/**
* {@code minecraft:butcher}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<PoiType> BUTCHER = create(key("butcher"));
/**
* {@code minecraft:cartographer}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<PoiType> CARTOGRAPHER = create(key("cartographer"));
/**
* {@code minecraft:cleric}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<PoiType> CLERIC = create(key("cleric"));
/**
* {@code minecraft:farmer}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<PoiType> FARMER = create(key("farmer"));
/**
* {@code minecraft:fisherman}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<PoiType> FISHERMAN = create(key("fisherman"));
/**
* {@code minecraft:fletcher}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<PoiType> FLETCHER = create(key("fletcher"));
/**
* {@code minecraft:home}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<PoiType> HOME = create(key("home"));
/**
* {@code minecraft:leatherworker}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<PoiType> LEATHERWORKER = create(key("leatherworker"));
/**
* {@code minecraft:librarian}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<PoiType> LIBRARIAN = create(key("librarian"));
/**
* {@code minecraft:lightning_rod}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<PoiType> LIGHTNING_ROD = create(key("lightning_rod"));
/**
* {@code minecraft:lodestone}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<PoiType> LODESTONE = create(key("lodestone"));
/**
* {@code minecraft:mason}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<PoiType> MASON = create(key("mason"));
/**
* {@code minecraft:meeting}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<PoiType> MEETING = create(key("meeting"));
/**
* {@code minecraft:nether_portal}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<PoiType> NETHER_PORTAL = create(key("nether_portal"));
/**
* {@code minecraft:shepherd}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<PoiType> SHEPHERD = create(key("shepherd"));
/**
* {@code minecraft:test_instance}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<PoiType> TEST_INSTANCE = create(key("test_instance"));
/**
* {@code minecraft:toolsmith}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<PoiType> TOOLSMITH = create(key("toolsmith"));
/**
* {@code minecraft:weaponsmith}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<PoiType> WEAPONSMITH = create(key("weaponsmith"));
private PoiTypeKeys() {
}
private static TypedKey<PoiType> create(final Key key) {
return TypedKey.create(RegistryKey.POINT_OF_INTEREST_TYPE, key);
}
}
@@ -1,68 +0,0 @@
package io.papermc.paper.registry.keys;
import static net.kyori.adventure.key.Key.key;
import io.papermc.paper.annotation.GeneratedClass;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.TypedKey;
import io.papermc.paper.world.Timeline;
import net.kyori.adventure.key.Key;
import org.jspecify.annotations.NullMarked;
/**
* Vanilla keys for {@link RegistryKey#TIMELINE}.
*
* @apiNote The fields provided here are a direct representation of
* what is available from the vanilla game source. They may be
* changed (including removals) on any Minecraft version
* bump, so cross-version compatibility is not provided on the
* same level as it is on most of the other API.
*/
@SuppressWarnings({
"unused",
"SpellCheckingInspection"
})
@NullMarked
@GeneratedClass
public final class TimelineKeys {
/**
* {@code minecraft:day}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Timeline> DAY = create(key("day"));
/**
* {@code minecraft:early_game}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Timeline> EARLY_GAME = create(key("early_game"));
/**
* {@code minecraft:moon}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Timeline> MOON = create(key("moon"));
/**
* {@code minecraft:villager_schedule}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<Timeline> VILLAGER_SCHEDULE = create(key("villager_schedule"));
private TimelineKeys() {
}
/**
* Creates a typed key for {@link Timeline} in the registry {@code minecraft:timeline}.
*
* @param key the value's key in the registry
* @return a new typed key
*/
public static TypedKey<Timeline> create(final Key key) {
return TypedKey.create(RegistryKey.TIMELINE, key);
}
}
@@ -1,54 +0,0 @@
package io.papermc.paper.registry.keys;
import static net.kyori.adventure.key.Key.key;
import io.papermc.paper.annotation.GeneratedClass;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.TypedKey;
import io.papermc.paper.world.WorldClock;
import net.kyori.adventure.key.Key;
import org.jspecify.annotations.NullMarked;
/**
* Vanilla keys for {@link RegistryKey#WORLD_CLOCK}.
*
* @apiNote The fields provided here are a direct representation of
* what is available from the vanilla game source. They may be
* changed (including removals) on any Minecraft version
* bump, so cross-version compatibility is not provided on the
* same level as it is on most of the other API.
*/
@SuppressWarnings({
"unused",
"SpellCheckingInspection"
})
@NullMarked
@GeneratedClass
public final class WorldClockKeys {
/**
* {@code minecraft:overworld}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<WorldClock> OVERWORLD = create(key("overworld"));
/**
* {@code minecraft:the_end}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TypedKey<WorldClock> THE_END = create(key("the_end"));
private WorldClockKeys() {
}
/**
* Creates a typed key for {@link WorldClock} in the registry {@code minecraft:world_clock}.
*
* @param key the value's key in the registry
* @return a new typed key
*/
public static TypedKey<WorldClock> create(final Key key) {
return TypedKey.create(RegistryKey.WORLD_CLOCK, key);
}
}
@@ -3,14 +3,14 @@ package io.papermc.paper.registry.keys.tags;
import static net.kyori.adventure.key.Key.key;
import io.papermc.paper.annotation.GeneratedClass;
import io.papermc.paper.entity.poi.PoiType;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.tag.TagKey;
import io.papermc.paper.world.Timeline;
import net.kyori.adventure.key.Key;
import org.jspecify.annotations.NullMarked;
/**
* Vanilla tag keys for {@link RegistryKey#TIMELINE}.
* Vanilla tag keys for {@link RegistryKey#POINT_OF_INTEREST_TYPE}.
*
* @apiNote The fields provided here are a direct representation of
* what is available from the vanilla game source. They may be
@@ -24,45 +24,38 @@ import org.jspecify.annotations.NullMarked;
})
@NullMarked
@GeneratedClass
public final class TimelineTagKeys {
public final class PoiTypeTagKeys {
/**
* {@code #minecraft:in_end}
* {@code #minecraft:acquirable_job_site}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TagKey<Timeline> IN_END = create(key("in_end"));
public static final TagKey<PoiType> ACQUIRABLE_JOB_SITE = create(key("acquirable_job_site"));
/**
* {@code #minecraft:in_nether}
* {@code #minecraft:bee_home}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TagKey<Timeline> IN_NETHER = create(key("in_nether"));
public static final TagKey<PoiType> BEE_HOME = create(key("bee_home"));
/**
* {@code #minecraft:in_overworld}
* {@code #minecraft:village}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TagKey<Timeline> IN_OVERWORLD = create(key("in_overworld"));
public static final TagKey<PoiType> VILLAGE = create(key("village"));
/**
* {@code #minecraft:universal}
*
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
*/
public static final TagKey<Timeline> UNIVERSAL = create(key("universal"));
private TimelineTagKeys() {
private PoiTypeTagKeys() {
}
/**
* Creates a tag key for {@link Timeline} in the registry {@code minecraft:timeline}.
* Creates a tag key for {@link PoiType} in the registry {@code minecraft:point_of_interest_type}.
*
* @param key the tag key's key
* @return a new tag key
*/
public static TagKey<Timeline> create(final Key key) {
return TagKey.create(RegistryKey.TIMELINE, key);
public static TagKey<PoiType> create(final Key key) {
return TagKey.create(RegistryKey.POINT_OF_INTEREST_TYPE, key);
}
}
@@ -134,7 +134,7 @@ public class TimingHistory {
}
}
return pair(
worldMap.get(world.getKey().toString()),
worldMap.get(world.key().asString()),
toArrayMapper(regions.values(),new Function<RegionData, Object>() {
@NotNull
@Override
@@ -125,10 +125,11 @@ public class MaterialTags {
/**
* Covers all dyes.
*
* @deprecated in favour of {@link Tag#ITEMS_DYES}
*/
public static final MaterialSetTag DYES = new MaterialSetTag(keyFor("dyes"))
.endsWith("_DYE")
.ensureSize("DYES", 16).lock();
@Deprecated(since = "26.1")
public static final MaterialSetTag DYES = replacedBy(Tag.ITEMS_DYES);
/**
* Covers all variants of gates.
@@ -75,7 +75,7 @@ public final class NamespacedTag implements com.destroystokyo.paper.Namespaced {
Preconditions.checkArgument(key != null, "Key cannot be null");
this.namespace = plugin.getName().toLowerCase(Locale.ROOT);
this.key = key.toLowerCase().toLowerCase(Locale.ROOT);
this.key = key.toLowerCase(Locale.ROOT);
// Check validity after normalization
Preconditions.checkArgument(VALID_NAMESPACE.matcher(this.namespace).matches(), "Invalid namespace. Must be [a-z0-9._-]: %s", this.namespace);
@@ -9,9 +9,16 @@ import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
* Called when processing a player's attack on an entity when the player's attack strength cooldown is reset
* Called when processing a player's attack on an entity when the player's attack strength cooldown is reset.
*
* @deprecated In modern Minecraft, this does not properly represent spear attacks and additionally does not
* cover the wide range of ways that the cooldown may be reset.
* <p>
* Additionally, the cancellation of event is misleading as the client will reset the cooldown manually.
* So there is no way for the server to control this.
*/
@NullMarked
@Deprecated(since = "26.1", forRemoval = true)
public class PlayerAttackEntityCooldownResetEvent extends PlayerEvent implements Cancellable {
private static final HandlerList HANDLER_LIST = new HandlerList();
@@ -257,16 +257,16 @@ public interface PlayerProfile extends org.bukkit.profile.PlayerProfile, PlayerH
@Override
default void applySkinToPlayerHeadContents(final PlayerHeadObjectContents.Builder builder) {
if (this.getProperties().isEmpty() && (this.getName() != null) != (this.getId() != null)) {
if (this.getProperties().isEmpty()) {
if (this.getId() != null) {
builder.id(this.getId());
} else {
} else if (this.getName() != null) {
builder.name(this.getName());
}
return;
} else {
builder.id(this.getId())
.name(this.getName())
.profileProperties(this.getProperties().stream().map(prop -> property(prop.getName(), prop.getValue(), prop.getSignature())).toList());
}
builder.id(this.getId())
.name(this.getName())
.profileProperties(this.getProperties().stream().map(prop -> property(prop.getName(), prop.getValue(), prop.getSignature())).toList());
}
}
@@ -3,6 +3,7 @@ package io.papermc.paper;
import com.destroystokyo.paper.SkinParts;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import io.papermc.paper.datacomponent.item.ResolvableProfile;
import io.papermc.paper.entity.poi.PoiType;
import io.papermc.paper.world.damagesource.CombatEntry;
import io.papermc.paper.world.damagesource.FallLocationType;
import java.util.Set;
@@ -52,6 +53,14 @@ public interface InternalAPIBridge {
*/
DamageEffect getDamageEffect(String key);
/**
* Creates an occupancy instance for the passed enum entry name.
*
* @param enumNameEntry the enum entry
* @return the occupancy
*/
PoiType.Occupancy createOccupancy(String enumNameEntry);
/**
* Constructs the legacy custom biome instance for the biome enum.
*
@@ -2,6 +2,7 @@ package io.papermc.paper.datacomponent.item;
import io.papermc.paper.datacomponent.DataComponentBuilder;
import net.kyori.adventure.key.Key;
import org.checkerframework.checker.index.qual.Positive;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jspecify.annotations.NullMarked;
@@ -23,7 +24,7 @@ public interface UseCooldown {
* @return builder
*/
@Contract(value = "_ -> new", pure = true)
static UseCooldown.Builder useCooldown(final float seconds) {
static UseCooldown.Builder useCooldown(final @Positive float seconds) {
return ItemComponentTypesBridge.bridge().useCooldown(seconds);
}
@@ -33,7 +34,7 @@ public interface UseCooldown {
* @return cooldown seconds
*/
@Contract(pure = true)
float seconds();
@Positive float seconds();
/**
* The unique resource location to identify this cooldown group.
@@ -1,6 +1,7 @@
package io.papermc.paper.datacomponent.item;
import io.papermc.paper.datacomponent.DataComponentBuilder;
import org.checkerframework.checker.index.qual.NonNegative;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
@@ -23,7 +24,7 @@ public interface Weapon {
*
* @return durability
*/
int itemDamagePerAttack();
@NonNegative int itemDamagePerAttack();
/**
* The number of seconds that blocking is disabled.
@@ -46,7 +47,7 @@ public interface Weapon {
* @return the builder for chaining
* @see #itemDamagePerAttack()
*/
Builder itemDamagePerAttack(int damage);
Builder itemDamagePerAttack(@NonNegative int damage);
/**
* Sets the disable blocking duration (in seconds).
@@ -27,8 +27,7 @@ public interface ItemDamageFunction {
*
* @return the threshold
*/
@NonNegative
float threshold();
@NonNegative float threshold();
/**
* Get the constant amount of damage applied to the item, if threshold is passed.
@@ -60,7 +59,7 @@ public interface ItemDamageFunction {
interface Builder extends DataComponentBuilder<ItemDamageFunction> {
@Contract(value = "_ -> this", mutates = "this")
ItemDamageFunction.Builder threshold(@NonNegative final float threshold);
ItemDamageFunction.Builder threshold(final @NonNegative float threshold);
@Contract(value = "_ -> this", mutates = "this")
ItemDamageFunction.Builder base(final float base);
@@ -0,0 +1,30 @@
package io.papermc.paper.entity.poi;
import java.util.function.Predicate;
import org.bukkit.Location;
import org.bukkit.World;
import org.jspecify.annotations.NullMarked;
/**
* Holds the result of searching for a point of interest.
*
* @see World#locateAllPoiInRange(Location, Predicate, int)
* @see World#locateAllPoiInRange(Location, Predicate, int, PoiType.Occupancy)
*/
@NullMarked
public interface PoiSearchResult {
/**
* Returns the {@link PoiType}.
*
* @return the {@link PoiType}
*/
PoiType poiType();
/**
* Return the location of the {@link PoiType}.
*
* @return the location the {@link PoiType} was found at
*/
Location location();
}
@@ -0,0 +1,66 @@
package io.papermc.paper.entity.poi;
import io.papermc.paper.InternalAPIBridge;
import org.bukkit.Keyed;
import org.bukkit.block.data.BlockData;
import org.jspecify.annotations.NullMarked;
/**
* Represents a point of interest (POI).
* <p>
* Many PoiTypes act as a marker vs storing occupants examples of this are most
* explicitly {@link PoiTypes#NETHER_PORTAL}. This state can be checked using the method
* {@link #hasOccupants()} which will return {@code true} if the PoiType can have occupants.
*
* @see PoiTypes
*/
@NullMarked
public interface PoiType extends Keyed {
/**
* Determines whether the provided BlockState is relevant to this
* point of interest.
*
* @param data the BlockData to check
* @return {@code true} if the BlockState is relevant, otherwise {@code false}
*/
boolean is(BlockData data);
/**
* Determines whether this PoiType can actually hold occupants.
*
* @return {@code true} if this PoiType can hold occupants
*/
boolean hasOccupants();
/**
* Determines the type of occupancy the point of interest has.
* <p>
* A PoiType occupancy is defined by how much "space" a point of interest
* has.
* <p>
* With context to a Villager workstation if a villager is currently using
* a workstation as a source of its profession the occupancy would be
* {@link Occupancy#IS_OCCUPIED}, however, if no villager was attached to
* a workstation its occupancy would be {@link Occupancy#HAS_SPACE}.
*/
interface Occupancy {
/**
* The poi has space.
*/
Occupancy HAS_SPACE = occupancy("HAS_SPACE");
/**
* The poi is occupied.
*/
Occupancy IS_OCCUPIED = occupancy("IS_OCCUPIED");
/**
* The poi is either occupied or has space.
*/
Occupancy ANY = occupancy("ANY");
private static Occupancy occupancy(final String enumEntryName) {
return InternalAPIBridge.get().createOccupancy(enumEntryName);
}
}
}
@@ -0,0 +1,64 @@
package io.papermc.paper.entity.poi;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.KeyPattern;
import org.bukkit.Registry;
import org.jspecify.annotations.NullMarked;
/**
* All the built-in point of interest types.
*/
@NullMarked
public final class PoiTypes {
// Start generate - PoiTypes
public static final PoiType ARMORER = get("armorer");
public static final PoiType BEE_NEST = get("bee_nest");
public static final PoiType BEEHIVE = get("beehive");
public static final PoiType BUTCHER = get("butcher");
public static final PoiType CARTOGRAPHER = get("cartographer");
public static final PoiType CLERIC = get("cleric");
public static final PoiType FARMER = get("farmer");
public static final PoiType FISHERMAN = get("fisherman");
public static final PoiType FLETCHER = get("fletcher");
public static final PoiType HOME = get("home");
public static final PoiType LEATHERWORKER = get("leatherworker");
public static final PoiType LIBRARIAN = get("librarian");
public static final PoiType LIGHTNING_ROD = get("lightning_rod");
public static final PoiType LODESTONE = get("lodestone");
public static final PoiType MASON = get("mason");
public static final PoiType MEETING = get("meeting");
public static final PoiType NETHER_PORTAL = get("nether_portal");
public static final PoiType SHEPHERD = get("shepherd");
public static final PoiType TEST_INSTANCE = get("test_instance");
public static final PoiType TOOLSMITH = get("toolsmith");
public static final PoiType WEAPONSMITH = get("weaponsmith");
// End generate - PoiTypes
private static PoiType get(@KeyPattern.Value final String key) {
return Registry.POINT_OF_INTEREST_TYPE.getOrThrow(Key.key(Key.MINECRAFT_NAMESPACE, key));
}
private PoiTypes() {
}
}
@@ -1,5 +1,6 @@
package io.papermc.paper.event.player;
import com.google.common.base.Preconditions;
import io.papermc.paper.connection.PlayerConfigurationConnection;
import org.bukkit.Location;
import org.bukkit.Server;
@@ -52,7 +53,7 @@ public class AsyncPlayerSpawnLocationEvent extends Event {
* @return the spawn location
*/
public Location getSpawnLocation() {
return this.spawnLocation;
return this.spawnLocation.clone();
}
/**
@@ -61,6 +62,8 @@ public class AsyncPlayerSpawnLocationEvent extends Event {
* @param location the spawn location
*/
public void setSpawnLocation(final Location location) {
Preconditions.checkArgument(location != null, "Location cannot be null");
Preconditions.checkArgument(location.getWorld() != null, "World cannot be null");
this.spawnLocation = location.clone();
}
@@ -2,9 +2,8 @@ package io.papermc.paper.registry;
import io.papermc.paper.datacomponent.DataComponentType;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.entity.poi.PoiType;
import io.papermc.paper.registry.tag.TagKey;
import io.papermc.paper.world.Timeline;
import io.papermc.paper.world.WorldClock;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.KeyPattern;
import net.kyori.adventure.key.Keyed;
@@ -94,6 +93,11 @@ public sealed interface RegistryKey<T> extends Keyed permits RegistryKeyImpl {
* @see io.papermc.paper.registry.keys.VillagerProfessionKeys
*/
RegistryKey<Villager.Profession> VILLAGER_PROFESSION = create("villager_profession");
/**
* Built-in registry for poi types.
* @see io.papermc.paper.registry.keys.PoiTypeKeys
*/
RegistryKey<PoiType> POINT_OF_INTEREST_TYPE = create("point_of_interest_type");
/**
* Built-in registry for villager types.
* @see io.papermc.paper.registry.keys.VillagerTypeKeys
@@ -253,16 +257,6 @@ public sealed interface RegistryKey<T> extends Keyed permits RegistryKeyImpl {
* @see io.papermc.paper.registry.keys.DialogKeys
*/
RegistryKey<Dialog> DIALOG = create("dialog");
/**
* Data-driven registry for world clocks.
* @see io.papermc.paper.registry.keys.WorldClockKeys
*/
RegistryKey<WorldClock> WORLD_CLOCK = create("world_clock");
/**
* Data-driven registry for timelines.
* @see io.papermc.paper.registry.keys.TimelineKeys
*/
RegistryKey<Timeline> TIMELINE = create("timeline");
/* ******************* *
@@ -12,6 +12,7 @@ import net.kyori.adventure.text.Component;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.EquipmentSlotGroup;
import org.bukkit.inventory.ItemType;
import org.checkerframework.checker.index.qual.NonNegative;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Range;
@@ -99,7 +100,7 @@ public interface EnchantmentRegistryEntry {
*
* @return the anvil cost of this enchantment
*/
@Range(from = 0, to = Integer.MAX_VALUE) int anvilCost();
@NonNegative int anvilCost();
/**
* Provides a list of slot groups this enchantment may be active in.
@@ -245,7 +246,7 @@ public interface EnchantmentRegistryEntry {
* @see Enchantment#getAnvilCost()
*/
@Contract(value = "_ -> this", mutates = "this")
Builder anvilCost(@Range(from = 0, to = Integer.MAX_VALUE) int anvilCost);
Builder anvilCost(@NonNegative int anvilCost);
/**
* Configures the list of slot groups this enchantment may be active in.
@@ -2,9 +2,9 @@ package io.papermc.paper.registry.data;
import io.papermc.paper.registry.RegistryBuilder;
import org.bukkit.GameEvent;
import org.checkerframework.checker.index.qual.NonNegative;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Range;
/**
* A data-centric version-specific registry entry for the {@link GameEvent} type.
@@ -19,7 +19,7 @@ public interface GameEventRegistryEntry {
* @return the range of blocks, represented as an int.
* @see GameEvent#getRange()
*/
@Range(from = 0, to = Integer.MAX_VALUE) int range();
@NonNegative int range();
/**
* A mutable builder for the {@link GameEventRegistryEntry} plugins may change in applicable registry events.
@@ -42,6 +42,6 @@ public interface GameEventRegistryEntry {
* @see GameEvent#getRange()
*/
@Contract(value = "_ -> this", mutates = "this")
Builder range(@Range(from = 0, to = Integer.MAX_VALUE) int range);
Builder range(@NonNegative int range);
}
}
@@ -1,103 +0,0 @@
package io.papermc.paper.registry.data;
import io.papermc.paper.registry.RegistryBuilder;
import io.papermc.paper.registry.TypedKey;
import io.papermc.paper.world.Timeline;
import io.papermc.paper.world.WorldClock;
import java.util.Map;
import java.util.OptionalInt;
import net.kyori.adventure.key.Key;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Range;
/**
* A data-centric version-specific registry entry for the {@link Timeline} type.
*/
@ApiStatus.Experimental
@ApiStatus.NonExtendable
public interface TimelineRegistryEntry {
/**
* Gets the clock that drives this timeline.
*
* @return the clock
*/
@Contract(pure = true)
TypedKey<WorldClock> clock();
/**
* Gets the timeline period, if any.
*
* @return the timeline period
*/
@Contract(pure = true)
OptionalInt periodTicks();
/**
* Gets the time markers defined by this timeline.
*
* @return the time markers
*/
@Contract(pure = true)
Map<Key, TimeMarker> timeMarkers();
/**
* A mutable builder for the {@link TimelineRegistryEntry} plugins may change in applicable registry events.
* <p>
* The following values are required for each builder:
* <ul>
* <li>{@link #clock(TypedKey)}</li>
* </ul>
*/
@ApiStatus.Experimental
@ApiStatus.NonExtendable
interface Builder extends TimelineRegistryEntry, RegistryBuilder<Timeline> {
/**
* Sets the clock that drives this timeline.
*
* @param clock the clock
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder clock(TypedKey<WorldClock> clock);
/**
* Sets the timeline period.
*
* @param periodTicks the period in ticks
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder periodTicks(@Range(from = 1, to = Integer.MAX_VALUE) int periodTicks);
/**
* Clears the timeline period.
*
* @return this builder
*/
@Contract(value = "-> this", mutates = "this")
Builder clearPeriodTicks();
/**
* Adds or replaces a time marker on this timeline.
*
* @param key the time marker key
* @param ticks the marker tick
* @param showInCommands whether the marker should be suggested in commands
* @return this builder
*/
@Contract(value = "_, _, _ -> this", mutates = "this")
Builder addTimeMarker(Key key, @Range(from = 0, to = Integer.MAX_VALUE) int ticks, boolean showInCommands);
}
/**
* A time marker defined by a timeline.
*
* @param ticks the tick this marker occurs on
* @param showInCommands whether the marker should be suggested in commands
*/
record TimeMarker(@Range(from = 0, to = Integer.MAX_VALUE) int ticks, boolean showInCommands) {
}
}
@@ -1,21 +0,0 @@
package io.papermc.paper.registry.data;
import io.papermc.paper.registry.RegistryBuilder;
import io.papermc.paper.world.WorldClock;
import org.jetbrains.annotations.ApiStatus;
/**
* A data-centric version-specific registry entry for the {@link WorldClock} type.
*/
@ApiStatus.Experimental
@ApiStatus.NonExtendable
public interface WorldClockRegistryEntry {
/**
* A mutable builder for the {@link WorldClockRegistryEntry} plugins may change in applicable registry events.
*/
@ApiStatus.Experimental
@ApiStatus.NonExtendable
interface Builder extends WorldClockRegistryEntry, RegistryBuilder<WorldClock> {
}
}
@@ -14,13 +14,9 @@ import io.papermc.paper.registry.data.InstrumentRegistryEntry;
import io.papermc.paper.registry.data.JukeboxSongRegistryEntry;
import io.papermc.paper.registry.data.PaintingVariantRegistryEntry;
import io.papermc.paper.registry.data.PigVariantRegistryEntry;
import io.papermc.paper.registry.data.TimelineRegistryEntry;
import io.papermc.paper.registry.data.WolfVariantRegistryEntry;
import io.papermc.paper.registry.data.WorldClockRegistryEntry;
import io.papermc.paper.registry.data.ZombieNautilusVariantRegistryEntry;
import io.papermc.paper.registry.data.dialog.DialogRegistryEntry;
import io.papermc.paper.world.Timeline;
import io.papermc.paper.world.WorldClock;
import org.bukkit.Art;
import org.bukkit.GameEvent;
import org.bukkit.JukeboxSong;
@@ -60,8 +56,6 @@ public final class RegistryEvents {
public static final RegistryEventProvider<Pig.Variant, PigVariantRegistryEntry.Builder> PIG_VARIANT = create(RegistryKey.PIG_VARIANT);
public static final RegistryEventProvider<ZombieNautilus.Variant, ZombieNautilusVariantRegistryEntry.Builder> ZOMBIE_NAUTILUS_VARIANT = create(RegistryKey.ZOMBIE_NAUTILUS_VARIANT);
public static final RegistryEventProvider<Dialog, DialogRegistryEntry.Builder> DIALOG = create(RegistryKey.DIALOG);
public static final RegistryEventProvider<WorldClock, WorldClockRegistryEntry.Builder> WORLD_CLOCK = create(RegistryKey.WORLD_CLOCK);
public static final RegistryEventProvider<Timeline, TimelineRegistryEntry.Builder> TIMELINE = create(RegistryKey.TIMELINE);
// End generate - RegistryEvents
private RegistryEvents() {
@@ -1,11 +0,0 @@
package io.papermc.paper.world;
import org.bukkit.Keyed;
import org.jetbrains.annotations.ApiStatus;
/**
* Represents a timeline that can apply world attributes or define time markers for a {@link WorldClock}.
*/
@ApiStatus.Experimental
public interface Timeline extends Keyed {
}
@@ -1,11 +0,0 @@
package io.papermc.paper.world;
import org.bukkit.Keyed;
import org.jetbrains.annotations.ApiStatus;
/**
* Represents a world clock.
*/
@ApiStatus.Experimental
public interface WorldClock extends Keyed {
}
@@ -857,11 +857,15 @@ public final class Bukkit {
}
/**
* Gets the world with the given name.
* Gets the world with the given legacy Bukkit name.
*
* @param name the name of the world to retrieve
* @return a world with the given name, or null if none exists
* <p>This method is considered obsolete and is a candidate for future deprecation.
* Prefer using {@link #getWorld(NamespacedKey)}.</p>
*
* @param name the legacy Bukkit name of the world to retrieve
* @return a world with the given legacy Bukkit name, or null if none exists
*/
@ApiStatus.Obsolete
@Nullable
public static World getWorld(@NotNull String name) {
return server.getWorld(name);
@@ -877,7 +881,7 @@ public final class Bukkit {
public static World getWorld(@NotNull UUID uid) {
return server.getWorld(uid);
}
// Paper start
/**
* Gets the world from the given NamespacedKey
*
@@ -899,7 +903,6 @@ public final class Bukkit {
public static World getWorld(@NotNull net.kyori.adventure.key.Key worldKey) {
return server.getWorld(worldKey);
}
// Paper end
/**
* Create a new virtual {@link WorldBorder}.
@@ -1,14 +1,14 @@
package org.bukkit;
import com.google.common.base.Preconditions;
import io.papermc.paper.math.FinePosition;
import io.papermc.paper.math.Rotation;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Predicate;
import io.papermc.paper.math.FinePosition;
import io.papermc.paper.math.Rotation;
import org.bukkit.block.Block;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.entity.Entity;
@@ -574,7 +574,7 @@ public class Location implements Cloneable, ConfigurationSerializable, io.paperm
} else if (o.getWorld() == null || getWorld() == null) {
throw new IllegalArgumentException("Cannot measure distance to a null world");
} else if (o.getWorld() != getWorld()) {
throw new IllegalArgumentException("Cannot measure distance between " + getWorld().getKey() + " and " + o.getWorld().getKey());
throw new IllegalArgumentException("Cannot measure distance between " + getWorld().key().asString() + " and " + o.getWorld().key().asString());
}
return NumberConversions.square(x - o.x) + NumberConversions.square(y - o.y) + NumberConversions.square(z - o.z);
@@ -1186,10 +1186,11 @@ public class Location implements Cloneable, ConfigurationSerializable, io.paperm
@Utility
@NotNull
public Map<String, Object> serialize() {
Map<String, Object> data = new HashMap<String, Object>();
Map<String, Object> data = new HashMap<>();
if (this.world != null) {
data.put("world_key", getWorld().getKey().toString());
World world = this.getWorld();
if (world != null) {
data.put("world_key", world.key().asString());
}
data.put("x", this.x);
@@ -1219,7 +1220,8 @@ public class Location implements Cloneable, ConfigurationSerializable, io.paperm
requiresWorld = true;
}
if (args.containsKey("world_key")) {
world = Bukkit.getWorld(NamespacedKey.fromString((String) args.get("world_key")));
NamespacedKey key = NamespacedKey.fromString((String) args.get("world_key"));
world = key == null ? null : Bukkit.getWorld(key);
requiresWorld = true;
}
@@ -1227,7 +1229,6 @@ public class Location implements Cloneable, ConfigurationSerializable, io.paperm
throw new IllegalArgumentException("unknown world");
}
return new Location(world, NumberConversions.toDouble(args.get("x")), NumberConversions.toDouble(args.get("y")), NumberConversions.toDouble(args.get("z")), NumberConversions.toFloat(args.get("yaw")), NumberConversions.toFloat(args.get("pitch")));
}
@@ -5,6 +5,7 @@ import java.util.List;
import java.util.Random;
import java.util.function.Consumer;
import java.util.function.Predicate;
import io.papermc.paper.world.MoonPhase;
import org.bukkit.block.Biome;
import org.bukkit.block.BlockState;
import org.bukkit.block.data.BlockData;
@@ -526,12 +527,12 @@ public interface RegionAccessor extends Keyed, io.papermc.paper.world.flag.Featu
@NotNull
public <T extends Entity> T addEntity(@NotNull T entity);
// Paper start
/**
* @return the current moon phase at the current time in the world
* @apiNote the returned value may be inaccurate in custom biome using environmental attribute override
*/
@NotNull
io.papermc.paper.world.MoonPhase getMoonPhase();
MoonPhase getMoonPhase();
/**
* Get the world's key
@@ -559,5 +560,4 @@ public interface RegionAccessor extends Keyed, io.papermc.paper.world.flag.Featu
* @return collides or not
*/
boolean hasCollisionsIn(@NotNull org.bukkit.util.BoundingBox boundingBox);
// Paper end
}
@@ -5,6 +5,7 @@ import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import io.papermc.paper.datacomponent.DataComponentType;
import io.papermc.paper.entity.poi.PoiType;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.TypedKey;
@@ -280,6 +281,12 @@ public interface Registry<T extends Keyed> extends Iterable<T> {
* @see Villager.Profession
*/
Registry<Villager.Profession> VILLAGER_PROFESSION = registryFor(RegistryKey.VILLAGER_PROFESSION);
/**
* Point of interest types.
*
* @see PoiType
*/
Registry<PoiType> POINT_OF_INTEREST_TYPE = registryFor(RegistryKey.POINT_OF_INTEREST_TYPE);
/**
* Villager type.
*
@@ -16,7 +16,6 @@ import java.util.UUID;
import java.util.function.Consumer;
import java.util.logging.Logger;
import io.papermc.paper.configuration.ServerConfiguration;
import io.papermc.paper.world.WorldClock;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
@@ -166,85 +165,6 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
@NotNull
public Collection<? extends Player> getOnlinePlayers();
/**
* Gets the relative in-game time of the specified clock.
* <p>
* The relative time is analogous to hours * 1000
*
* @param clock the clock
* @return The current relative time
* @see #getFullTime(WorldClock) Returns an absolute time of the clock
*/
public long getTime(@NotNull WorldClock clock);
/**
* Sets the relative in-game time of the specified clock.
* <p>
* The relative time is analogous to hours * 1000
* <p>
* Note that setting the relative time below the current relative time
* will actually move the clock forward a day. If you require to rewind
* time, please see {@link #setFullTime(WorldClock, long)}
*
* @param clock the clock
* @param time The new relative time to set the clock to (in hours*1000)
* @see #setFullTime(WorldClock, long) Sets the absolute time of a specific clock
*/
public void setTime(@NotNull WorldClock clock, long time);
/**
* Gets the full in-game time of the specified clock.
*
* @param clock the clock
* @return The current absolute time
* @see #getTime(WorldClock) Returns a relative time of the clock
*/
public long getFullTime(@NotNull WorldClock clock);
/**
* Sets the in-game time of the specified clock.
* <p>
* Note that this sets the full time of the clock, which may cause adverse
* effects such as breaking redstone clocks and any scheduled events
*
* @param clock the clock
* @param time The new absolute time to set this clock to
* @see #setTime(WorldClock, long) Sets the relative time of a specific clock
*/
public void setFullTime(@NotNull WorldClock clock, long time);
/**
* Gets whether the specified clock is paused.
*
* @param clock the clock
* @return whether the clock is paused
*/
public boolean isClockPaused(@NotNull WorldClock clock);
/**
* Sets whether the specified clock is paused.
*
* @param clock the clock
* @param paused whether the clock is paused
*/
public void setClockPaused(@NotNull WorldClock clock, boolean paused);
/**
* Gets the rate of the specified clock.
*
* @param clock the clock
* @return the clock rate
*/
public float getClockRate(@NotNull WorldClock clock);
/**
* Sets the rate of the specified clock.
*
* @param clock the clock
* @param rate the clock rate
*/
public void setClockRate(@NotNull WorldClock clock, float rate);
/**
* Get the maximum amount of players which can login to this server.
*
+76 -30
View File
@@ -1,8 +1,8 @@
package org.bukkit;
import io.papermc.paper.entity.poi.PoiSearchResult;
import io.papermc.paper.entity.poi.PoiType;
import io.papermc.paper.raytracing.PositionedRayTraceConfigurationBuilder;
import io.papermc.paper.world.Timeline;
import io.papermc.paper.world.WorldClock;
import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
@@ -45,6 +45,7 @@ import org.bukkit.util.BoundingBox;
import org.bukkit.util.RayTraceResult;
import org.bukkit.util.StructureSearchResult;
import org.bukkit.util.Vector;
import org.checkerframework.checker.index.qual.Positive;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
@@ -2073,10 +2074,8 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
*
* @param time The new relative time to set the in-game time to (in
* hours*1000)
* @see Server#setTime(WorldClock, long) Sets the relative time of a specific clock
* @deprecated use {@link Server#setTime(WorldClock, long)} with {@link #getClock()} instead
* @see #setFullTime(long) Sets the absolute time of this world
*/
@Deprecated(since = "26.1")
public void setTime(long time);
/**
@@ -2094,31 +2093,11 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
* effects such as breaking redstone clocks and any scheduled events
*
* @param time The new absolute time to set this world to
* @see Server#setFullTime(WorldClock, long) Sets the absolute time of a specific clock
* @deprecated use {@link Server#setFullTime(WorldClock, long)} with {@link #getClock()} instead
* @see #setTime(long) Sets the relative time of this world
* @throws IllegalArgumentException if this world does not have a world clock (e.g. the nether)
*/
@Deprecated(since = "26.1")
public void setFullTime(long time);
/**
* Gets the clock of this world.
* <p>
* This may be a world-specific override of the dimension type's clock.
*
* @return the clock, or null if this world does not have one
*/
@Nullable
public WorldClock getClock();
/**
* Gets the timelines applied by this world's dimension type.
*
* @return the timelines
*/
@NotNull
public List<Timeline> getTimelines();
// Paper start
/**
@@ -2791,10 +2770,11 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
/**
* Gets if this world is natural.
* <p>
* When false, the moon is not visible and eyeblossoms do not open/close
* When true, eyeblossoms cycle open/close, nether portals can spawn
* zombified piglins and creaking heart works
*
* @return true if world is natural
* @deprecated replaced by the gameplay/eyeblossom_open and gameplay/creaking_active environment attributes
* @deprecated replaced by the gameplay/nether_portal_spawns_piglin, gameplay/eyeblossom_open and gameplay/creaking_active environmental attributes
*/
@Deprecated(since = "1.21.11")
public boolean isNatural();
@@ -2806,7 +2786,7 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
* not always be the case.
*
* @return true if beds work in this world
* @deprecated Due to 1.21.11 beds changes, a boolean no longer
* @deprecated due to 1.21.11 beds changes, a boolean no longer
* represents if they work. There is no replacement API yet
*/
@ApiStatus.Obsolete(since = "1.21.11")
@@ -2831,6 +2811,7 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
* transforming to zombified piglins.
*
* @return true if piglins will not transform to zombified piglins
* @apiNote the returned value may be inaccurate in custom biome using environmental attribute override
*/
public boolean isPiglinSafe();
@@ -2838,6 +2819,7 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
* Gets if this world allows players to charge and use respawn anchors.
*
* @return true if players can charge and use respawn anchors
* @apiNote the returned value may be inaccurate in custom biome using environmental attribute override
*/
public boolean isRespawnAnchorWorks();
@@ -2846,6 +2828,7 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
* raid.
*
* @return true if raids will be triggered
* @apiNote the returned value may be inaccurate in custom biome using environmental attribute override
*/
public boolean hasRaids();
@@ -2859,7 +2842,7 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
* </ul>
*
* @return true if this world has the above mechanics
* @deprecated as of 1.21.11, ultra warm is replaced by the WATER_EVAPORATES, FAST_LAVA, and DEFAULT_DRIPSTONE_PARTICLE environment attributes.
* @deprecated replaced by the gameplay/water_evaporates and gameplay/fast_lava environmental attributes
*/
@Deprecated(since = "1.21.11")
public boolean isUltraWarm();
@@ -4317,6 +4300,69 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
return java.util.Optional.ofNullable(this.locateNearestBiome(origin, radius, step, step, biome)).map(BiomeSearchResult::getLocation).orElse(null);
}
/**
* Finds the nearest point of interest closest to the given location
* without any occupancy restriction.
*
* @param origin where to start looking for a new point of interest at
* @param poiType the poi type to find
* @param radius the radius
* @return a location at the nearest PoiType or {@code null} if no poi was found
*/
@Nullable
default Location locateNearestPoi(@NotNull Location origin, @NotNull PoiType poiType, @Positive int radius) {
return this.locateNearestPoi(origin, poiType, radius, PoiType.Occupancy.ANY);
}
/**
* Finds the nearest point of interest closest to the given location.
* <p>
* {@link PoiType} that return {@code false} for {@link PoiType#hasOccupants()}
* may not behave as expected for given occupancies other than
* {@link PoiType.Occupancy#ANY}.
*
* @param origin where to start looking for a new point of interest at
* @param poiType the poi type to find
* @param radius the radius
* @param occupancy the current required occupancy of the point of interest
* @return a location at the nearest PoiType or {@code null} if no poi was found
*/
@Nullable
Location locateNearestPoi(@NotNull Location origin, @NotNull PoiType poiType, @Positive int radius, @NotNull PoiType.Occupancy occupancy);
/**
* Finds all valid {@link PoiType} in the provided radius and returns them
* in a list format without any occupancy restriction.
*
* @param origin the center point of the radius
* @param poiTypePredicate the predicate to test whether a PoiType
* can be collected into the result
* @param radius the radius
* @return a list of search results containing all found Poi's in the range
*/
@NotNull
default List<PoiSearchResult> locateAllPoiInRange(@NotNull Location origin, @NotNull Predicate<PoiType> poiTypePredicate, @Positive int radius) {
return this.locateAllPoiInRange(origin, poiTypePredicate, radius, PoiType.Occupancy.ANY);
}
/**
* Finds all valid {@link PoiType} in the provided radius and returns them
* in a list format.
* <p>
* {@link PoiType} that return false for {@link PoiType#hasOccupants()}
* may not behave as expected for given occupancies other than
* {@link PoiType.Occupancy#ANY}.
*
* @param origin the center point of the radius
* @param poiTypePredicate the predicate to test whether a PoiType
* can be collected into the result
* @param radius the radius
* @param occupancy the current required occupancy of the point of interest
* @return a list of search results containing all found Poi's in the range
*/
@NotNull
List<PoiSearchResult> locateAllPoiInRange(@NotNull Location origin, @NotNull Predicate<PoiType> poiTypePredicate, @Positive int radius, @NotNull PoiType.Occupancy occupancy);
/**
* Gets the coordinate scaling of this world.
*
@@ -1,14 +1,8 @@
package org.bukkit;
import com.google.common.base.Preconditions;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.TypedKey;
import io.papermc.paper.world.Timeline;
import io.papermc.paper.world.WorldClock;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;
import io.papermc.paper.math.Position;
import org.bukkit.command.CommandSender;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.ChunkGenerator;
@@ -32,8 +26,10 @@ public class WorldCreator {
private String generatorSettings = "";
private boolean hardcore = false;
private boolean bonusChest = false;
private TypedKey<WorldClock> clock = null;
private List<TypedKey<Timeline>> timelines = null;
private @Nullable Position spawnPositionOverride;
private @Nullable Float spawnYawOverride;
private @Nullable Float spawnPitchOverride;
/**
* Creates an empty WorldCreationOptions for the given world name.
@@ -161,8 +157,6 @@ public class WorldCreator {
generateStructures = world.canGenerateStructures();
hardcore = world.isHardcore();
bonusChest = world.hasBonusChest();
clock = world.getClock() == null ? null : TypedKey.create(RegistryKey.WORLD_CLOCK, world.getClock().getKey());
timelines = world.getTimelines().stream().map(timeline -> TypedKey.create(RegistryKey.TIMELINE, timeline.getKey())).toList();
return this;
}
@@ -186,8 +180,6 @@ public class WorldCreator {
generatorSettings = creator.generatorSettings();
hardcore = creator.hardcore();
bonusChest = creator.bonusChest();
clock = creator.clock();
timelines = creator.timelines() == null ? null : new ArrayList<>(creator.timelines());
return this;
}
@@ -274,6 +266,80 @@ public class WorldCreator {
return this;
}
/**
* Sets the forced spawn position for the world created by this {@link WorldCreator}.
* <p>
* This overrides vanilla and custom generator behavior without loading any chunks.
* When a forced spawn is specified, the bonus chest will not be generated.
*
* @param position the spawn position
* @param yaw the yaw rotation at spawn
* @param pitch the pitch rotation at spawn
* @return this object, for chaining
*/
@NotNull
public WorldCreator forcedSpawnPosition(@NotNull Position position, float yaw, float pitch) {
this.spawnPositionOverride = position; // If you set this to null, it wont do anything!
this.spawnYawOverride = yaw;
this.spawnPitchOverride = pitch;
return this;
}
/**
* Clears any previously forced spawn position.
* <p>
* After calling this, vanilla spawn selection behavior is used.
*
* @return this object, for chaining
*/
@NotNull
public WorldCreator clearForcedSpawnPosition() {
this.spawnPositionOverride = null;
this.spawnYawOverride = null;
this.spawnPitchOverride = null;
return this;
}
/**
* Gets the forced spawn position that will be applied when this world is created.
* <p>
* If this returns {@code null}, vanilla or custom generator behavior will be used
* to determine the spawn position.
*
* @return the forced spawn position, or {@code null} for the vanilla behavior
*/
public @Nullable Position forcedSpawnPosition() {
return this.spawnPositionOverride;
}
/**
* Gets the forced spawn yaw that will be applied when this world is created.
* <p>
* If this returns {@code null}, the spawn yaw will be determined by vanilla behavior
* or the world generator.
* <p>
* This value is only meaningful if a forced spawn position is present.
*
* @return the forced spawn yaw, or {@code null} for the vanilla behavior
*/
public @Nullable Float forcedSpawnYaw() {
return this.spawnYawOverride;
}
/**
* Gets the forced spawn pitch that will be applied when this world is created.
* <p>
* If this returns {@code null}, the spawn pitch will be determined by vanilla behavior
* or the world generator.
* <p>
* This value is only meaningful if a forced spawn position is present.
*
* @return the forced spawn pitch, or {@code null} for the vanilla behavior
*/
public @Nullable Float forcedSpawnPitch() {
return this.spawnPitchOverride;
}
/**
* Gets the generator that will be used to create or load the world.
* <p>
@@ -526,56 +592,6 @@ public class WorldCreator {
return bonusChest;
}
/**
* Sets the clock that will be used by this world.
* <p>
* If unset, the clock from the selected environment's dimension type will be used.
* If set, this overrides the selected environment dimension type's clock.
*
* @param clock the clock, or null to use the environment default
* @return This object, for chaining
*/
@NotNull
public WorldCreator clock(@Nullable TypedKey<WorldClock> clock) {
this.clock = clock;
return this;
}
/**
* Gets the clock override that will be used by this world.
*
* @return the clock override, or null to use the environment default
*/
@Nullable
public TypedKey<WorldClock> clock() {
return this.clock;
}
/**
* Sets the timelines that will be used by this world's dimension type.
* <p>
* If unset, the timelines from the selected environment's dimension type will be used.
* Passing an empty collection will create or load the world with no timelines.
*
* @param timelines the timelines, or null to use the environment default
* @return This object, for chaining
*/
@NotNull
public WorldCreator timelines(@Nullable Collection<TypedKey<Timeline>> timelines) {
this.timelines = timelines == null ? null : new ArrayList<>(timelines);
return this;
}
/**
* Gets the timeline overrides that will be used by this world's dimension type.
*
* @return the timeline overrides, or null to use the environment default
*/
@Nullable
public List<TypedKey<Timeline>> timelines() {
return this.timelines == null ? null : List.copyOf(this.timelines);
}
/**
* Creates a world with the specified options.
* <p>
@@ -10,6 +10,7 @@ import org.bukkit.Keyed;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.Translatable;
import org.bukkit.entity.EntityType;
import org.bukkit.util.OldEnum;
import org.jetbrains.annotations.NotNull;
@@ -170,6 +171,13 @@ public interface Attribute extends OldEnum<Attribute>, Keyed, Translatable, net.
@NotNull
Sentiment getSentiment();
/**
* {@return the default value of this attribute}
* <p>
* Default attribute values may differ between entity types, use {@link EntityType#getDefaultAttributes()} to get default attribute values for a specific entity type.
*/
double getDefaultValue();
/**
* @param name of the attribute.
* @return the attribute with the given name.
@@ -111,6 +111,11 @@ public interface AttributeInstance {
* Gets the default value of the Attribute attached to this instance.
*
* @return server default value
* @deprecated this method is placed misleadingly as it does not return the default value in the current entity's context, but in the server's context.
* Use {@link Attribute#getDefaultValue()} as a replacement and see the note there about default values for specific entity types
* @see Attribute#getDefaultValue()
* @see org.bukkit.entity.EntityType#getDefaultAttributes()
*/
@Deprecated(since = "26.1")
double getDefaultValue();
}
@@ -115,7 +115,7 @@ public interface AreaEffectCloud extends Entity {
float getRadiusPerTick();
/**
* Gets the amount that the radius of this cloud will decrease by each tick.
* Sets the amount that the radius of this cloud will decrease by each tick.
*
* @param radius per tick delta
*/
@@ -139,7 +139,7 @@ public interface AreaEffectCloud extends Entity {
/**
* Sets the particle which this cloud will be composed of
*
* @param <T> type of particle data (see {@link Particle#getDataType()}
* @param <T> type of particle data (see {@link Particle#getDataType()})
* @param particle the new particle type
* @param data the data to use for the particle or null,
* the type of this depends on {@link Particle#getDataType()}
@@ -2,12 +2,15 @@ package org.bukkit.entity;
import org.bukkit.attribute.Attribute;
import org.bukkit.damage.DamageSource;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.bukkit.damage.DamageType;
import org.bukkit.event.entity.EntityRegainHealthEvent;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
/**
* Represents an {@link Entity} that has health and can take damage.
*/
@NullMarked
public interface Damageable extends Entity {
/**
* Deals the given amount of damage to this entity.
@@ -32,7 +35,24 @@ public interface Damageable extends Entity {
* @param amount amount of damage to deal
* @param damageSource source to which the damage should be attributed
*/
void damage(double amount, @NotNull DamageSource damageSource);
void damage(double amount, DamageSource damageSource);
/**
* Sets the entity's health to 0 and kill the entity with a generic DamageSource.
*
* @throws IllegalStateException if is used in world generation
*/
default void kill() {
this.kill(DamageSource.builder(DamageType.GENERIC_KILL).build());
}
/**
* Sets the entity's health to 0 and kill the entity with the specified DamageSource.
*
* @param damageSource the DamageSource to use for the kill
* @throws IllegalStateException if is used in world generation
*/
void kill(DamageSource damageSource);
/**
* Gets the entity's health from 0 to {@link #getMaxHealth()}, where 0 is dead.
@@ -57,7 +77,7 @@ public interface Damageable extends Entity {
* @param amount heal amount
*/
default void heal(final double amount) {
this.heal(amount, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.CUSTOM);
this.heal(amount, EntityRegainHealthEvent.RegainReason.CUSTOM);
}
/**
@@ -66,7 +86,7 @@ public interface Damageable extends Entity {
* @param amount heal amount
* @param reason heal reason
*/
void heal(double amount, @NotNull org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason reason);
void heal(double amount, EntityRegainHealthEvent.RegainReason reason);
/**
* Gets the entity's absorption amount.
@@ -1,17 +1,23 @@
package org.bukkit.entity;
import io.papermc.paper.datacomponent.DataComponentTypes;
import java.util.UUID;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.FireworkMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public interface Firework extends Projectile {
/**
* Get a copy of the fireworks meta
*
* @return A copy of the current Firework meta
* @apiNote obsolete in favor of {@link #getItem()} / {@link #setItem(ItemStack)} with the equivalent {@link DataComponentTypes#FIREWORKS} component
*/
@NotNull
@ApiStatus.Obsolete
FireworkMeta getFireworkMeta();
/**
@@ -20,8 +26,10 @@ public interface Firework extends Projectile {
* Adjusts detonation ticks automatically.
*
* @param meta The FireworkMeta to apply
* @apiNote obsolete in favor of {@link #getItem()} / {@link #setItem(ItemStack)} with the equivalent {@link DataComponentTypes#FIREWORKS} component
*/
void setFireworkMeta(@NotNull FireworkMeta meta);
@ApiStatus.Obsolete
void setFireworkMeta(FireworkMeta meta);
/**
* Set the {@link LivingEntity} to which this firework is attached.
@@ -48,8 +56,7 @@ public interface Firework extends Projectile {
*
* @return the attached entity, or null if none
*/
@Nullable
LivingEntity getAttachedTo();
@Nullable LivingEntity getAttachedTo();
/**
* Set the ticks that this firework has been alive. If this value exceeds
@@ -59,7 +66,7 @@ public interface Firework extends Projectile {
* @deprecated use {@link #setTicksFlown(int)}
* @return true if the life was set, false if this firework has already detonated
*/
@Deprecated(forRemoval = true) // Paper
@Deprecated(forRemoval = true, since = "1.18.2")
boolean setLife(int ticks);
/**
@@ -69,7 +76,7 @@ public interface Firework extends Projectile {
* @deprecated use {@link #getTicksFlown()}
* @return the life ticks
*/
@Deprecated(forRemoval = true) // Paper
@Deprecated(forRemoval = true, since = "1.18.2")
int getLife();
/**
@@ -79,7 +86,7 @@ public interface Firework extends Projectile {
* @deprecated use {@link #setTicksToDetonate(int)}
* @return true if the time was set, false if this firework has already detonated
*/
@Deprecated(forRemoval = true) // Paper
@Deprecated(forRemoval = true, since = "1.18.2")
boolean setMaxLife(int ticks);
/**
@@ -88,7 +95,7 @@ public interface Firework extends Projectile {
* @deprecated use {@link #getTicksToDetonate()}
* @return the maximum life in ticks
*/
@Deprecated(forRemoval = true) // Paper
@Deprecated(forRemoval = true, since = "1.18.2")
int getMaxLife();
/**
@@ -122,30 +129,30 @@ public interface Firework extends Projectile {
*/
void setShotAtAngle(boolean shotAtAngle);
// Paper start
@org.jetbrains.annotations.Nullable
public java.util.UUID getSpawningEntity();
/**
* Retrieves the UUID of the entity responsible for spawning this firework.
*
* @return the UUID of the spawning entity, or null if no spawning entity is associated
*/
@Nullable UUID getSpawningEntity();
/**
* If this firework is boosting an entity, return it
* @deprecated use {@link #getAttachedTo()}
* @see #setAttachedTo(LivingEntity)
* @return The entity being boosted
*/
@org.jetbrains.annotations.Nullable
@Deprecated
default LivingEntity getBoostedEntity() {
return getAttachedTo();
@Deprecated(since = "1.18.2")
default @Nullable LivingEntity getBoostedEntity() {
return this.getAttachedTo();
}
// Paper end
// Paper start - Firework API
/**
* Gets the item used in the firework.
*
* @return firework item
*/
@NotNull
public org.bukkit.inventory.ItemStack getItem();
ItemStack getItem();
/**
* Sets the item used in the firework.
@@ -154,7 +161,7 @@ public interface Firework extends Projectile {
*
* @param itemStack item to set
*/
void setItem(@org.jetbrains.annotations.Nullable org.bukkit.inventory.ItemStack itemStack);
void setItem(@Nullable ItemStack itemStack);
/**
* Gets the number of ticks the firework has flown.
@@ -184,5 +191,4 @@ public interface Firework extends Projectile {
* @param ticks ticks to detonate on
*/
void setTicksToDetonate(int ticks);
// Paper end
}
@@ -1,10 +1,12 @@
package org.bukkit.entity;
import io.papermc.paper.datacomponent.DataComponentTypes;
import io.papermc.paper.datacomponent.item.Fireworks;
import io.papermc.paper.datacomponent.item.UseCooldown;
import java.util.Arrays;
import java.util.Collection;
import java.util.Set;
import java.util.function.Consumer;
import io.papermc.paper.datacomponent.item.UseCooldown;
import net.kyori.adventure.key.Key;
import org.bukkit.GameMode;
import org.bukkit.Location;
@@ -20,7 +22,6 @@ import org.bukkit.inventory.MainHand;
import org.bukkit.inventory.MenuType;
import org.bukkit.inventory.Merchant;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.inventory.meta.FireworkMeta;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@@ -1009,16 +1010,35 @@ public interface HumanEntity extends LivingEntity, AnimalTamer, InventoryHolder
* Perform a firework boost.
* <p>
* This method will only work such that {@link #isGliding()} is true and
* the entity is actively gliding with an elytra. Additionally, the supplied
* {@code fireworkItemStack} must be a firework rocket. The power of the boost
* will directly correlate to {@link FireworkMeta#getPower()}.
* the entity is actively gliding with an item with {@link DataComponentTypes#GLIDER} component.
* <p>
* The power of the boost will directly correlate to {@link Fireworks#flightDuration()} from {@link DataComponentTypes#FIREWORKS} component.
* <p>
* This method does not fire {@link com.destroystokyo.paper.event.player.PlayerElytraBoostEvent}.
*
* @param fireworkItemStack the firework item stack to use to glide
* @param boosterItem the item to use to boost
* @return the attached {@link Firework}, or null if the entity could not
* be boosted
* @throws IllegalArgumentException if the fireworkItemStack is not a firework
*/
@Nullable
public Firework fireworkBoost(ItemStack fireworkItemStack);
Firework fireworkBoost(ItemStack boosterItem);
/**
* Perform a firework boost.
* <p>
* This method will only work such that {@link #isGliding()} is true and
* the entity is actively gliding with an item with {@link DataComponentTypes#GLIDER} component.
* <p>
* The power of the boost will directly correlate to {@link Fireworks#flightDuration()} from {@link DataComponentTypes#FIREWORKS} component.
* <p>
* This method does not fire {@link com.destroystokyo.paper.event.player.PlayerElytraBoostEvent}.
*
* @return the attached {@link Firework}, or null if the entity could not
* be boosted
*/
@Nullable
default Firework fireworkBoost() {
return this.fireworkBoost(ItemStack.empty());
}
}
@@ -1,6 +1,7 @@
package org.bukkit.entity;
import com.destroystokyo.paper.ClientOption;
import com.google.common.base.Preconditions;
import io.papermc.paper.connection.PlayerGameConnection;
import io.papermc.paper.entity.LookAnchor;
import io.papermc.paper.entity.PlayerGiveResult;
@@ -3349,8 +3350,7 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
/**
* Get the player's current client side view distance.
* <br>
* Will default to the server view distance if the client has not yet
* communicated this information,
* Will default to 2 if the client has not yet communicated this information.
*
* @return client view distance as above
*/
@@ -3617,24 +3617,22 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
<T> T getClientOption(ClientOption<T> option);
// Paper end - client option API
// Paper start - elytra boost API
/**
* Boost a Player that's {@link #isGliding()} using a {@link Firework}.
* If the creation of the entity is cancelled, no boosting is done.
* This method does not fire {@link com.destroystokyo.paper.event.player.PlayerElytraBoostEvent}.
*
* @param firework The {@link Material#FIREWORK_ROCKET} to boost the player with
* @param boosterItem The itemstack to boost the player with
* @return The {@link Firework} boosting the Player or null if the spawning of the entity was cancelled
* @throws IllegalArgumentException if {@link #isGliding()} is false
* or if the {@code firework} isn't a {@link Material#FIREWORK_ROCKET}
* @throws IllegalStateException if {@link #isGliding()} is false
* @deprecated use {@link HumanEntity#fireworkBoost(ItemStack)} instead. Note that this method <b>does not</b>
* check if the player is gliding or not.
*/
default @Nullable Firework boostElytra(final ItemStack firework) {
com.google.common.base.Preconditions.checkState(this.isGliding(), "Player must be gliding");
return this.fireworkBoost(firework);
@Deprecated(since = "1.20.5")
default @Nullable Firework boostElytra(final ItemStack boosterItem) {
Preconditions.checkState(this.isGliding(), "Player must be gliding");
return this.fireworkBoost(boosterItem);
}
// Paper end - elytra boost API
// Paper start - sendOpLevel API
/**
@@ -1,8 +1,9 @@
package org.bukkit.entity;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jspecify.annotations.NullMarked;
@NullMarked
public interface ThrowableProjectile extends Projectile {
/**
@@ -10,7 +11,6 @@ public interface ThrowableProjectile extends Projectile {
*
* @return The thrown item display ItemStack
*/
@NotNull
ItemStack getItem();
/**
@@ -18,5 +18,5 @@ public interface ThrowableProjectile extends Projectile {
*
* @param item ItemStack set to be displayed
*/
void setItem(@NotNull ItemStack item);
void setItem(ItemStack item);
}
@@ -1,13 +1,17 @@
package org.bukkit.entity;
import io.papermc.paper.datacomponent.DataComponentTypes;
import java.util.Collection;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.PotionMeta;
import org.bukkit.potion.PotionEffect;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
* Represents a thrown potion bottle
*/
@NullMarked
public interface ThrownPotion extends ThrowableProjectile {
/**
@@ -15,8 +19,7 @@ public interface ThrownPotion extends ThrowableProjectile {
*
* @return The potion effects
*/
@NotNull
public Collection<PotionEffect> getEffects();
Collection<PotionEffect> getEffects();
/**
* Returns a copy of the ItemStack for this thrown potion.
@@ -27,25 +30,24 @@ public interface ThrownPotion extends ThrowableProjectile {
*
* @return A copy of the ItemStack for this thrown potion.
*/
@NotNull
public ItemStack getItem();
ItemStack getItem();
/**
* Set the ItemStack for this thrown potion.
*
* @param item New ItemStack
*/
public void setItem(@NotNull ItemStack item);
void setItem(ItemStack item);
// Paper start - Projectile API
/**
* Gets a copy of the PotionMeta for this thrown potion.
* This includes what effects will be applied by this potion.
*
* @return potion meta
* @apiNote obsolete in favor of {@link #getItem()} / {@link #setItem(ItemStack)} with the equivalent {@link DataComponentTypes#POTION_CONTENTS} component
*/
@NotNull
org.bukkit.inventory.meta.PotionMeta getPotionMeta();
@ApiStatus.Obsolete
PotionMeta getPotionMeta();
/**
* Sets the PotionMeta of this thrown potion.
@@ -54,12 +56,13 @@ public interface ThrownPotion extends ThrowableProjectile {
* Note that the type of {@link #getItem()} is irrelevant
*
* @param meta potion meta
* @apiNote obsolete in favor of {@link #getItem()} / {@link #setItem(ItemStack)} with the equivalent {@link DataComponentTypes#POTION_CONTENTS} component
*/
void setPotionMeta(@NotNull org.bukkit.inventory.meta.PotionMeta meta);
@ApiStatus.Obsolete
void setPotionMeta(PotionMeta meta);
/**
* Splashes the potion at its current location.
*/
void splash();
// Paper end
}
@@ -1,13 +1,22 @@
package org.bukkit.event.block;
import com.google.common.base.Preconditions;
import org.bukkit.block.Block;
import org.bukkit.event.HandlerList;
import org.checkerframework.common.value.qual.IntRange;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jspecify.annotations.NullMarked;
/**
* Called when a redstone current changes
* Called when a redstone current changes.
* <p>
* It includes the relevant mutation of the {@code powered} and {@code power}
* properties even if the block is not able to produce a redstone signal.
* For the {@code powered} property, a high state will be considered as
* a current of 15 and a low state as 0. Setting the new current to a different
* value will prevent most action in this case.
*/
@NullMarked
public class BlockRedstoneEvent extends BlockEvent {
private static final HandlerList HANDLER_LIST = new HandlerList();
@@ -16,46 +25,45 @@ public class BlockRedstoneEvent extends BlockEvent {
private int newCurrent;
@ApiStatus.Internal
public BlockRedstoneEvent(@NotNull final Block block, final int oldCurrent, final int newCurrent) {
public BlockRedstoneEvent(final Block block, final int oldCurrent, final int newCurrent) {
super(block);
this.oldCurrent = oldCurrent;
this.newCurrent = newCurrent;
}
/**
* Gets the old current of this block
* Gets the old current of this block.
*
* @return The previous current
* @return the previous current
*/
public int getOldCurrent() {
public @IntRange(from = 0, to = 15) int getOldCurrent() {
return this.oldCurrent;
}
/**
* Gets the new current of this block
* Gets the new current of this block.
*
* @return The new current
* @return the new current
*/
public int getNewCurrent() {
public @IntRange(from = 0, to = 15) int getNewCurrent() {
return this.newCurrent;
}
/**
* Sets the new current of this block
* Sets the new current of this block.
*
* @param newCurrent The new current to set
* @param newCurrent the new current to set
*/
public void setNewCurrent(int newCurrent) {
public void setNewCurrent(@IntRange(from = 0, to = 15) int newCurrent) {
Preconditions.checkArgument(newCurrent >= 0 && newCurrent <= 15, "New current must be a redstone signal between 0 and 15 (was %s)", newCurrent);
this.newCurrent = newCurrent;
}
@NotNull
@Override
public HandlerList getHandlers() {
return HANDLER_LIST;
}
@NotNull
public static HandlerList getHandlerList() {
return HANDLER_LIST;
}
@@ -102,6 +102,10 @@ public class EntityUnleashEvent extends EntityEvent implements Cancellable {
* When the entity's leashholder is more than 10 blocks away
*/
DISTANCE,
/**
* When the leashed entity is removed from the game
*/
LEASHED_GONE,
UNKNOWN;
}
}
@@ -1,27 +1,30 @@
package org.bukkit.event.hanging;
import org.bukkit.damage.DamageSource;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Hanging;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NullMarked;
/**
* Triggered when a hanging entity is removed by an entity
*/
@NullMarked
public class HangingBreakByEntityEvent extends HangingBreakEvent {
private final Entity remover;
private final DamageSource damageSource;
@ApiStatus.Internal
public HangingBreakByEntityEvent(@NotNull final Hanging hanging, @NotNull final Entity remover) {
this(hanging, remover, HangingBreakEvent.RemoveCause.ENTITY);
public HangingBreakByEntityEvent(final Hanging hanging, final Entity remover, final DamageSource damageSource) {
this(hanging, remover, damageSource, HangingBreakEvent.RemoveCause.ENTITY);
}
@ApiStatus.Internal
public HangingBreakByEntityEvent(@NotNull final Hanging hanging, @NotNull final Entity remover, @NotNull final HangingBreakEvent.RemoveCause cause) {
public HangingBreakByEntityEvent(final Hanging hanging, final Entity remover, final DamageSource damageSource, final HangingBreakEvent.RemoveCause cause) {
super(hanging, cause);
this.remover = remover;
this.damageSource = damageSource;
}
/**
@@ -29,8 +32,16 @@ public class HangingBreakByEntityEvent extends HangingBreakEvent {
*
* @return the entity that removed the hanging entity
*/
@NotNull
public Entity getRemover() {
return this.remover;
}
/**
* Gets the {@link DamageSource} that caused the hanging entity to be removed.
*
* @return the damage source
*/
public DamageSource getDamageSource() {
return this.damageSource;
}
}
@@ -6,24 +6,26 @@ import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
/**
* Called when a player gets kicked from the server
*/
@NullMarked
public class PlayerKickEvent extends PlayerEvent implements Cancellable {
private static final HandlerList HANDLER_LIST = new HandlerList();
private Component kickReason;
private Component leaveMessage;
private @Nullable Component leaveMessage;
private final Cause cause;
private boolean cancelled;
@ApiStatus.Internal
@Deprecated(forRemoval = true)
public PlayerKickEvent(@NotNull final Player playerKicked, @NotNull final String kickReason, @NotNull final String leaveMessage) {
public PlayerKickEvent(final Player playerKicked, final String kickReason, final String leaveMessage) {
super(playerKicked);
this.kickReason = LegacyComponentSerializer.legacySection().deserialize(kickReason);
this.leaveMessage = LegacyComponentSerializer.legacySection().deserialize(leaveMessage);
@@ -32,7 +34,7 @@ public class PlayerKickEvent extends PlayerEvent implements Cancellable {
@ApiStatus.Internal
@Deprecated(forRemoval = true)
public PlayerKickEvent(@NotNull final Player playerKicked, @NotNull final Component kickReason, @NotNull final Component leaveMessage) {
public PlayerKickEvent(final Player playerKicked, final Component kickReason, final Component leaveMessage) {
super(playerKicked);
this.kickReason = kickReason;
this.leaveMessage = leaveMessage;
@@ -40,7 +42,7 @@ public class PlayerKickEvent extends PlayerEvent implements Cancellable {
}
@ApiStatus.Internal
public PlayerKickEvent(@NotNull final Player playerKicked, @NotNull final Component kickReason, @NotNull final Component leaveMessage, @NotNull final Cause cause) {
public PlayerKickEvent(final Player playerKicked, final Component kickReason, final Component leaveMessage, final Cause cause) {
super(playerKicked);
this.kickReason = kickReason;
this.leaveMessage = leaveMessage;
@@ -52,7 +54,7 @@ public class PlayerKickEvent extends PlayerEvent implements Cancellable {
*
* @return string kick reason
*/
public @NotNull Component reason() {
public Component reason() {
return this.kickReason;
}
@@ -61,7 +63,7 @@ public class PlayerKickEvent extends PlayerEvent implements Cancellable {
*
* @param kickReason kick reason
*/
public void reason(@NotNull Component kickReason) {
public void reason(Component kickReason) {
this.kickReason = kickReason;
}
@@ -71,7 +73,6 @@ public class PlayerKickEvent extends PlayerEvent implements Cancellable {
* @return string kick reason
* @deprecated in favour of {@link #reason()}
*/
@NotNull
@Deprecated
public String getReason() {
return LegacyComponentSerializer.legacySection().serialize(this.kickReason);
@@ -81,10 +82,10 @@ public class PlayerKickEvent extends PlayerEvent implements Cancellable {
* Sets the reason why the player is getting kicked
*
* @param kickReason kick reason
* @deprecated in favour of {@link #reason(net.kyori.adventure.text.Component)}
* @deprecated in favour of {@link #reason(Component)}
*/
@Deprecated
public void setReason(@NotNull String kickReason) {
public void setReason(String kickReason) {
this.kickReason = LegacyComponentSerializer.legacySection().deserialize(kickReason);
}
@@ -93,16 +94,16 @@ public class PlayerKickEvent extends PlayerEvent implements Cancellable {
*
* @return string kick reason
*/
public @NotNull Component leaveMessage() {
public @Nullable Component leaveMessage() {
return this.leaveMessage;
}
/**
* Sets the leave message send to all online players
*
* @param leaveMessage leave message
* @param leaveMessage leave message. If {@code null}, no message will be sent
*/
public void leaveMessage(@NotNull Component leaveMessage) {
public void leaveMessage(@Nullable Component leaveMessage) {
this.leaveMessage = leaveMessage;
}
@@ -112,28 +113,26 @@ public class PlayerKickEvent extends PlayerEvent implements Cancellable {
* @return string kick reason
* @deprecated in favour of {@link #leaveMessage()}
*/
@NotNull
@Deprecated
public String getLeaveMessage() {
return LegacyComponentSerializer.legacySection().serialize(this.leaveMessage);
public @Nullable String getLeaveMessage() {
return LegacyComponentSerializer.legacySection().serializeOrNull(this.leaveMessage);
}
/**
* Sets the leave message send to all online players
*
* @param leaveMessage leave message
* @deprecated in favour of {@link #leaveMessage(net.kyori.adventure.text.Component)}
* @param leaveMessage leave message. If {@code null}, no message will be sent
* @deprecated in favour of {@link #leaveMessage(Component)}
*/
@Deprecated
public void setLeaveMessage(@NotNull String leaveMessage) {
this.leaveMessage = LegacyComponentSerializer.legacySection().deserialize(leaveMessage);
public void setLeaveMessage(@Nullable String leaveMessage) {
this.leaveMessage = LegacyComponentSerializer.legacySection().deserializeOrNull(leaveMessage);
}
/**
* Gets the cause of this kick
*/
@NotNull
public org.bukkit.event.player.PlayerKickEvent.Cause getCause() {
public PlayerKickEvent.Cause getCause() {
return this.cause;
}
@@ -147,13 +146,11 @@ public class PlayerKickEvent extends PlayerEvent implements Cancellable {
this.cancelled = cancel;
}
@NotNull
@Override
public HandlerList getHandlers() {
return HANDLER_LIST;
}
@NotNull
public static HandlerList getHandlerList() {
return HANDLER_LIST;
}
@@ -1,5 +1,6 @@
package org.bukkit.event.vehicle;
import org.bukkit.damage.DamageSource;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Vehicle;
import org.bukkit.event.Cancellable;
@@ -15,18 +16,30 @@ public class VehicleDamageEvent extends VehicleEvent implements Cancellable {
private static final HandlerList HANDLER_LIST = new HandlerList();
private final DamageSource damageSource;
private final Entity attacker;
private double damage;
private boolean cancelled;
@ApiStatus.Internal
public VehicleDamageEvent(@NotNull final Vehicle vehicle, @Nullable final Entity attacker, final double damage) {
public VehicleDamageEvent(final @NotNull Vehicle vehicle, final @NotNull DamageSource damageSource, final @Nullable Entity attacker, final double damage) {
super(vehicle);
this.damageSource = damageSource;
this.attacker = attacker;
this.damage = damage;
}
/**
* Gets the DamageSource that caused the damage.
*
* @return the DamageSource that caused the damage
*/
@NotNull
public DamageSource getDamageSource() {
return this.damageSource;
}
/**
* Gets the Entity that is attacking the vehicle
*
@@ -1,5 +1,6 @@
package org.bukkit.event.vehicle;
import org.bukkit.damage.DamageSource;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Vehicle;
import org.bukkit.event.Cancellable;
@@ -17,15 +18,27 @@ public class VehicleDestroyEvent extends VehicleEvent implements Cancellable {
private static final HandlerList HANDLER_LIST = new HandlerList();
private final DamageSource damageSource;
private final Entity attacker;
private boolean cancelled;
@ApiStatus.Internal
public VehicleDestroyEvent(@NotNull final Vehicle vehicle, @Nullable final Entity attacker) {
public VehicleDestroyEvent(final @NotNull Vehicle vehicle, final @NotNull DamageSource damageSource, final @Nullable Entity attacker) {
super(vehicle);
this.damageSource = damageSource;
this.attacker = attacker;
}
/**
* Gets the DamageSource that has destroyed the vehicle.
*
* @return the DamageSource that has destroyed the vehicle
*/
@NotNull
public DamageSource getDamageSource() {
return this.damageSource;
}
/**
* Gets the Entity that has destroyed the vehicle, potentially null
*
@@ -1,50 +1,39 @@
package org.bukkit.event.world;
import io.papermc.paper.world.WorldClock;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jspecify.annotations.NullMarked;
/**
* Called when the time skips for a world clock.
* <p>
* If the event is cancelled the time will not change.
*/
// TODO - snapshot - 26.1 clock
@ApiStatus.Experimental
@NullMarked
public class ClockTimeSkipEvent extends Event implements Cancellable {
private static final HandlerList HANDLER_LIST = new HandlerList();
private final WorldClock clock;
private final SkipReason skipReason;
private long skipAmount;
private boolean cancelled;
@ApiStatus.Internal
public ClockTimeSkipEvent(@NotNull WorldClock clock, @NotNull SkipReason skipReason, long skipAmount) {
this.clock = clock;
public ClockTimeSkipEvent(final SkipReason skipReason, final long skipAmount) {
this.skipReason = skipReason;
this.skipAmount = skipAmount;
}
/**
* Gets the clock whose time is changing.
*
* @return the clock, or null if the change does not target a specific clock
*/
@NotNull
public WorldClock getClock() {
return this.clock;
}
/**
* Gets the reason why the time has skipped.
*
* @return a SkipReason value detailing why the time has skipped
*/
@NotNull
public SkipReason getSkipReason() {
return this.skipReason;
}
@@ -77,13 +66,11 @@ public class ClockTimeSkipEvent extends Event implements Cancellable {
this.cancelled = cancel;
}
@NotNull
@Override
public HandlerList getHandlers() {
return HANDLER_LIST;
}
@NotNull
public static HandlerList getHandlerList() {
return HANDLER_LIST;
}
@@ -1,6 +1,5 @@
package org.bukkit.event.world;
import io.papermc.paper.world.WorldClock;
import org.bukkit.World;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.ApiStatus;
@@ -10,6 +9,8 @@ import org.jetbrains.annotations.NotNull;
* Called when the time skips in a world.
* <p>
* If the event is cancelled the time will not change.
*
* @see ClockTimeSkipEvent for changing of clocks that affect all worlds
*/
public class TimeSkipEvent extends ClockTimeSkipEvent {
@@ -18,8 +19,8 @@ public class TimeSkipEvent extends ClockTimeSkipEvent {
private final World world;
@ApiStatus.Internal
public TimeSkipEvent(@NotNull World world, @NotNull WorldClock clock, @NotNull SkipReason skipReason, long skipAmount) {
super(clock, skipReason, skipAmount);
public TimeSkipEvent(@NotNull World world, @NotNull SkipReason skipReason, long skipAmount) {
super(skipReason, skipAmount);
this.world = world;
}
@@ -1,6 +1,7 @@
package org.bukkit.inventory.meta.components;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.checkerframework.checker.index.qual.NonNegative;
import org.jetbrains.annotations.ApiStatus;
/**
@@ -14,14 +15,14 @@ public interface FoodComponent extends ConfigurationSerializable {
*
* @return nutrition value
*/
int getNutrition();
@NonNegative int getNutrition();
/**
* Sets the food restored by this item when eaten.
*
* @param nutrition new nutrition value, must be non-negative
*/
void setNutrition(int nutrition);
void setNutrition(@NonNegative int nutrition);
/**
* Gets the saturation restored by this item when eaten.
@@ -133,26 +133,74 @@ public class PotionEffect implements ConfigurationSerializable {
}
// Paper start
/**
* Returns a new potion effect with the provided type and this effect's
* duration, amplifier, ambient, particles, and icon.
*
* @param type effect type
* @return a new potion effect with the provided type
*/
@NotNull
public PotionEffect withType(@NotNull PotionEffectType type) {
return new PotionEffect(type, duration, amplifier, ambient, particles, icon);
}
/**
* Returns a new potion effect with the provided duration and this effect's
* type, amplifier, ambient, particles, and icon.
*
* @param duration measured in ticks, see {@link
* PotionEffect#getDuration()}
* @return a new potion effect with the provided duration
*/
@NotNull
public PotionEffect withDuration(int duration) {
return new PotionEffect(this.type, duration, amplifier, ambient, particles, icon);
}
/**
* Returns a new potion effect with the provided amplifier and this effect's
* type, duration, ambient, particles, and icon.
*
* @param amplifier the amplifier, see {@link PotionEffect#getAmplifier()}
* @return a new potion effect with the provided amplifier
*/
@NotNull
public PotionEffect withAmplifier(int amplifier) {
return new PotionEffect(this.type, duration, amplifier, ambient, particles, icon);
}
/**
* Returns a new potion effect with the provided ambient and this effect's
* type, duration, amplifier, particles, and icon.
*
* @param ambient the ambient status, see {@link PotionEffect#isAmbient()}
* @return a new potion effect with the provided ambient
*/
@NotNull
public PotionEffect withAmbient(boolean ambient) {
return new PotionEffect(this.type, duration, amplifier, ambient, particles, icon);
}
/**
* Returns a new potion effect with the provided particles and this effect's
* type, duration, amplifier, ambient, and icon.
*
* @param particles the particle status, see {@link PotionEffect#hasParticles()}
* @return a new potion effect with the provided particles
*/
@NotNull
public PotionEffect withParticles(boolean particles) {
return new PotionEffect(this.type, duration, amplifier, ambient, particles, icon);
}
/**
* Returns a new potion effect with the provided icon and this effect's
* type, duration, amplifier, ambient, and particles.
*
* @param icon the icon status, see {@link PotionEffect#hasIcon()}
* @return a new potion effect with the provided icon
*/
@NotNull
public PotionEffect withIcon(boolean icon) {
return new PotionEffect(this.type, duration, amplifier, ambient, particles, icon);
@@ -1,5 +1,6 @@
package org.spigotmc.event.player;
import com.google.common.base.Preconditions;
import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.Warning;
@@ -49,6 +50,8 @@ public class PlayerSpawnLocationEvent extends PlayerEvent {
* @param location the spawn location
*/
public void setSpawnLocation(@NotNull Location location) {
Preconditions.checkArgument(location != null, "Location cannot be null");
Preconditions.checkArgument(location.getWorld() != null, "World cannot be null");
this.spawnLocation = location.clone();
}
@@ -27,6 +27,7 @@ import io.papermc.paper.datacomponent.item.SwingAnimation;
import io.papermc.paper.datacomponent.item.consumable.ItemUseAnimation;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.item.MapPostProcessing;
import io.papermc.paper.entity.poi.PoiTypes;
import io.papermc.paper.world.WeatheringCopperState;
import io.papermc.typewriter.preset.EnumCloneRewriter;
import io.papermc.typewriter.preset.model.EnumValue;
@@ -231,6 +232,7 @@ public final class Rewriters {
.register("PigSoundVariant", Pig.SoundVariant.class, new RegistryFieldRewriter<>(Registries.PIG_SOUND_VARIANT, "getSoundVariant"))
.register("ZombieNautilusVariant", ZombieNautilus.Variant.class, new RegistryFieldRewriter<>(Registries.ZOMBIE_NAUTILUS_VARIANT, "getVariant"))
.register("Dialog", Dialog.class, new RegistryFieldRewriter<>(Registries.DIALOG, "getDialog"))
.register("PoiTypes", PoiTypes.class, new RegistryFieldRewriter<>(Registries.POINT_OF_INTEREST_TYPE, "get"))
.register("MemoryKey", MemoryKey.class, new MemoryKeyRewriter())
// .register("ItemType", org.bukkit.inventory.ItemType.class, new io.papermc.generator.rewriter.types.simple.ItemTypeRewriter()) - disable for now, lynx want the generic type
.register("BlockType", BlockType.class, new BlockTypeRewriter())
@@ -4,6 +4,7 @@ import io.papermc.generator.utils.ClassHelper;
import io.papermc.paper.datacomponent.DataComponentType;
import io.papermc.paper.datacomponent.DataComponentTypes;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.entity.poi.PoiTypes;
import io.papermc.paper.registry.data.BannerPatternRegistryEntry;
import io.papermc.paper.registry.data.CatTypeRegistryEntry;
import io.papermc.paper.registry.data.ChickenVariantRegistryEntry;
@@ -17,13 +18,9 @@ import io.papermc.paper.registry.data.JukeboxSongRegistryEntry;
import io.papermc.paper.registry.data.PaintingVariantRegistryEntry;
import io.papermc.paper.registry.data.PigVariantRegistryEntry;
import io.papermc.paper.registry.data.SoundEventRegistryEntry;
import io.papermc.paper.registry.data.TimelineRegistryEntry;
import io.papermc.paper.registry.data.WolfVariantRegistryEntry;
import io.papermc.paper.registry.data.WorldClockRegistryEntry;
import io.papermc.paper.registry.data.ZombieNautilusVariantRegistryEntry;
import io.papermc.paper.registry.data.dialog.DialogRegistryEntry;
import io.papermc.paper.world.Timeline;
import io.papermc.paper.world.WorldClock;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
@@ -69,7 +66,6 @@ import net.minecraft.world.item.alchemy.Potions;
import net.minecraft.world.item.enchantment.Enchantments;
import net.minecraft.world.item.equipment.trim.TrimMaterials;
import net.minecraft.world.item.equipment.trim.TrimPatterns;
import net.minecraft.world.clock.WorldClocks;
import net.minecraft.world.level.biome.Biomes;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BannerPatterns;
@@ -77,7 +73,6 @@ import net.minecraft.world.level.gamerules.GameRules;
import net.minecraft.world.level.levelgen.structure.BuiltinStructures;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.level.saveddata.maps.MapDecorationTypes;
import net.minecraft.world.timeline.Timelines;
import org.bukkit.Art;
import org.bukkit.Fluid;
import org.bukkit.GameEvent;
@@ -181,7 +176,8 @@ public final class RegistryEntries {
entry(Registries.FLUID, Fluids.class, Fluid.class),
entry(Registries.SOUND_EVENT, SoundEvents.class, Sound.class).allowDirect().apiRegistryField("SOUNDS").apiRegistryBuilder(SoundEventRegistryEntry.Builder.class, "PaperSoundEventRegistryEntry.PaperBuilder", RegistryEntry.RegistryModificationApiSupport.NONE),
entry(Registries.DATA_COMPONENT_TYPE, DataComponents.class, DataComponentType.class, "Paper").preload(DataComponentTypes.class).apiAccessName("of"),
entry(Registries.GAME_RULE, GameRules.class, GameRule.class).genericArgCount(1)/*.preload(org.bukkit.GameRules.class)*/ // only preload once the old names are removed
entry(Registries.GAME_RULE, GameRules.class, GameRule.class).genericArgCount(1)/*.preload(org.bukkit.GameRules.class)*/, // only preload once the old names are removed
entry(Registries.POINT_OF_INTEREST_TYPE, PoiTypes.class, io.papermc.paper.entity.poi.PoiType.class, "Paper").preload(io.papermc.paper.entity.poi.PoiTypes.class)
);
public static final List<RegistryEntry<?>> DATA_DRIVEN = List.of(
@@ -207,9 +203,7 @@ public final class RegistryEntries {
entry(Registries.PIG_VARIANT, PigVariants.class, Pig.Variant.class).writableApiRegistryBuilder(PigVariantRegistryEntry.Builder.class, "PaperPigVariantRegistryEntry.PaperBuilder"),
entry(Registries.PIG_SOUND_VARIANT, PigSoundVariants.class, Pig.SoundVariant.class),
entry(Registries.ZOMBIE_NAUTILUS_VARIANT, ZombieNautilusVariants.class, ZombieNautilus.Variant.class).writableApiRegistryBuilder(ZombieNautilusVariantRegistryEntry.Builder.class, "PaperZombieNautilusVariantRegistryEntry.PaperBuilder"),
entry(Registries.DIALOG, Dialogs.class, Dialog.class, "Paper").allowDirect().writableApiRegistryBuilder(DialogRegistryEntry.Builder.class, "PaperDialogRegistryEntry.PaperBuilder"),
entry(Registries.WORLD_CLOCK, WorldClocks.class, WorldClock.class).writableApiRegistryBuilder(WorldClockRegistryEntry.Builder.class, "PaperWorldClockRegistryEntry.PaperBuilder").delayed(),
entry(Registries.TIMELINE, Timelines.class, Timeline.class).writableApiRegistryBuilder(TimelineRegistryEntry.Builder.class, "PaperTimelineRegistryEntry.PaperBuilder").delayed()
entry(Registries.DIALOG, Dialogs.class, Dialog.class, "Paper").allowDirect().writableApiRegistryBuilder(DialogRegistryEntry.Builder.class, "PaperDialogRegistryEntry.PaperBuilder")
);
public static final List<RegistryEntry<?>> API_ONLY = List.of(
+3 -3
View File
@@ -14,8 +14,8 @@ plugins {
val paperMavenPublicUrl = "https://repo.papermc.io/repository/maven-public/"
dependencies {
mache("io.papermc:mache:26.1.2+build.1")
paperclip("io.papermc:paperclip:3.0.3")
mache("io.papermc:mache:26.1.2+build.3")
paperclip("io.papermc:paperclip:3.0.4")
}
paperweight {
@@ -23,7 +23,7 @@ paperweight {
gitFilePatches = false
updatingMinecraft {
// oldPaperCommit = "7e80cef5198561d0db53406127e5b8bc7af51577"
// oldPaperCommit = "711c5de2b05df39b72c44ff54e9f9381f8d153cb"
}
}
File diff suppressed because it is too large. Load diff
@@ -33491,7 +33491,7 @@ index 109d7b563ee799d998a5773c5a36e61ca3607d46..747755082639436731989d3ee4b3c30a
return structureTemplate.save(new CompoundTag());
}
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 1b064f00c33712c3d2d85ed8520f1eda02abfbf9..65a971379f35c4b1b519b93787f690d891e29523 100644
index c809ea1f2620c543d9bc80afdd64489f2fb98744..49be7709026cd75cfcb31715550387f0ba954a16 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -299,6 +299,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -33503,18 +33503,18 @@ index 1b064f00c33712c3d2d85ed8520f1eda02abfbf9..65a971379f35c4b1b519b93787f690d8
Thread thread = new ca.spottedleaf.moonrise.common.util.TickThread(() -> serverReference.get().runServer(), "Server thread");
thread.setUncaughtExceptionHandler((t, e) -> LOGGER.error("Uncaught exception in server thread", e));
diff --git a/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java b/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
index c206c74acc80c281d06a669eb668239dc61018eb..884fa9545a93008fa92863c845aef8f18d88e25a 100644
index 4346f3bace176e09d9ffb02e9af29a1281071e2a..df4f03f3a346e0ab0074757b3a077b2407135ac3 100644
--- a/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
+++ b/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
@@ -99,7 +99,7 @@ public class SimpleRegionStorage implements ca.spottedleaf.moonrise.patches.chun
}
// Spigot end
injectDatafixingContext(chunkTag, dataFixContextTag);
- chunkTag = this.dataFixType.update(this.fixerUpper, chunkTag, version, targetVersion);
+ chunkTag = ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(this.getDataConverterType(), chunkTag, version, targetVersion); // Paper - rewrite dataconverter system
// Spigot start
if (stopBelowZero) {
chunkTag.putString("Status", net.minecraft.core.registries.BuiltInRegistries.CHUNK_STATUS.getKey(net.minecraft.world.level.chunk.status.ChunkStatus.SPAWN).toString());
@@ -100,7 +100,7 @@ public class SimpleRegionStorage implements ca.spottedleaf.moonrise.patches.chun
}
// Spigot end
injectDatafixingContext(chunkTag, dataFixContextTag);
- chunkTag = this.dataFixType.update(this.fixerUpper, chunkTag, version, targetVersion);
+ chunkTag = ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(this.getDataConverterType(), chunkTag, version, targetVersion); // Paper - rewrite dataconverter system
// Spigot start
if (stopBelowZero) {
chunkTag.putString("Status", net.minecraft.core.registries.BuiltInRegistries.CHUNK_STATUS.getKey(net.minecraft.world.level.chunk.status.ChunkStatus.SPAWN).toString());
@@ -118,6 +118,20 @@ public class SimpleRegionStorage implements ca.spottedleaf.moonrise.patches.chun
}
}
@@ -33537,23 +33537,23 @@ index c206c74acc80c281d06a669eb668239dc61018eb..884fa9545a93008fa92863c845aef8f1
return this.upgradeChunkTag(chunkTag, defaultVersion, null, SharedConstants.getCurrentVersion().dataVersion().version());
}
diff --git a/net/minecraft/world/level/levelgen/structure/StructureCheck.java b/net/minecraft/world/level/levelgen/structure/StructureCheck.java
index 2ab055d5cdfb1e2e7cfb6f8a769c3d571acb2861..a45da98735045d55d3da14271c9bbb7a37f3151c 100644
index cba5c9ea5fd424e7aaeefaa8cc5ffe37f307b370..ce7856fa3b0f48099d09057bb44ce95a42367301 100644
--- a/net/minecraft/world/level/levelgen/structure/StructureCheck.java
+++ b/net/minecraft/world/level/levelgen/structure/StructureCheck.java
@@ -161,7 +161,7 @@ public class StructureCheck {
@@ -163,7 +163,7 @@ public class StructureCheck {
CompoundTag fixedChunkTag;
try {
- fixedChunkTag = DataFixTypes.CHUNK.updateToCurrentVersion(this.fixerUpper, chunkTag, version);
+ fixedChunkTag = ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.CHUNK, chunkTag, version, ca.spottedleaf.dataconverter.minecraft.util.Version.getCurrentVersion()); // Paper - replace chunk converter
} catch (Exception var12) {
LOGGER.warn("Failed to partially datafix chunk {}", pos, var12);
} catch (Exception e) {
LOGGER.warn("Failed to partially datafix chunk {}", pos, e);
return StructureCheckResult.CHUNK_LOAD_NEEDED;
diff --git a/net/minecraft/world/level/levelgen/structure/templatesystem/loader/TemplateSource.java b/net/minecraft/world/level/levelgen/structure/templatesystem/loader/TemplateSource.java
index 780a3eb08b7fb2c385c8e75e2e5f02a9d67297f4..2c7e4a6288f0c7101755de4be8f14449cdc4e01e 100644
index e61076a9ff3d9bce5c44e548158716db105db3ee..fe1500ebbfb09bef18989c891a19608ec9d4661c 100644
--- a/net/minecraft/world/level/levelgen/structure/templatesystem/loader/TemplateSource.java
+++ b/net/minecraft/world/level/levelgen/structure/templatesystem/loader/TemplateSource.java
@@ -79,7 +79,7 @@ public abstract class TemplateSource {
@@ -71,7 +71,7 @@ public abstract class TemplateSource {
public StructureTemplate readStructure(final CompoundTag tag) {
StructureTemplate structureTemplate = new StructureTemplate();
int version = NbtUtils.getDataVersion(tag, 500);
@@ -33563,7 +33563,7 @@ index 780a3eb08b7fb2c385c8e75e2e5f02a9d67297f4..2c7e4a6288f0c7101755de4be8f14449
}
}
diff --git a/net/minecraft/world/level/storage/PlayerDataStorage.java b/net/minecraft/world/level/storage/PlayerDataStorage.java
index 41cbb27861f33ad8d7523f3b2fef9a30a69693a9..c5898bfb8b4f075b08090cba2e9f980174e0a326 100644
index 8eda63e1f07f7998875713447c5f39eea3f85ca4..eddb6ebe3fa50a818e54079b73ba117be0abd195 100644
--- a/net/minecraft/world/level/storage/PlayerDataStorage.java
+++ b/net/minecraft/world/level/storage/PlayerDataStorage.java
@@ -98,7 +98,7 @@ public class PlayerDataStorage {
@@ -28,10 +28,10 @@ and then catch exceptions and close if they fire.
Part of this commit was authored by: Spottedleaf, sandtechnology
diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java
index 0bc6ab5e498f82c5b3cbdf8dddfed6c8406ad511..9cd6ec17981da310be1d35054af080d6b33beff8 100644
index 13faa13a9d3120cdeaca63d6eb3a0feb8019fb3a..d14aea970ce98edfe3dc3692e707334e7e150804 100644
--- a/net/minecraft/network/Connection.java
+++ b/net/minecraft/network/Connection.java
@@ -65,7 +65,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -64,7 +64,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;
@@ -51,7 +51,7 @@ index 0bc6ab5e498f82c5b3cbdf8dddfed6c8406ad511..9cd6ec17981da310be1d35054af080d6
public Connection(final PacketFlow receiving) {
this.receiving = receiving;
@@ -393,11 +397,38 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -392,11 +396,38 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
}
public void send(final Packet<?> packet, final @Nullable ChannelFutureListener listener, final boolean flush) {
@@ -93,7 +93,7 @@ index 0bc6ab5e498f82c5b3cbdf8dddfed6c8406ad511..9cd6ec17981da310be1d35054af080d6
}
}
@@ -406,7 +437,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -405,7 +436,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
this.flushQueue();
action.accept(this);
} else {
@@ -102,7 +102,7 @@ index 0bc6ab5e498f82c5b3cbdf8dddfed6c8406ad511..9cd6ec17981da310be1d35054af080d6
}
}
@@ -420,21 +451,41 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -419,21 +450,42 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
}
private void doSendPacket(final Packet<?> packet, final @Nullable ChannelFutureListener listener, final boolean flush) {
@@ -133,7 +133,8 @@ index 0bc6ab5e498f82c5b3cbdf8dddfed6c8406ad511..9cd6ec17981da310be1d35054af080d6
+ }
+ } catch (final Exception e) {
+ LOGGER.error("NetworkException: {}", player, e);
+ this.disconnect(Component.translatable("disconnect.genericReason", "Internal Exception: " + e.getMessage()));
+ Component reason = Component.translatable("disconnect.genericReason", "Internal Exception: " + e.getMessage());
+ this.send(new ClientboundDisconnectPacket(reason), PacketSendListener.thenRun(() -> this.disconnect(reason)));
+ packet.onPacketDispatchFinish(player, null);
}
+ // Paper end - Optimize network
@@ -148,7 +149,7 @@ index 0bc6ab5e498f82c5b3cbdf8dddfed6c8406ad511..9cd6ec17981da310be1d35054af080d6
}
}
@@ -446,16 +497,57 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -445,16 +497,57 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
}
}
@@ -211,7 +212,7 @@ index 0bc6ab5e498f82c5b3cbdf8dddfed6c8406ad511..9cd6ec17981da310be1d35054af080d6
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
@@ -525,6 +617,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -524,6 +617,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
public void disconnect(final DisconnectionDetails details) {
this.preparing = false; // Spigot
@@ -219,7 +220,7 @@ index 0bc6ab5e498f82c5b3cbdf8dddfed6c8406ad511..9cd6ec17981da310be1d35054af080d6
if (this.channel == null) {
this.delayedDisconnect = details;
}
@@ -714,7 +807,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -705,7 +799,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
public void handleDisconnection() {
if (this.channel != null && !this.channel.isOpen()) {
if (this.disconnectionHandled) {
@@ -228,7 +229,7 @@ index 0bc6ab5e498f82c5b3cbdf8dddfed6c8406ad511..9cd6ec17981da310be1d35054af080d6
} else {
this.disconnectionHandled = true;
PacketListener packetListener = this.getPacketListener();
@@ -725,7 +818,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -716,7 +810,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
);
disconnectListener.onDisconnect(details);
}
@@ -237,7 +238,7 @@ index 0bc6ab5e498f82c5b3cbdf8dddfed6c8406ad511..9cd6ec17981da310be1d35054af080d6
// Paper start - Add PlayerConnectionCloseEvent
if (packetListener instanceof net.minecraft.server.network.ServerCommonPacketListenerImpl commonPacketListener) {
/* Player was logged in, either game listener or configuration listener */
@@ -760,4 +853,96 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -751,4 +845,96 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
public void setBandwidthLogger(final LocalSampleLogger bandwidthLogger) {
this.bandwidthDebugMonitor = new BandwidthDebugMonitor(bandwidthLogger);
}
@@ -371,10 +372,10 @@ index 1480d0db90f5797e3dee19503e52d1e783493ac3..bd76e55ea56dd7c1e232806d1d13a747
+ // Paper end
}
diff --git a/net/minecraft/server/network/ServerConnectionListener.java b/net/minecraft/server/network/ServerConnectionListener.java
index b0445b4ad9adb6d2dec08537559d2ed243ca69bb..b266e4f7b437578356568d86910e3b428ee66523 100644
index 77e1c02b74c1f6597fbb5a457bf463fe52de019e..6d506004583b2415ab356e34e2f8d6ac0e39cca2 100644
--- a/net/minecraft/server/network/ServerConnectionListener.java
+++ b/net/minecraft/server/network/ServerConnectionListener.java
@@ -52,11 +52,13 @@ public class ServerConnectionListener {
@@ -51,11 +51,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<>();
@@ -388,7 +389,7 @@ index b0445b4ad9adb6d2dec08537559d2ed243ca69bb..b266e4f7b437578356568d86910e3b42
}
}
// Paper end - prevent blocking on adding a new connection while the server is ticking
@@ -92,6 +94,7 @@ public class ServerConnectionListener {
@@ -87,6 +89,7 @@ public class ServerConnectionListener {
} catch (ChannelException var5) {
}
@@ -31,18 +31,18 @@ this fix, as the data will remain in the oversized file. Once the server returns
to a jar with this fix, the data will be restored.
diff --git a/net/minecraft/world/level/chunk/storage/RegionFile.java b/net/minecraft/world/level/chunk/storage/RegionFile.java
index 5149635679ec03d881e63effbd79fc193d7e52c8..d628e06a8dbf11b491b323c10a512fde22e72a12 100644
index 026b1f31d5041786e90ba372a228732aee094c16..d39f8ae8f5736a7f4590ca41ac95792a61975baf 100644
--- a/net/minecraft/world/level/chunk/storage/RegionFile.java
+++ b/net/minecraft/world/level/chunk/storage/RegionFile.java
@@ -69,6 +69,7 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
@@ -68,6 +68,7 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
this.info = info;
this.path = path;
this.version = version;
+ this.initOversizedState(); // Paper
if (!Files.isDirectory(externalFileDir)) {
throw new IllegalArgumentException("Expected directory, got " + externalFileDir.toAbsolutePath());
} else {
@@ -465,4 +466,75 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
}
@@ -463,4 +464,75 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
private interface CommitOp {
void run() throws IOException;
}
@@ -54,7 +54,7 @@ index 5149635679ec03d881e63effbd79fc193d7e52c8..d628e06a8dbf11b491b323c10a512fde
+ private synchronized void initOversizedState() throws IOException {
+ Path metaFile = getOversizedMetaFile();
+ if (Files.exists(metaFile)) {
+ final byte[] read = java.nio.file.Files.readAllBytes(metaFile);
+ final byte[] read = Files.readAllBytes(metaFile);
+ System.arraycopy(read, 0, oversized, 0, oversized.length);
+ for (byte temp : oversized) {
+ oversizedCount += temp;
@@ -98,7 +98,7 @@ index 5149635679ec03d881e63effbd79fc193d7e52c8..d628e06a8dbf11b491b323c10a512fde
+ }
+
+ private void writeOversizedMeta() throws IOException {
+ java.nio.file.Files.write(getOversizedMetaFile(), oversized);
+ Files.write(this.getOversizedMetaFile(), this.oversized);
+ }
+
+ private Path getOversizedMetaFile() {
@@ -119,16 +119,24 @@ index 5149635679ec03d881e63effbd79fc193d7e52c8..d628e06a8dbf11b491b323c10a512fde
+ // Paper end
}
diff --git a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
index e9316cc08fe4cb4af28583a8cb1ec658243bffd0..aa36e241d49725eb13f00e57d020d4da24add53d 100644
index 36f8c19e3e90740e6d58eb8dbf573b2fdf4fed3d..3d0a09662e5ed1f7bc0c02dbe6de529791f2f66f 100644
--- a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+++ b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
@@ -241,6 +241,43 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
@@ -16,6 +16,7 @@ import net.minecraft.world.level.ChunkPos;
import org.jspecify.annotations.Nullable;
public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise.patches.chunk_system.io.ChunkSystemRegionFileStorage { // Paper - rewrite chunk system
+ private static final org.slf4j.Logger LOGGER = com.mojang.logging.LogUtils.getLogger(); // Paper
public static final String ANVIL_EXTENSION = ".mca";
private static final int MAX_CACHE_SIZE = 256;
public final Long2ObjectLinkedOpenHashMap<RegionFile> regionCache = new Long2ObjectLinkedOpenHashMap<>();
@@ -241,6 +242,43 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
// Paper end - rewrite chunk system
}
+ // Paper start
+ private static void printOversizedLog(String msg, Path file, int x, int z) {
+ org.apache.logging.log4j.LogManager.getLogger().fatal(msg + " (" + file.toString().replaceAll(".+[\\\\/]", "") + " - " + x + "," + z + ") Go clean it up to remove this message. /minecraft:tp " + (x<<4)+" 128 "+(z<<4) + " - DO NOT REPORT THIS TO PAPER - You may ask for help on Discord, but do not file an issue. These error messages can not be removed.");
+ LOGGER.error("{} ({} - {},{}) Go clean it up to remove this message. /minecraft:tp {} 128 {} - DO NOT REPORT THIS TO PAPER - You may ask for help on Discord, but do not file an issue. These error messages can not be removed.", msg, file.toString().replaceAll(".+[\\\\/]", ""), x, z, x << 4, z << 4);
+ }
+
+ private static CompoundTag readOversizedChunk(RegionFile regionfile, ChunkPos chunkCoordinate) throws IOException {
@@ -145,9 +153,9 @@ index e9316cc08fe4cb4af28583a8cb1ec658243bffd0..aa36e241d49725eb13f00e57d020d4da
+ mergeChunkList(chunk.getCompoundOrEmpty("Level"), oversizedLevel, "TileEntities", "TileEntities");
+
+ return chunk;
+ } catch (Throwable throwable) {
+ throwable.printStackTrace();
+ throw throwable;
+ } catch (Throwable t) {
+ t.printStackTrace();
+ throw t;
+ }
+ }
+ }
@@ -166,7 +174,7 @@ index e9316cc08fe4cb4af28583a8cb1ec658243bffd0..aa36e241d49725eb13f00e57d020d4da
public @Nullable CompoundTag read(final ChunkPos pos) throws IOException {
// CraftBukkit start - SPIGOT-5680: There's no good reason to preemptively create files on read, save that for writing
RegionFile region = this.getRegionFile(pos, true);
@@ -248,6 +285,12 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
@@ -248,6 +286,12 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
return null;
}
// CraftBukkit end
@@ -177,9 +185,9 @@ index e9316cc08fe4cb4af28583a8cb1ec658243bffd0..aa36e241d49725eb13f00e57d020d4da
+ }
+ // Paper end
CompoundTag var4;
try (DataInputStream regionChunkInputStream = region.getChunkDataInputStream(pos)) {
@@ -290,6 +333,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
return regionChunkInputStream == null ? null : NbtIo.read(regionChunkInputStream);
@@ -283,6 +327,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
} else {
try (DataOutputStream output = region.getChunkDataOutputStream(pos)) {
NbtIo.write(value, output);
@@ -354,10 +354,10 @@ index 0000000000000000000000000000000000000000..ce6b57eeeeb1bd652f4bb53c19dcfbc0
+ }
+}
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index ed52a5e099b74b71ae05f21a4ad0d01dee9c2a60..9f43d29aa4fecb03d6ef49e71e4de846dca187de 100644
index 89da20b0682708ba8bfc0d10381eb9784d80c395..b5d63d7b848c11ed322d512adeee102886cbb6fd 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -865,6 +865,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -864,6 +864,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
profiler.pop();
}
@@ -365,7 +365,7 @@ index ed52a5e099b74b71ae05f21a4ad0d01dee9c2a60..9f43d29aa4fecb03d6ef49e71e4de846
this.entityTickList
.forEach(
entity -> {
@@ -1381,12 +1382,15 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -1383,12 +1384,15 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
entity.totalEntityAge++; // Paper - age-like counter for all entities
profiler.push(entity.typeHolder()::getRegisteredName);
profiler.incrementCounter("tickNonPassenger");
@@ -382,7 +382,7 @@ index ed52a5e099b74b71ae05f21a4ad0d01dee9c2a60..9f43d29aa4fecb03d6ef49e71e4de846
}
// Paper start - log detailed entity tick information
} finally {
@@ -1397,7 +1401,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -1399,7 +1403,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
// Paper end - log detailed entity tick information
}
@@ -391,7 +391,7 @@ index ed52a5e099b74b71ae05f21a4ad0d01dee9c2a60..9f43d29aa4fecb03d6ef49e71e4de846
if (entity.isRemoved() || entity.getVehicle() != vehicle) {
entity.stopRiding();
} else if (entity instanceof Player || this.entityTickList.contains(entity)) {
@@ -1407,12 +1411,21 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -1409,12 +1413,21 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
ProfilerFiller profiler = Profiler.get();
profiler.push(entity.typeHolder()::getRegisteredName);
profiler.incrementCounter("tickPassenger");
@@ -462,7 +462,7 @@ index 0df8332933203a904bd9ef9efb3c9bce21e65441..1a502cbd8acea9420fa6dd8d716018b5
public void tick() {
super.tick();
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index f7b4da570ee28b7128b4d2cd1c67b89e3980bced..93d3fb313d538cc1067b1db010458169a1320122 100644
index 593265d78564b60bacbb4899f18a6e74bf56601d..84c664711658eb83b5ff9d4c8470fd8ec54a5473 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -382,6 +382,15 @@ public abstract class Entity
@@ -521,10 +521,10 @@ index f7b4da570ee28b7128b4d2cd1c67b89e3980bced..93d3fb313d538cc1067b1db010458169
delta = this.maybeBackOffFromEdge(delta, moverType);
Vec3 movement = this.collide(delta);
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
index c4da41f3b5ba6d8492d3776654b1ce62a5015895..6a0ebbdfcc0bc07915574c1cf87463e82213ca7e 100644
index f62e535b62d249dd3be19d7c1b02fae8dad31c10..6eb95b979ffe37d78fd68eb57ebe71891beb0d28 100644
--- a/net/minecraft/world/entity/LivingEntity.java
+++ b/net/minecraft/world/entity/LivingEntity.java
@@ -3385,6 +3385,14 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
@@ -3380,6 +3380,14 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
protected void playAttackSound() {
}
@@ -540,10 +540,10 @@ index c4da41f3b5ba6d8492d3776654b1ce62a5015895..6a0ebbdfcc0bc07915574c1cf87463e8
public void tick() {
super.tick();
diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java
index c7c53ef16e104ebc48aeb8719783fb4c45d39fc2..90eaa775dc134041647618c3b965334a9bb9fd3b 100644
index e63ac861b785cf3dccac25404454289c44b7ec9d..8b036640be655c65852e0862e30962e443d3024d 100644
--- a/net/minecraft/world/entity/Mob.java
+++ b/net/minecraft/world/entity/Mob.java
@@ -211,6 +211,19 @@ public abstract class Mob extends LivingEntity implements Targeting, EquipmentUs
@@ -211,6 +211,20 @@ public abstract class Mob extends LivingEntity implements Targeting, EquipmentUs
return this.lookControl;
}
@@ -551,6 +551,7 @@ index c7c53ef16e104ebc48aeb8719783fb4c45d39fc2..90eaa775dc134041647618c3b965334a
+ @Override
+ public void inactiveTick() {
+ super.inactiveTick();
+ if (!this.aware) return; // Paper - Do not tick AI for inactive unaware mobs
+ if (this.goalSelector.inactiveTick()) {
+ this.goalSelector.tick();
+ }
@@ -564,7 +565,7 @@ index c7c53ef16e104ebc48aeb8719783fb4c45d39fc2..90eaa775dc134041647618c3b965334a
return this.getControlledVehicle() instanceof Mob riding ? riding.getMoveControl() : this.moveControl;
}
diff --git a/net/minecraft/world/entity/PathfinderMob.java b/net/minecraft/world/entity/PathfinderMob.java
index c4b4a135f61955c2b87801772a2b31eeeb77bf45..9d12cc2d169f243ba23485e2782bca7064282a36 100644
index a19d8d4c0075340e8d187d5f8b6ce989567d3163..c661311dfdc1e21e3d6ec65e1e8b953637149675 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 {
@@ -577,7 +578,7 @@ index c4b4a135f61955c2b87801772a2b31eeeb77bf45..9d12cc2d169f243ba23485e2782bca70
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 6bc2d38fb990628784deb5ee615e34a0fc320a47..844ced7a271ffb557f3c8f2fcd6a4c6ad499aeae 100644
index c918154444a05917a3622947d0ddbc29bdb9bead..b13bdc2852504903fa054c42f89299a5d33dfbd3 100644
--- a/net/minecraft/world/entity/ai/goal/GoalSelector.java
+++ b/net/minecraft/world/entity/ai/goal/GoalSelector.java
@@ -24,6 +24,7 @@ public class GoalSelector {
@@ -639,7 +640,7 @@ index d46e0e3a4a7fc3bb3f1dd695d4cc563778b99db0..c6e9b155c2341c13d569f28f54f0c1d2
}
}
diff --git a/net/minecraft/world/entity/item/ItemEntity.java b/net/minecraft/world/entity/item/ItemEntity.java
index c482c3348267ca2b88109e34291aa860e0cde916..b11b59d80e37d272770ab51a63028fdddfe800d6 100644
index ca0b7ce4ab535d7112b315a7de1dc5130797ea2d..974d1d7b283aace71f0ea8ef07ba68a17b75af8a 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 {
@@ -673,7 +674,7 @@ index c482c3348267ca2b88109e34291aa860e0cde916..b11b59d80e37d272770ab51a63028fdd
public void tick() {
if (this.getItem().isEmpty()) {
diff --git a/net/minecraft/world/entity/npc/villager/Villager.java b/net/minecraft/world/entity/npc/villager/Villager.java
index a80e6a23cf29cb44943339101f15565dbc19af1f..f481fd661440f77c98940cbde4a8b95818e4a22c 100644
index 084a4a945d4fc6a76878da00e5015c79fc88b47f..e4ba6e200e85f4e226d6a3e68db734f277b89226 100644
--- a/net/minecraft/world/entity/npc/villager/Villager.java
+++ b/net/minecraft/world/entity/npc/villager/Villager.java
@@ -244,11 +244,35 @@ public class Villager extends AbstractVillager implements VillagerDataHolder, Re
@@ -687,7 +688,7 @@ index a80e6a23cf29cb44943339101f15565dbc19af1f..f481fd661440f77c98940cbde4a8b958
+ if (this.getUnhappyCounter() > 0) {
+ this.setUnhappyCounter(this.getUnhappyCounter() - 1);
+ }
+ if (this.isEffectiveAi()) {
+ if (this.aware && this.isEffectiveAi()) { // Paper - Do not tick AI for inactive unaware mobs
+ if (this.level().spigotConfig.tickInactiveVillagers) {
+ this.customServerAiStep(this.level().getMinecraftWorld());
+ } else {
@@ -778,7 +779,7 @@ index f3861c936855a0d9c9170585f4da4ce1cd77b854..3b4cbbc4c8ca1cd3110d1d6ba9e35312
public void tick() {
super.tick();
diff --git a/net/minecraft/world/entity/vehicle/minecart/MinecartHopper.java b/net/minecraft/world/entity/vehicle/minecart/MinecartHopper.java
index 3a590d4dc980a2912e9cc043d8c8db4cf9d60803..06ab7c48b18c03af494ab10fc2b584cefc776749 100644
index f46cca0467bb4da5511bc953ce5b43fa606bf978..445c5cafad71028171c1f261939661775dd7f2bd 100644
--- a/net/minecraft/world/entity/vehicle/minecart/MinecartHopper.java
+++ b/net/minecraft/world/entity/vehicle/minecart/MinecartHopper.java
@@ -50,6 +50,7 @@ public class MinecartHopper extends AbstractMinecartContainer implements Hopper
@@ -789,20 +790,21 @@ index 3a590d4dc980a2912e9cc043d8c8db4cf9d60803..06ab7c48b18c03af494ab10fc2b584ce
}
public boolean isEnabled() {
@@ -103,11 +104,13 @@ public class MinecartHopper extends AbstractMinecartContainer implements Hopper
@@ -103,12 +104,14 @@ public class MinecartHopper extends AbstractMinecartContainer implements Hopper
public boolean suckInItems() {
if (HopperBlockEntity.suckInItems(this.level(), this)) {
+ this.immunize(); // Paper
return true;
} else {
for (ItemEntity entity : this.level()
.getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate(0.25, 0.0, 0.25), EntitySelector.ENTITY_STILL_ALIVE)) {
if (HopperBlockEntity.addItem(this, entity)) {
+ this.immunize(); // Paper
return true;
}
}
for (ItemEntity entity : this.level()
.getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate(0.25, 0.0, 0.25), EntitySelector.ENTITY_STILL_ALIVE)) {
if (HopperBlockEntity.addItem(this, entity)) {
+ this.immunize(); // Paper
return true;
}
}
@@ -142,4 +145,11 @@ public class MinecartHopper extends AbstractMinecartContainer implements Hopper
public AbstractContainerMenu createMenu(final int containerId, final Inventory inventory) {
return new HopperMenu(containerId, inventory, this);
@@ -816,7 +818,7 @@ index 3a590d4dc980a2912e9cc043d8c8db4cf9d60803..06ab7c48b18c03af494ab10fc2b584ce
+
}
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index 995b51d859316c6128b30d930e58dd47224514b2..329aef0e55664fbdd98c84d4d7cee74cd633e9c3 100644
index cc492445307ddc08446484d272866f01646cf7b3..a5d46832d4f8a13b0bf67b523c21d5c3b44209a9 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -155,6 +155,12 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
@@ -833,7 +835,7 @@ index 995b51d859316c6128b30d930e58dd47224514b2..329aef0e55664fbdd98c84d4d7cee74c
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 ec01093094f1d4de2708791ae6a5545268a021ae..f904c587fafe7395473450f766354d874e36ebac 100644
index ef78f286ca14512c3dfccdb8fb06ebe9affa1589..c5c0b01c41055a82e49c1546d4525da0acce13a4 100644
--- a/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
+++ b/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
@@ -159,6 +159,10 @@ public class PistonMovingBlockEntity extends BlockEntity {
@@ -30,7 +30,7 @@ Additionally, this lets us avoid even allocating new objects for this too, furth
reducing memory usage.
diff --git a/net/minecraft/world/phys/shapes/IndirectMerger.java b/net/minecraft/world/phys/shapes/IndirectMerger.java
index 7e525297aaca8c18cf1669ab80040475029ff652..b342e4c0260fc5a64e5943e6342de0703233a99b 100644
index 6c637af546a411d5790ceba9936aa9128d05d97e..e18f24a3941e1102648bac841851a8749f2d9438 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 {
@@ -67,7 +67,7 @@ index 7e525297aaca8c18cf1669ab80040475029ff652..b342e4c0260fc5a64e5943e6342de070
this.firstIndices = new int[capacity];
this.secondIndices = new int[capacity];
diff --git a/net/minecraft/world/phys/shapes/Shapes.java b/net/minecraft/world/phys/shapes/Shapes.java
index de10ce3faa86d6ed6d528575a083722a8feedf8d..7574443bc098c1a0958b64f1f54e76322e48a2e7 100644
index ccdaf6043f667b6983e4492cf570947ed5179df1..5c552f618d09fa9456195bbfa643ce4a1a03bd84 100644
--- a/net/minecraft/world/phys/shapes/Shapes.java
+++ b/net/minecraft/world/phys/shapes/Shapes.java
@@ -343,11 +343,26 @@ public final class Shapes {
@@ -117,9 +117,9 @@ index de10ce3faa86d6ed6d528575a083722a8feedf8d..7574443bc098c1a0958b64f1f54e7632
} else if (second.getDouble(secondSize) < first.getDouble(0) - 1.0E-7) {
return new NonOverlappingMerger(second, first, true);
} else {
- return (IndexMerger)(firstSize == secondSize && Objects.equals(first, second)
- return firstSize == secondSize && Objects.equals(first, second)
- ? new IdenticalMerger(first)
- : new IndirectMerger(first, second, firstOnlyMatters, secondOnlyMatters));
- : new IndirectMerger(first, second, firstOnlyMatters, secondOnlyMatters);
+ return new IndirectMerger(first, second, firstOnlyMatters, secondOnlyMatters); // Paper - Identical happens more often than Disjoint
}
}
@@ -5,7 +5,7 @@ Subject: [PATCH] optimize dirt and snow spreading
diff --git a/net/minecraft/world/level/block/SpreadingSnowyBlock.java b/net/minecraft/world/level/block/SpreadingSnowyBlock.java
index 6695d7f76daf45ed3e73c4984326b8b59e5a76cc..f868b1ccfa0e373c47344d000f26fbe8507c3510 100644
index 85666946f8cb23255360bca8efde2ce13abcbc0a..839bfdda7f54e6aabe401db6da1856544b032bf8 100644
--- a/net/minecraft/world/level/block/SpreadingSnowyBlock.java
+++ b/net/minecraft/world/level/block/SpreadingSnowyBlock.java
@@ -27,8 +27,13 @@ public abstract class SpreadingSnowyBlock extends SnowyBlock {
@@ -22,8 +22,8 @@ index 6695d7f76daf45ed3e73c4984326b8b59e5a76cc..f868b1ccfa0e373c47344d000f26fbe8
+ BlockState aboveState = chunk.getBlockState(above); // Paper - Perf: optimize dirt and snow spreading
if (aboveState.is(Blocks.SNOW) && aboveState.getValue(SnowLayerBlock.LAYERS) == 1) {
return true;
} else if (aboveState.getFluidState().isFull()) {
@@ -40,8 +45,14 @@ public abstract class SpreadingSnowyBlock extends SnowyBlock {
}
@@ -42,8 +47,14 @@ public abstract class SpreadingSnowyBlock extends SnowyBlock {
}
private static boolean canPropagate(final BlockState state, final LevelReader level, final BlockPos pos) {
@@ -39,7 +39,7 @@ index 6695d7f76daf45ed3e73c4984326b8b59e5a76cc..f868b1ccfa0e373c47344d000f26fbe8
}
@Override
@@ -50,7 +61,14 @@ public abstract class SpreadingSnowyBlock extends SnowyBlock {
@@ -52,7 +63,14 @@ public abstract class SpreadingSnowyBlock extends SnowyBlock {
Registry<Block> blocks = level.registryAccess().lookupOrThrow(Registries.BLOCK);
Optional<Block> baseBlock = blocks.getOptional(this.baseBlock);
if (!baseBlock.isEmpty()) {
@@ -55,7 +55,7 @@ index 6695d7f76daf45ed3e73c4984326b8b59e5a76cc..f868b1ccfa0e373c47344d000f26fbe8
// CraftBukkit start
if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(level, pos, baseBlock.get().defaultBlockState()).isCancelled()) {
return;
@@ -63,8 +81,20 @@ public abstract class SpreadingSnowyBlock extends SnowyBlock {
@@ -65,8 +83,20 @@ public abstract class SpreadingSnowyBlock extends SnowyBlock {
for (int i = 0; i < 4; i++) {
BlockPos testPos = pos.offset(random.nextInt(3) - 1, random.nextInt(5) - 3, random.nextInt(3) - 1);
@@ -86,7 +86,7 @@ index 0003a548fe4a48ecfe3534f3de309a920568b773..fae764ffd6858c920cafd2fba4444793
+ // Paper end - Use Velocity cipher
}
diff --git a/net/minecraft/network/CompressionDecoder.java b/net/minecraft/network/CompressionDecoder.java
index 2f660186f00414b37fa5c01d00c048173feaef13..fe3f0b59561e7b7236aba9cca2a13cee8955a933 100644
index 8497561d90bf6165d1e56a044b3b409c234e1ef8..ddd5daf92d03dd343e6164d55fff638509175e6d 100644
--- a/net/minecraft/network/CompressionDecoder.java
+++ b/net/minecraft/network/CompressionDecoder.java
@@ -12,14 +12,22 @@ import java.util.zip.Inflater;
@@ -169,7 +169,7 @@ index 2f660186f00414b37fa5c01d00c048173feaef13..fe3f0b59561e7b7236aba9cca2a13cee
this.validateDecompressed = validateDecompressed;
}
diff --git a/net/minecraft/network/CompressionEncoder.java b/net/minecraft/network/CompressionEncoder.java
index 319325e741cc314b84844bb49ea569aaf014f319..20b5cfe1ce36d74cd6b2f95566a1bc34fc0aaa7f 100644
index 595db570b0ade9cad7de77d2ab7b10d9e68699df..ac095f59d9f17d8239d6493d1c1e3942139b039f 100644
--- a/net/minecraft/network/CompressionEncoder.java
+++ b/net/minecraft/network/CompressionEncoder.java
@@ -6,17 +6,30 @@ import io.netty.handler.codec.MessageToByteEncoder;
@@ -207,33 +207,34 @@ index 319325e741cc314b84844bb49ea569aaf014f319..20b5cfe1ce36d74cd6b2f95566a1bc34
int uncompressedLength = uncompressed.readableBytes();
if (uncompressedLength > 8388608) {
throw new IllegalArgumentException("Packet too big (is " + uncompressedLength + ", should be less than 8388608)");
@@ -25,6 +38,7 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
VarInt.write(out, 0);
out.writeBytes(uncompressed);
} else {
+ if (this.deflater != null) { // Paper - Use Velocity cipher
byte[] input = new byte[uncompressedLength];
uncompressed.readBytes(input);
VarInt.write(out, input.length);
@@ -37,6 +51,17 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
}
this.deflater.reset();
+ // Paper start - Use Velocity cipher
+ return;
+ }
+
+ VarInt.write(out, uncompressedLength);
+ final ByteBuf compatibleIn = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(ctx.alloc(), this.compressor, uncompressed);
+ try {
+ this.compressor.deflate(compatibleIn, out);
+ } finally {
+ compatibleIn.release();
+ }
@@ -26,6 +39,7 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
VarInt.write(out, 0);
out.writeBytes(uncompressed);
} else {
+ if (this.deflater != null) { // Paper - Use Velocity cipher
byte[] input = new byte[uncompressedLength];
uncompressed.readBytes(input);
VarInt.write(out, input.length);
@@ -38,6 +52,18 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
}
this.deflater.reset();
+ // Paper start - Use Velocity cipher
+ return;
+ }
+
+ VarInt.write(out, uncompressedLength);
+ final ByteBuf compatibleIn = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(ctx.alloc(), this.compressor, uncompressed);
+ try {
+ this.compressor.deflate(compatibleIn, out);
+ } finally {
+ compatibleIn.release();
+ }
+ // Paper end - Use Velocity cipher
}
}
@@ -48,4 +73,31 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
@@ -48,4 +74,31 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
public void setThreshold(final int threshold) {
this.threshold = threshold;
}
@@ -266,10 +267,10 @@ index 319325e741cc314b84844bb49ea569aaf014f319..20b5cfe1ce36d74cd6b2f95566a1bc34
+ // Paper end - Use Velocity cipher
}
diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java
index 9cd6ec17981da310be1d35054af080d6b33beff8..d7ed1621709c491525daaf7adc8e9f0d781c93e1 100644
index d14aea970ce98edfe3dc3692e707334e7e150804..0f06d509125e72ec11d263d0d5c2179d65f6d8dd 100644
--- a/net/minecraft/network/Connection.java
+++ b/net/minecraft/network/Connection.java
@@ -738,11 +738,22 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -730,11 +730,22 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
return connection;
}
@@ -296,7 +297,7 @@ index 9cd6ec17981da310be1d35054af080d6b33beff8..d7ed1621709c491525daaf7adc8e9f0d
public boolean isEncrypted() {
return this.encrypted;
@@ -780,16 +791,17 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -772,16 +783,17 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
public void setupCompression(final int threshold, final boolean validateDecompressed) {
if (threshold >= 0) {
@@ -318,10 +319,10 @@ index 9cd6ec17981da310be1d35054af080d6b33beff8..d7ed1621709c491525daaf7adc8e9f0d
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 b266e4f7b437578356568d86910e3b428ee66523..0b57e3bcd0319e4e9571fddbcb85db5a3105b8b2 100644
index 6d506004583b2415ab356e34e2f8d6ac0e39cca2..474b7f06223514d95bad3d91356dfaa5435b77f5 100644
--- a/net/minecraft/server/network/ServerConnectionListener.java
+++ b/net/minecraft/server/network/ServerConnectionListener.java
@@ -77,6 +77,10 @@ public class ServerConnectionListener {
@@ -76,6 +76,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.
@@ -333,10 +334,10 @@ index b266e4f7b437578356568d86910e3b428ee66523..0b57e3bcd0319e4e9571fddbcb85db5a
.add(
new ServerBootstrap()
diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
index 8203765d5570644b4c8f6790f361bca2c7066b25..718f00ad9c587a4b0c12f351c10d462113f16ac3 100644
index 50e4f4037702875fe7755b169947abd800b06e7b..722c832ce18fd930d9fdb4f718dc13bfc962e5dd 100644
--- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
@@ -245,11 +245,9 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
@@ -251,11 +251,9 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
}
SecretKey secretKey = packet.getSecretKey(serverPrivateKey);
@@ -346,6 +347,6 @@ index 8203765d5570644b4c8f6790f361bca2c7066b25..718f00ad9c587a4b0c12f351c10d4621
this.state = ServerLoginPacketListenerImpl.State.AUTHENTICATING;
- this.connection.setEncryptionKey(decryptCipher, encryptCipher);
+ this.connection.setEncryptionKey(secretKey); // Paper - Use Velocity cipher
} catch (CryptException var7) {
throw new IllegalStateException("Protocol error", var7);
} catch (CryptException e) {
throw new IllegalStateException("Protocol error", e);
}
@@ -7,7 +7,7 @@ Optimise the stream.anyMatch statement to move to a bitset
where we can replace the call with a single bitwise operation.
diff --git a/net/minecraft/world/entity/ai/goal/Goal.java b/net/minecraft/world/entity/ai/goal/Goal.java
index 5746eefd9e8fa851e74ccf6b5d889a9f8e6fc1e0..81fa892212f586e8cd7938556eeaa9f9dc2654aa 100644
index aa5f0e00daf26650b943a24676638581e494a112..60e0d618c603caef5e3516c9fdfc63ff45a7cdff 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;
@@ -69,7 +69,7 @@ index 5746eefd9e8fa851e74ccf6b5d889a9f8e6fc1e0..81fa892212f586e8cd7938556eeaa9f9
// 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 844ced7a271ffb557f3c8f2fcd6a4c6ad499aeae..75d8953845b2e9acb62b19a859d0e39fd63533eb 100644
index b13bdc2852504903fa054c42f89299a5d33dfbd3..1ddf8796d14d472bec48546f25ac4583d483c8e9 100644
--- a/net/minecraft/world/entity/ai/goal/GoalSelector.java
+++ b/net/minecraft/world/entity/ai/goal/GoalSelector.java
@@ -23,7 +23,8 @@ public class GoalSelector {
@@ -119,27 +119,24 @@ index 844ced7a271ffb557f3c8f2fcd6a4c6ad499aeae..75d8953845b2e9acb62b19a859d0e39f
goal.stop();
}
}
@@ -95,11 +96,14 @@ public class GoalSelector {
@@ -95,8 +96,14 @@ public class GoalSelector {
profiler.push("goalUpdate");
for (WrappedGoal goalx : this.availableGoals) {
- if (!goalx.isRunning()
- && !goalContainsAnyFlags(goalx, this.disabledFlags)
- && goalCanBeReplacedForAllFlags(goalx, this.lockedFlags)
- && goalx.canUse()) {
- for (Goal.Flag flag : goalx.getFlags()) {
for (WrappedGoal goal : this.availableGoals) {
- if (!goal.isRunning() && !goalContainsAnyFlags(goal, this.disabledFlags) && goalCanBeReplacedForAllFlags(goal, this.lockedFlags) && goal.canUse()) {
- for (Goal.Flag flag : goal.getFlags()) {
+ // Paper start
+ if (!goalx.isRunning() && !goalContainsAnyFlags(goalx, this.goalTypes) && goalCanBeReplacedForAllFlags(goalx, this.lockedFlags) && goalx.canUse()) {
+ long flagIterator = goalx.getFlags().getBackingSet();
+ int wrappedGoalSize = goalx.getFlags().size();
+ if (!goal.isRunning() && !goalContainsAnyFlags(goal, this.goalTypes) && goalCanBeReplacedForAllFlags(goal, this.lockedFlags) && goal.canUse()) {
+ 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
WrappedGoal currentGoal = this.lockedFlags.getOrDefault(flag, NO_GOAL);
currentGoal.stop();
this.lockedFlags.put(flag, goalx);
@@ -131,11 +135,11 @@ public class GoalSelector {
this.lockedFlags.put(flag, goal);
@@ -128,11 +135,11 @@ public class GoalSelector {
}
public void disableControlFlag(final Goal.Flag flag) {
@@ -9,7 +9,7 @@ creating too large of a packet to sed.
Co-authored-by: Spottedleaf <Spottedleaf@users.noreply.github.com>
diff --git a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
index 2bfc4cb47fc51345fc733b6ab3b19ace4f8bc171..dad2b89355b05900251955d015451fe195bfdb49 100644
index 6ecda939755534ce0f35680be16a04c0a0e0a312..488d5654f5414c9950f19e86a791507366366e01 100644
--- a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
+++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
@@ -32,6 +32,14 @@ public class ClientboundLevelChunkPacketData {
@@ -7,7 +7,7 @@ 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 135d932a31a557ff06643fb563262423d48cd540..9fb025b05cacc901d72d93c16cf2b7e48d4dd9a6 100644
index c16463febcf74e20e8e984bc19e3179a0f5f4ed1..3adfb334068d3722329f91011c41d5e59b1956c9 100644
--- a/net/minecraft/world/entity/ai/behavior/GateBehavior.java
+++ b/net/minecraft/world/entity/ai/behavior/GateBehavior.java
@@ -69,7 +69,7 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E>
@@ -57,7 +57,7 @@ index 135d932a31a557ff06643fb563262423d48cd540..9fb025b05cacc901d72d93c16cf2b7e4
@Override
@@ -118,24 +132,36 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E>
public static enum RunningPolicy {
public enum RunningPolicy {
RUN_ONE {
+ // Paper start - Perf: Remove streams from hot code
@Override
@@ -98,7 +98,7 @@ index 135d932a31a557ff06643fb563262423d48cd540..9fb025b05cacc901d72d93c16cf2b7e4
}
}
diff --git a/net/minecraft/world/entity/ai/gossip/GossipContainer.java b/net/minecraft/world/entity/ai/gossip/GossipContainer.java
index 75233df622242db6c23f7a1a5812bf25942bca41..133aab614b102419ec731e901858c0347076bae9 100644
index 600633670eb269ab1f4f50865942c46ed4c66fc6..9008b12745628fdcc225415e20e740192185bdd8 100644
--- a/net/minecraft/world/entity/ai/gossip/GossipContainer.java
+++ b/net/minecraft/world/entity/ai/gossip/GossipContainer.java
@@ -28,7 +28,7 @@ import net.minecraft.util.VisibleForDebug;
@@ -133,7 +133,7 @@ index 75233df622242db6c23f7a1a5812bf25942bca41..133aab614b102419ec731e901858c034
+ List<GossipContainer.GossipEntry> entries = this.decompress(); // Paper - Perf: Remove streams from hot code
if (entries.isEmpty()) {
return Collections.emptyList();
} else {
}
@@ -176,7 +190,23 @@ public class GossipContainer {
private final Object2IntMap<GossipType> entries = new Object2IntOpenHashMap<>();
@@ -16,25 +16,24 @@ This lets us get faster foreach iteration, as well as avoids map lookups on
the values when needed.
diff --git a/net/minecraft/world/level/pathfinder/PathFinder.java b/net/minecraft/world/level/pathfinder/PathFinder.java
index bb36ebf3a57cf1de98506a6391d3273b0f8e0f76..7564a67d2d63c3a7f95e1d6008e7308620653774 100644
index 53aa6995d18cae5663d392e606c049e2e9138a99..253074a0a14115cd3d18aaae6fa5822cf32346c9 100644
--- a/net/minecraft/world/level/pathfinder/PathFinder.java
+++ b/net/minecraft/world/level/pathfinder/PathFinder.java
@@ -54,8 +54,12 @@ public class PathFinder {
if (from == null) {
@@ -55,20 +55,24 @@ public class PathFinder {
return null;
} else {
- Map<Target, BlockPos> tos = targets.stream()
- .collect(Collectors.toMap(pos -> this.nodeEvaluator.getTarget(pos.getX(), pos.getY(), pos.getZ()), Function.identity()));
+ // Paper start - Perf: remove streams and optimize collection
+ List<Map.Entry<Target, BlockPos>> tos = Lists.newArrayList();
+ for (BlockPos pos : targets) {
+ tos.add(new java.util.AbstractMap.SimpleEntry<>(this.nodeEvaluator.getTarget(pos.getX(), pos.getY(), pos.getZ()), pos));
+ }
+ // Paper end - Perf: remove streams and optimize collection
Path path = this.findPath(from, tos, maxPathLength, reachRange, maxVisitedNodesMultiplier);
this.nodeEvaluator.done();
return path;
@@ -63,12 +67,12 @@ public class PathFinder {
}
- Map<Target, BlockPos> tos = targets.stream()
- .collect(Collectors.toMap(pos -> this.nodeEvaluator.getTarget(pos.getX(), pos.getY(), pos.getZ()), Function.identity()));
+ // Paper start - Perf: remove streams and optimize collection
+ List<Map.Entry<Target, BlockPos>> tos = Lists.newArrayList();
+ for (BlockPos pos : targets) {
+ tos.add(new java.util.AbstractMap.SimpleEntry<>(this.nodeEvaluator.getTarget(pos.getX(), pos.getY(), pos.getZ()), pos));
+ }
+ // Paper end - Perf: remove streams and optimize collection
Path path = this.findPath(from, tos, maxPathLength, reachRange, maxVisitedNodesMultiplier);
this.nodeEvaluator.done();
return path;
}
private @Nullable Path findPath(
@@ -63,7 +62,7 @@ index bb36ebf3a57cf1de98506a6391d3273b0f8e0f76..7564a67d2d63c3a7f95e1d6008e73086
current.closed = true;
- for (Target target : targets) {
+ // Paper start - optimize collection
+ // Paper start - Perf: remove streams and optimize collection
+ for (int positionIndex = 0, size = targets.size(); positionIndex < size; positionIndex++) {
+ final Map.Entry<Target, BlockPos> entry = targets.get(positionIndex);
+ Target target = entry.getKey();
@@ -75,17 +74,21 @@ index bb36ebf3a57cf1de98506a6391d3273b0f8e0f76..7564a67d2d63c3a7f95e1d6008e73086
}
}
@@ -126,34 +134,40 @@ public class PathFinder {
@@ -126,34 +134,41 @@ public class PathFinder {
}
}
- Optional<Path> optPath = !reachedTargets.isEmpty()
- ? reachedTargets.stream()
- .map(targetx -> this.reconstructPath(targetx.getBestNode(), targetMap.get(targetx), true))
- .map(target -> this.reconstructPath(target.getBestNode(), targetMap.get(target), true))
- .min(Comparator.comparingInt(Path::getNodeCount))
- : targets.stream()
- .map(targetx -> this.reconstructPath(targetx.getBestNode(), targetMap.get(targetx), false))
- .map(target -> this.reconstructPath(target.getBestNode(), targetMap.get(target), false))
- .min(Comparator.comparingDouble(Path::getDistToTarget).thenComparingInt(Path::getNodeCount));
- profiler.pop();
- if (optPath.isEmpty()) {
- return null;
+ // Paper start - Perf: remove streams and optimize collection
+ Path best = null;
+ boolean entryListIsEmpty = reachedTargets.isEmpty();
+ Comparator<Path> comparator = entryListIsEmpty
@@ -96,23 +99,21 @@ index bb36ebf3a57cf1de98506a6391d3273b0f8e0f76..7564a67d2d63c3a7f95e1d6008e73086
+ if (best == null || comparator.compare(path, best) < 0) {
+ best = path;
+ }
+ }
profiler.pop();
- if (optPath.isEmpty()) {
- return null;
- } else {
- Path path = optPath.get();
- if (captureDebug) {
- path.setDebug(this.openSet.getHeap(), closedSet.toArray(Node[]::new), targets);
}
-
- Path path = optPath.get();
- if (captureDebug) {
- path.setDebug(this.openSet.getHeap(), closedSet.toArray(Node[]::new), targets);
+ profiler.pop();
+ if (captureDebug && best != null) {
+ Set<Target> set = Sets.newHashSet();
+ for(Map.Entry<Target, BlockPos> entry : targets) {
+ set.add(entry.getKey());
}
-
- return path;
+ }
+ best.setDebug(this.openSet.getHeap(), closedSet.toArray(Node[]::new), set);
}
-
- return path;
+ return best;
+ // Paper end - Perf: remove streams and optimize collection
}
@@ -48,10 +48,10 @@ index cff05f64af7f425b77cbbd7680c5cc592faa798c..0ab4080c8e30d5ee14ce5fcbac64b031
serverEntity.getLastSentYRot(),
entity.getType(),
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
index 23ae1694c3d5fffdb2277316f66c5fd59157f5ca..be28a41084d656c44b611cf3a01ba89de610d5b2 100644
index 7f879bc44354c9fac0238208db12b55a948982fb..55cfe38c0daa3ca71a71bf8c7d88fd613fabd82d 100644
--- a/net/minecraft/server/level/ChunkMap.java
+++ b/net/minecraft/server/level/ChunkMap.java
@@ -1361,6 +1361,7 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP
@@ -1357,6 +1357,7 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP
ChunkMap.this.level.debugSynchronizers().startTrackingEntity(player, this.entity);
}
// Paper end - entity tracking events
@@ -60,7 +60,7 @@ index 23ae1694c3d5fffdb2277316f66c5fd59157f5ca..be28a41084d656c44b611cf3a01ba89d
} else {
this.removePlayer(player);
diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java
index 6774d12d04fa848624fb8bf7a3c2b8bcb88f6e8f..19bb751d3e9c347fe2446412f85c9f28ec8212e6 100644
index a62ec41b1319857f0d6cd223d013a92b1e2c2b0d..5fad52157fc22755196ce9eaddaa2bc23c5bb86b 100644
--- a/net/minecraft/server/level/ServerEntity.java
+++ b/net/minecraft/server/level/ServerEntity.java
@@ -89,6 +89,13 @@ public class ServerEntity {
@@ -19,12 +19,14 @@ Co-authored-by: egg82 <eggys82@gmail.com>
diff --git a/io/papermc/paper/redstone/RedstoneWireTurbo.java b/io/papermc/paper/redstone/RedstoneWireTurbo.java
new file mode 100644
index 0000000000000000000000000000000000000000..ff747a1ecdf3c888bca0d69de4f85dcd810b6139
index 0000000000000000000000000000000000000000..8e03419df1469c6fecfaa82fcb319a58635b39f3
--- /dev/null
+++ b/io/papermc/paper/redstone/RedstoneWireTurbo.java
@@ -0,0 +1,954 @@
@@ -0,0 +1,949 @@
+package io.papermc.paper.redstone;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ThreadLocalRandom;
@@ -36,11 +38,7 @@ index 0000000000000000000000000000000000000000..ff747a1ecdf3c888bca0d69de4f85dcd
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.RedStoneWireBlock;
+import net.minecraft.world.level.block.state.BlockState;
+import org.bukkit.craftbukkit.block.CraftBlock;
+import org.bukkit.event.block.BlockRedstoneEvent;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import org.bukkit.craftbukkit.event.CraftEventFactory;
+
+public final class RedstoneWireTurbo {
+ /*
@@ -914,12 +912,9 @@ index 0000000000000000000000000000000000000000..ff747a1ecdf3c888bca0d69de4f85dcd
+ // always be in the range of 0 to 15, the following if will correct that.
+ if (k > j) j = k;
+
+ // egg82's amendment
+ // Adding Bukkit's BlockRedstoneEvent - er.. event.
+ // [egg82] Adding Bukkit's BlockRedstoneEvent.
+ if (i != j) {
+ BlockRedstoneEvent event = new BlockRedstoneEvent(CraftBlock.at(worldIn, upd.self), i, j);
+ worldIn.getCraftServer().getPluginManager().callEvent(event);
+ j = event.getNewCurrent();
+ j = CraftEventFactory.callRedstoneChange(worldIn, upd.self, i, j).getNewCurrent();
+ }
+
+ if (i != j) {
@@ -978,10 +973,10 @@ index 0000000000000000000000000000000000000000..ff747a1ecdf3c888bca0d69de4f85dcd
+ }
+}
diff --git a/net/minecraft/world/level/block/RedStoneWireBlock.java b/net/minecraft/world/level/block/RedStoneWireBlock.java
index 0ee7bc3a10896a4f59fa94df158bce741aea0464..a677c3bfddfa0b7947394a3975ec43794be07d7e 100644
index 0ee7bc3a10896a4f59fa94df158bce741aea0464..a00002774755eb90a0b772fe742b30a5e09452cf 100644
--- a/net/minecraft/world/level/block/RedStoneWireBlock.java
+++ b/net/minecraft/world/level/block/RedStoneWireBlock.java
@@ -269,6 +269,60 @@ public class RedStoneWireBlock extends Block {
@@ -269,6 +269,56 @@ public class RedStoneWireBlock extends Block {
return relativeState.isFaceSturdy(level, relativePos, Direction.UP) || relativeState.is(Blocks.HOPPER);
}
@@ -1021,12 +1016,8 @@ index 0ee7bc3a10896a4f59fa94df158bce741aea0464..a677c3bfddfa0b7947394a3975ec4379
+ int oldPower = state.getValue(POWER);
+ int newPower = ((DefaultRedstoneWireEvaluator) evaluator).calculateTargetStrength(level, pos);
+ if (oldPower != newPower) {
+ org.bukkit.event.block.BlockRedstoneEvent event = new org.bukkit.event.block.BlockRedstoneEvent(org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), oldPower, newPower);
+ level.getCraftServer().getPluginManager().callEvent(event);
+
+ newPower = event.getNewCurrent();
+
+ if (level.getBlockState(pos) == state) {
+ newPower = org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(level, pos, oldPower, newPower).getNewCurrent();
+ state = state.setValue(POWER, newPower);
+ // [Space Walker] suppress shape updates and emit those manually to
+ // bypass the new neighbor update stack.
@@ -1042,7 +1033,7 @@ index 0ee7bc3a10896a4f59fa94df158bce741aea0464..a677c3bfddfa0b7947394a3975ec4379
private void updatePowerStrength(
final Level level,
final BlockPos pos,
@@ -303,7 +357,7 @@ public class RedStoneWireBlock extends Block {
@@ -303,7 +353,7 @@ public class RedStoneWireBlock extends Block {
@Override
protected void onPlace(final BlockState state, final Level level, final BlockPos pos, final BlockState oldState, final boolean movedByPiston) {
if (!oldState.is(state.getBlock()) && !level.isClientSide()) {
@@ -1051,7 +1042,7 @@ index 0ee7bc3a10896a4f59fa94df158bce741aea0464..a677c3bfddfa0b7947394a3975ec4379
for (Direction direction : Direction.Plane.VERTICAL) {
level.updateNeighborsAt(pos.relative(direction), this);
@@ -320,7 +374,7 @@ public class RedStoneWireBlock extends Block {
@@ -320,7 +370,7 @@ public class RedStoneWireBlock extends Block {
level.updateNeighborsAt(pos.relative(direction), this);
}
@@ -1060,7 +1051,7 @@ index 0ee7bc3a10896a4f59fa94df158bce741aea0464..a677c3bfddfa0b7947394a3975ec4379
this.updateNeighborsOfNeighboringWires(level, pos);
}
}
@@ -347,7 +401,7 @@ public class RedStoneWireBlock extends Block {
@@ -347,7 +397,7 @@ public class RedStoneWireBlock extends Block {
if (!level.isClientSide()) {
if (block != this || !useExperimentalEvaluator(level)) {
if (state.canSurvive(level, pos)) {
@@ -1070,10 +1061,10 @@ index 0ee7bc3a10896a4f59fa94df158bce741aea0464..a677c3bfddfa0b7947394a3975ec4379
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 fe52dd304bc03b172654b89acb382c7f410d7946..afdcbb9e5a4c859d82bfb33a1d253514c17eed39 100644
index 45c3586a653f4ec22b9fb859b2869503d0b6c5b3..81b7b625dbfe69f2a9d74dac000a0e60a75db839 100644
--- a/net/minecraft/world/level/redstone/DefaultRedstoneWireEvaluator.java
+++ b/net/minecraft/world/level/redstone/DefaultRedstoneWireEvaluator.java
@@ -47,7 +47,7 @@ public class DefaultRedstoneWireEvaluator extends RedstoneWireEvaluator {
@@ -44,7 +44,7 @@ public class DefaultRedstoneWireEvaluator extends RedstoneWireEvaluator {
}
}
@@ -22,30 +22,25 @@ Alternate Current's wire handler.
diff --git a/alternate/current/wire/LevelHelper.java b/alternate/current/wire/LevelHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..ff663d3089627e75221aa128aff4bf5cc459addb
index 0000000000000000000000000000000000000000..dd533953f33aacfc91a8acad6c9a9d601df0bd86
--- /dev/null
+++ b/alternate/current/wire/LevelHelper.java
@@ -0,0 +1,66 @@
@@ -0,0 +1,61 @@
+package alternate.current.wire;
+
+import org.bukkit.craftbukkit.block.CraftBlock;
+import org.bukkit.event.block.BlockRedstoneEvent;
+
+import net.minecraft.core.BlockPos;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.level.chunk.ChunkAccess;
+import net.minecraft.world.level.chunk.status.ChunkStatus;
+import net.minecraft.world.level.chunk.LevelChunkSection;
+import net.minecraft.world.level.chunk.status.ChunkStatus;
+import org.bukkit.craftbukkit.event.CraftEventFactory;
+
+class LevelHelper {
+
+ static int doRedstoneEvent(ServerLevel level, BlockPos pos, int prevPower, int newPower) {
+ BlockRedstoneEvent event = new BlockRedstoneEvent(CraftBlock.at(level, pos), prevPower, newPower);
+ level.getCraftServer().getPluginManager().callEvent(event);
+
+ return event.getNewCurrent();
+ return CraftEventFactory.callRedstoneChange(level, pos, prevPower, newPower).getNewCurrent();
+ }
+
+ /**
@@ -2326,18 +2321,18 @@ index 0000000000000000000000000000000000000000..298076a0db4e6ee6e4775ac43bf749d9
+ }
+}
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 9f43d29aa4fecb03d6ef49e71e4de846dca187de..2e76ab96c66fd39b5e6619c3dbc75559d5c9b04a 100644
index 9fc39230f52f4cf6b44edd34aa9542b9d500538e..965d9f515d0410df3395d07c6cf5e6349c75c923 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -232,6 +232,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
public final WorldGenSettings worldGenSettings;
@@ -233,6 +233,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
private @Nullable ServerClockManager clockManager;
public boolean hasPhysicsEvent = true; // Paper - BlockPhysicsEvent
public boolean hasEntityMoveEvent; // Paper - Add EntityMoveEvent
+ private final alternate.current.wire.WireHandler wireHandler = new alternate.current.wire.WireHandler(this); // Paper - optimize redstone (Alternate Current)
@Override
public @Nullable LevelChunk getChunkIfLoaded(int x, int z) {
@@ -2774,6 +2775,13 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -2775,6 +2776,13 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
return this.debugSynchronizers;
}
@@ -2352,7 +2347,7 @@ index 9f43d29aa4fecb03d6ef49e71e4de846dca187de..2e76ab96c66fd39b5e6619c3dbc75559
return toLevel.dimension() != Level.NETHER || this.getGameRules().get(GameRules.ALLOW_ENTERING_NETHER_USING_PORTALS);
}
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index 329aef0e55664fbdd98c84d4d7cee74cd633e9c3..b9e07d5bd36993c5ac83b843866fabbea71c4281 100644
index 8d1d7582730e05bf605db31e13ee24242592074e..3d20ef3bc4e891e3e6e5f9f6f5efc6c0367369ea 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -2156,6 +2156,17 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
@@ -2370,11 +2365,11 @@ index 329aef0e55664fbdd98c84d4d7cee74cd633e9c3..b9e07d5bd36993c5ac83b843866fabbe
+ }
+ // Paper end - optimize redstone (Alternate Current)
+
public static enum ExplosionInteraction implements StringRepresentable {
public enum ExplosionInteraction implements StringRepresentable {
NONE("none"),
BLOCK("block"),
diff --git a/net/minecraft/world/level/block/RedStoneWireBlock.java b/net/minecraft/world/level/block/RedStoneWireBlock.java
index a677c3bfddfa0b7947394a3975ec43794be07d7e..ed471efa3ab705de62aec81c3940c1b9c5acdbc5 100644
index 5bdeaa9ae20de9f42e760d2207219d5f22d95e9b..0e285f38d282b29f625c5578ee1871a6c2b8585d 100644
--- a/net/minecraft/world/level/block/RedStoneWireBlock.java
+++ b/net/minecraft/world/level/block/RedStoneWireBlock.java
@@ -269,7 +269,7 @@ public class RedStoneWireBlock extends Block {
@@ -2386,7 +2381,7 @@ index a677c3bfddfa0b7947394a3975ec43794be07d7e..ed471efa3ab705de62aec81c3940c1b9
// The bulk of the new functionality is found in RedstoneWireTurbo.java
io.papermc.paper.redstone.RedstoneWireTurbo turbo = new io.papermc.paper.redstone.RedstoneWireTurbo(this);
@@ -357,7 +357,13 @@ public class RedStoneWireBlock extends Block {
@@ -353,7 +353,13 @@ public class RedStoneWireBlock extends Block {
@Override
protected void onPlace(final BlockState state, final Level level, final BlockPos pos, final BlockState oldState, final boolean movedByPiston) {
if (!oldState.is(state.getBlock()) && !level.isClientSide()) {
@@ -2401,7 +2396,7 @@ index a677c3bfddfa0b7947394a3975ec43794be07d7e..ed471efa3ab705de62aec81c3940c1b9
for (Direction direction : Direction.Plane.VERTICAL) {
level.updateNeighborsAt(pos.relative(direction), this);
@@ -374,7 +380,13 @@ public class RedStoneWireBlock extends Block {
@@ -370,7 +376,13 @@ public class RedStoneWireBlock extends Block {
level.updateNeighborsAt(pos.relative(direction), this);
}
@@ -2416,7 +2411,7 @@ index a677c3bfddfa0b7947394a3975ec43794be07d7e..ed471efa3ab705de62aec81c3940c1b9
this.updateNeighborsOfNeighboringWires(level, pos);
}
}
@@ -399,9 +411,15 @@ public class RedStoneWireBlock extends Block {
@@ -395,9 +407,15 @@ public class RedStoneWireBlock extends Block {
final BlockState state, final Level level, final BlockPos pos, final Block block, final @Nullable Orientation orientation, final boolean movedByPiston
) {
if (!level.isClientSide()) {
@@ -8,10 +8,10 @@ This ensures at least a valid version of the chunk exists
on disk, even if outdated
diff --git a/net/minecraft/world/level/chunk/storage/RegionFile.java b/net/minecraft/world/level/chunk/storage/RegionFile.java
index d628e06a8dbf11b491b323c10a512fde22e72a12..fc712f9dbc893a6dee8ccecc54d0af07659dee62 100644
index d39f8ae8f5736a7f4590ca41ac95792a61975baf..4d83986bccc2003b291dd93534da2d2260f6c428 100644
--- a/net/minecraft/world/level/chunk/storage/RegionFile.java
+++ b/net/minecraft/world/level/chunk/storage/RegionFile.java
@@ -25,6 +25,7 @@ import org.slf4j.Logger;
@@ -24,6 +24,7 @@ import org.slf4j.Logger;
public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patches.chunk_system.storage.ChunkSystemRegionFile { // Paper - rewrite chunk system
private static final Logger LOGGER = LogUtils.getLogger();
@@ -19,7 +19,7 @@ index d628e06a8dbf11b491b323c10a512fde22e72a12..fc712f9dbc893a6dee8ccecc54d0af07
private static final int SECTOR_BYTES = 4096;
@VisibleForTesting
protected static final int SECTOR_INTS = 1024;
@@ -453,6 +454,24 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
@@ -451,6 +452,24 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
this.pos = pos;
}
@@ -45,18 +45,10 @@ index d628e06a8dbf11b491b323c10a512fde22e72a12..fc712f9dbc893a6dee8ccecc54d0af07
public void close() throws IOException {
ByteBuffer result = ByteBuffer.wrap(this.buf, 0, this.count);
diff --git a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
index aa36e241d49725eb13f00e57d020d4da24add53d..5fa64ff30b8252f139ca7f77c14352c384c8c0e5 100644
index 3d0a09662e5ed1f7bc0c02dbe6de529791f2f66f..fedec4d680934730d38d8c5d609fe216938b668a 100644
--- a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+++ b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
@@ -16,6 +16,7 @@ import net.minecraft.world.level.ChunkPos;
import org.jspecify.annotations.Nullable;
public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise.patches.chunk_system.io.ChunkSystemRegionFileStorage { // Paper - rewrite chunk system
+ private static final org.slf4j.Logger LOGGER = com.mojang.logging.LogUtils.getLogger(); // Paper
public static final String ANVIL_EXTENSION = ".mca";
private static final int MAX_CACHE_SIZE = 256;
public final Long2ObjectLinkedOpenHashMap<RegionFile> regionCache = new Long2ObjectLinkedOpenHashMap<>();
@@ -122,11 +123,24 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
@@ -123,11 +123,24 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
// (and, the regionfile parameter is unused for writing until the write call)
final ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.WriteData writeData = ((ca.spottedleaf.moonrise.patches.chunk_system.storage.ChunkSystemRegionFile)regionFile).moonrise$startWrite(compound, pos);
@@ -81,7 +73,7 @@ index aa36e241d49725eb13f00e57d020d4da24add53d..5fa64ff30b8252f139ca7f77c14352c3
return writeData;
}
@@ -331,9 +345,18 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
@@ -325,9 +338,18 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
if (value == null) {
region.clear(pos);
} else {
@@ -96,12 +88,12 @@ index aa36e241d49725eb13f00e57d020d4da24add53d..5fa64ff30b8252f139ca7f77c14352c3
+ } catch (final RegionFileSizeException ex) {
+ region.clear(pos);
+ final int maxSize = RegionFile.MAX_CHUNK_SIZE / (1024 * 1024);
+ LOGGER.error("Chunk at (" + pos.x() + "," + pos.z() + ") in regionfile '" + region.getPath().toString() + "' exceeds max size of " + maxSize + "MiB, it has been deleted from disk.");
+ LOGGER.error("Chunk at ({},{}) in regionfile '{}' exceeds max size of {}MiB, it has been deleted from disk.", pos.x(), pos.z(), region.getPath().toString(), maxSize);
+ // Paper end - don't write garbage data to disk if writing serialization fails
}
}
}
@@ -376,4 +399,13 @@ 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;
}
@@ -33,10 +33,10 @@ index 0cfdbdee091f43ca011bc68f7479eb7b84801b09..8d9ce3d301d5f7e4106587ae00adb8dd
scopedCollector.forChild(entity.problemPath()), entity.registryAccess()
);
diff --git a/net/minecraft/world/entity/EntityType.java b/net/minecraft/world/entity/EntityType.java
index 2cdbb5f9bace96a8c4ad7fc4de17678ec0db8a6c..6b19f8cd8655cc52a0d8deadb9ca9f24473637a4 100644
index a1ee86c18c17e14ebf98f0e72680543addc4c7fa..1ee78f52205cbb0b254a28999f3707ccccbe998e 100644
--- a/net/minecraft/world/entity/EntityType.java
+++ b/net/minecraft/world/entity/EntityType.java
@@ -1647,7 +1647,18 @@ public class EntityType<T extends Entity> implements EntityTypeTest<Entity, T>,
@@ -1641,7 +1641,18 @@ public class EntityType<T extends Entity> implements EntityTypeTest<Entity, T>,
}
public static Stream<Entity> loadEntitiesRecursive(final ValueInput.ValueInputList entities, final Level level, final EntitySpawnReason reason) {
@@ -56,10 +56,10 @@ index 2cdbb5f9bace96a8c4ad7fc4de17678ec0db8a6c..6b19f8cd8655cc52a0d8deadb9ca9f24
return entity;
}));
diff --git a/net/minecraft/world/level/chunk/storage/EntityStorage.java b/net/minecraft/world/level/chunk/storage/EntityStorage.java
index 04045397dff89870316d0d374b9e472540c3298d..b1cd822c114b99a25fad2e649ca44596109aa0e5 100644
index 0326b79fea3f563c3cdfa8f7cf1a3af0e4f08d38..4bd083ecdea207ee5689760fccc9ce8a5a958d57 100644
--- a/net/minecraft/world/level/chunk/storage/EntityStorage.java
+++ b/net/minecraft/world/level/chunk/storage/EntityStorage.java
@@ -95,7 +95,18 @@ public class EntityStorage implements EntityPersistentStorage<Entity> {
@@ -92,7 +92,18 @@ public class EntityStorage implements EntityPersistentStorage<Entity> {
} else {
try (ProblemReporter.ScopedCollector reporter = new ProblemReporter.ScopedCollector(ChunkAccess.problemPath(pos), LOGGER)) {
ListTag entities = new ListTag();
@@ -55,10 +55,10 @@ index a6f9435a8e2a6c8cc05bd3873cb6fc0ec98973b8..d44fd9eea0ccb09c894d6431ede8a4c2
this.used.set(position, position + size);
}
diff --git a/net/minecraft/world/level/chunk/storage/RegionFile.java b/net/minecraft/world/level/chunk/storage/RegionFile.java
index fc712f9dbc893a6dee8ccecc54d0af07659dee62..b4a59b855b45018853e8122a5829a48ebd220a5f 100644
index 4d83986bccc2003b291dd93534da2d2260f6c428..42d07aef0b2f646ec52ed6f4e7c003dbf82bd92d 100644
--- a/net/minecraft/world/level/chunk/storage/RegionFile.java
+++ b/net/minecraft/world/level/chunk/storage/RegionFile.java
@@ -47,6 +47,363 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
@@ -46,6 +46,363 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
@VisibleForTesting
protected final RegionBitmap usedSectors = new RegionBitmap();
@@ -127,11 +127,11 @@ index fc712f9dbc893a6dee8ccecc54d0af07659dee62..b4a59b855b45018853e8122a5829a48e
+ private void backupRegionFile(Path to) {
+ try {
+ this.file.force(true);
+ LOGGER.warn("Backing up regionfile \"" + this.path.toAbsolutePath() + "\" to " + to.toAbsolutePath());
+ java.nio.file.Files.copy(this.path, to, java.nio.file.StandardCopyOption.COPY_ATTRIBUTES);
+ LOGGER.warn("Backed up the regionfile to " + to.toAbsolutePath());
+ LOGGER.warn("Backing up regionfile \"{}\" to {}", this.path.toAbsolutePath(), to.toAbsolutePath());
+ Files.copy(this.path, to, java.nio.file.StandardCopyOption.COPY_ATTRIBUTES);
+ LOGGER.warn("Backed up the regionfile to {}", to.toAbsolutePath());
+ } catch (IOException ex) {
+ LOGGER.error("Failed to backup to " + to.toAbsolutePath(), ex);
+ LOGGER.error("Failed to backup to {}", to.toAbsolutePath(), ex);
+ }
+ }
+
@@ -152,13 +152,13 @@ index fc712f9dbc893a6dee8ccecc54d0af07659dee62..b4a59b855b45018853e8122a5829a48e
+ }
+ ChunkPos ourLowerLeftPosition = RegionFileStorage.getRegionFileCoordinates(this.path);
+ if (ourLowerLeftPosition == null) {
+ LOGGER.error("Unable to get chunk location of regionfile " + this.path.toAbsolutePath() + ", cannot recover header");
+ LOGGER.error("Unable to get chunk location of regionfile {}, cannot recover header", this.path.toAbsolutePath());
+ return false;
+ }
+ synchronized (this) {
+ this.recalculateCount.getAndIncrement();
+
+ LOGGER.warn("Corrupt regionfile header detected! Attempting to re-calculate header offsets for regionfile " + this.path.toAbsolutePath(), new Throwable());
+ LOGGER.warn("Corrupt regionfile header detected! Attempting to re-calculate header offsets for regionfile {}", this.path.toAbsolutePath(), new Throwable());
+
+ // try to backup file so maybe it could be sent to us for further investigation
+
@@ -182,7 +182,7 @@ index fc712f9dbc893a6dee8ccecc54d0af07659dee62..b4a59b855b45018853e8122a5829a48e
+
+ ChunkPos chunkPos = SerializableChunkData.getChunkCoordinate(compound);
+ if (!inSameRegionfile(ourLowerLeftPosition, chunkPos)) {
+ LOGGER.error("Ignoring absolute chunk " + chunkPos + " in regionfile as it is not contained in the bounds of the regionfile '" + this.path.toAbsolutePath() + "'. It should be in regionfile (" + (chunkPos.x() >> 5) + "," + (chunkPos.z() >> 5) + ")");
+ LOGGER.error("Ignoring absolute chunk {} in regionfile as it is not contained in the bounds of the regionfile '{}'. It should be in regionfile ({},{})", chunkPos, this.path.toAbsolutePath(), chunkPos.x() >> 5, chunkPos.z() >> 5);
+ continue;
+ }
+ int location = (chunkPos.x() & 31) | ((chunkPos.z() & 31) << 5);
@@ -204,7 +204,7 @@ index fc712f9dbc893a6dee8ccecc54d0af07659dee62..b4a59b855b45018853e8122a5829a48e
+ isAikarOversized = true;
+ }
+ } catch (Exception ex) {
+ LOGGER.error("Failed to read aikar oversized data for absolute chunk (" + chunkPos.x() + "," + chunkPos.z() + ") in regionfile " + this.path.toAbsolutePath() + ", oversized data for this chunk will be lost", ex);
+ LOGGER.error("Failed to read aikar oversized data for absolute chunk ({},{}) in regionfile {}, oversized data for this chunk will be lost", chunkPos.x(), chunkPos.z(), this.path.toAbsolutePath(), ex);
+ // fall through, if we can't read aikar oversized we can't risk corrupting chunk data
+ }
+ }
@@ -253,7 +253,7 @@ index fc712f9dbc893a6dee8ccecc54d0af07659dee62..b4a59b855b45018853e8122a5829a48e
+ try {
+ chunkData = Files.readAllBytes(regionFile);
+ } catch (Exception ex) {
+ LOGGER.error("Failed to read oversized chunk data in file " + regionFile.toAbsolutePath() + ", data will be lost", ex);
+ LOGGER.error("Failed to read oversized chunk data in file {}, data will be lost", regionFile.toAbsolutePath(), ex);
+ continue;
+ }
+
@@ -273,12 +273,12 @@ index fc712f9dbc893a6dee8ccecc54d0af07659dee62..b4a59b855b45018853e8122a5829a48e
+ }
+
+ if (compound == null) {
+ LOGGER.error("Failed to read oversized chunk data in file " + regionFile.toAbsolutePath() + ", it's corrupt. Its data will be lost");
+ LOGGER.error("Failed to read oversized chunk data in file {}, it's corrupt. Its data will be lost", regionFile.toAbsolutePath());
+ continue;
+ }
+
+ if (!SerializableChunkData.getChunkCoordinate(compound).equals(oversizedCoords)) {
+ LOGGER.error("Can't use oversized chunk stored in " + regionFile.toAbsolutePath() + ", got absolute chunkpos: " + SerializableChunkData.getChunkCoordinate(compound) + ", expected " + oversizedCoords);
+ LOGGER.error("Can't use oversized chunk stored in {}, got absolute chunkpos: {}, expected {}", regionFile.toAbsolutePath(), net.minecraft.world.level.chunk.storage.SerializableChunkData.getChunkCoordinate(compound), oversizedCoords);
+ continue;
+ }
+
@@ -312,7 +312,7 @@ index fc712f9dbc893a6dee8ccecc54d0af07659dee62..b4a59b855b45018853e8122a5829a48e
+ if (newSectorAllocations.tryAllocate(sectorOffset, sectorLength)) {
+ calculatedOffsets[location] = sectorOffset << 8 | (sectorLength > 255 ? 255 : sectorLength); // support forge style oversized
+ } else {
+ LOGGER.error("Failed to allocate space for local chunk (overlapping data??) at (" + chunkX + "," + chunkZ + ") in regionfile " + this.path.toAbsolutePath() + ", chunk will be regenerated");
+ LOGGER.error("Failed to allocate space for local chunk (overlapping data??) at ({},{}) in regionfile {}, chunk will be regenerated", chunkX, chunkZ, this.path.toAbsolutePath());
+ }
+ }
+ }
@@ -336,7 +336,7 @@ index fc712f9dbc893a6dee8ccecc54d0af07659dee62..b4a59b855b45018853e8122a5829a48e
+ calculatedOffsets[location] = sectorOffset << 8 | (sectorLength > 255 ? 255 : sectorLength); // support forge style oversized
+ } catch (IOException ex) {
+ newSectorAllocations.free(sectorOffset, sectorLength);
+ LOGGER.error("Failed to write new oversized chunk data holder, local chunk at (" + chunkX + "," + chunkZ + ") in regionfile " + this.path.toAbsolutePath() + " will be regenerated");
+ LOGGER.error("Failed to write new oversized chunk data holder, local chunk at ({},{}) in regionfile {} will be regenerated", chunkX, chunkZ, this.path.toAbsolutePath());
+ }
+ }
+ }
@@ -358,7 +358,7 @@ index fc712f9dbc893a6dee8ccecc54d0af07659dee62..b4a59b855b45018853e8122a5829a48e
+ try {
+ this.writeOversizedMeta();
+ } catch (Exception ex) {
+ LOGGER.error("Failed to write aikar oversized chunk meta, all aikar style oversized chunk data will be lost for regionfile " + this.path.toAbsolutePath(), ex);
+ LOGGER.error("Failed to write aikar oversized chunk meta, all aikar style oversized chunk data will be lost for regionfile {}", this.path.toAbsolutePath(), ex);
+ Files.deleteIfExists(this.getOversizedMetaFile());
+ }
+ } else {
@@ -369,7 +369,7 @@ index fc712f9dbc893a6dee8ccecc54d0af07659dee62..b4a59b855b45018853e8122a5829a48e
+
+ // before we overwrite the old sectors, print a summary of the chunks that got changed.
+
+ LOGGER.info("Starting summary of changes for regionfile " + this.path.toAbsolutePath());
+ LOGGER.info("Starting summary of changes for regionfile {}", this.path.toAbsolutePath());
+
+ for (int chunkX = 0; chunkX < 32; ++chunkX) {
+ for (int chunkZ = 0; chunkZ < 32; ++chunkZ) {
@@ -386,16 +386,16 @@ index fc712f9dbc893a6dee8ccecc54d0af07659dee62..b4a59b855b45018853e8122a5829a48e
+
+ if (oldOffset == 0) {
+ // found lost data
+ LOGGER.info("Found missing data for local chunk (" + chunkX + "," + chunkZ + ") in regionfile " + this.path.toAbsolutePath());
+ LOGGER.info("Found missing data for local chunk ({},{}) in regionfile {}", chunkX, chunkZ, this.path.toAbsolutePath());
+ } else if (newOffset == 0) {
+ LOGGER.warn("Data for local chunk (" + chunkX + "," + chunkZ + ") could not be recovered in regionfile " + this.path.toAbsolutePath() + ", it will be regenerated");
+ LOGGER.warn("Data for local chunk ({},{}) could not be recovered in regionfile {}, it will be regenerated", chunkX, chunkZ, this.path.toAbsolutePath());
+ } else {
+ LOGGER.info("Local chunk (" + chunkX + "," + chunkZ + ") changed to point to newer data or correct chunk in regionfile " + this.path.toAbsolutePath());
+ LOGGER.info("Local chunk ({},{}) changed to point to newer data or correct chunk in regionfile {}", chunkX, chunkZ, this.path.toAbsolutePath());
+ }
+ }
+ }
+
+ LOGGER.info("End of change summary for regionfile " + this.path.toAbsolutePath());
+ LOGGER.info("End of change summary for regionfile {}", this.path.toAbsolutePath());
+
+ // simply destroy the timestamp header, it's not used
+
@@ -407,9 +407,9 @@ index fc712f9dbc893a6dee8ccecc54d0af07659dee62..b4a59b855b45018853e8122a5829a48e
+ try {
+ this.flush();
+ this.file.force(true); // try to ensure it goes through...
+ LOGGER.info("Successfully wrote new header to disk for regionfile " + this.path.toAbsolutePath());
+ LOGGER.info("Successfully wrote new header to disk for regionfile {}", this.path.toAbsolutePath());
+ } catch (IOException ex) {
+ LOGGER.error("Failed to write new header to disk for regionfile " + this.path.toAbsolutePath(), ex);
+ LOGGER.error("Failed to write new header to disk for regionfile {}", this.path.toAbsolutePath(), ex);
+ }
+ }
+
@@ -423,75 +423,58 @@ index fc712f9dbc893a6dee8ccecc54d0af07659dee62..b4a59b855b45018853e8122a5829a48e
@Override
public final ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.WriteData moonrise$startWrite(final net.minecraft.nbt.CompoundTag data, final ChunkPos pos) throws IOException {
@@ -75,6 +432,7 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
throw new IllegalArgumentException("Expected directory, got " + externalFileDir.toAbsolutePath());
} else {
this.externalFileDir = externalFileDir;
+ this.canRecalcHeader = info.dfuType()[0] == net.minecraft.util.datafix.DataFixTypes.CHUNK; // Paper - add can recalc flag
this.offsets = this.header.asIntBuffer();
this.offsets.limit(1024);
this.header.position(4096);
}
this.externalFileDir = externalFileDir;
+ this.canRecalcHeader = info.dfuType()[0] == net.minecraft.util.datafix.DataFixTypes.CHUNK; // Paper - add can recalc flag
this.offsets = this.header.asIntBuffer();
this.offsets.limit(1024);
this.header.position(4096);
@@ -95,11 +453,13 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
long size = Files.size(path);
long size = Files.size(path);
- for (int i = 0; i < 1024; i++) {
+ boolean needsHeaderRecalc = false; // Paper - recalculate header on header corruption
+ boolean hasBackedUp = false; // Paper - recalculate header on header corruption
+ for (int i = 0; i < 1024; i++) { final int headerLocation = i; // Paper - we expect this to be the header location
int offset = this.offsets.get(i);
if (offset != 0) {
- int sectorNumber = getSectorNumber(offset);
- int numSectors = getNumSectors(offset);
+ int sectorNumber = getSectorNumber(offset); // Paper - we expect this to be offset in file in sectors
+ int numSectors = getNumSectors(offset); // Paper - diff on change, we expect this to be sector length of region - watch out for reassignments
// Spigot start
if (numSectors == 255) {
// We're maxed out, so we need to read the proper length from the section
- for (int i = 0; i < 1024; i++) {
+ boolean needsHeaderRecalc = false; // Paper - recalculate header on header corruption
+ boolean hasBackedUp = false; // Paper - recalculate header on header corruption
+ for (int i = 0; i < 1024; i++) { final int headerLocation = i; // Paper - diff on change, we expect this to be the header location
int offset = this.offsets.get(i);
if (offset != 0) {
- int sectorNumber = getSectorNumber(offset);
- int numSectors = getNumSectors(offset);
+ int sectorNumber = getSectorNumber(offset); // Paper - diff on change, we expect this to be offset in file in sectors
+ int numSectors = getNumSectors(offset); // Paper - diff on change, we expect this to be sector length of region - watch out for reassignments
// Spigot start
if (numSectors == 255) {
// We're maxed out, so we need to read the proper length from the section
@@ -110,18 +470,62 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
// Spigot end
if (sectorNumber < 2) {
LOGGER.warn("Region file {} has invalid sector at index: {}; sector {} overlaps with header", path, i, sectorNumber);
- this.offsets.put(i, 0);
+ //this.offsets.put(i, 0); // Paper - we catch this, but need it in the header for the summary change
} else if (numSectors == 0) {
LOGGER.warn("Region file {} has an invalid sector at index: {}; size has to be > 0", path, i);
- this.offsets.put(i, 0);
+ //this.offsets.put(i, 0); // Paper - we catch this, but need it in the header for the summary change
} else if (sectorNumber * 4096L > size) {
LOGGER.warn("Region file {} has an invalid sector at index: {}; sector {} is out of bounds", path, i, sectorNumber);
- this.offsets.put(i, 0);
+ //this.offsets.put(i, 0); // Paper - we catch this, but need it in the header for the summary change
} else {
- this.usedSectors.force(sectorNumber, numSectors);
+ //this.usedSectors.force(sectorNumber, numSectors); // Paper - move this down so we can check if it fails to allocate
+ }
+ // Paper start - recalculate header on header corruption
+ if (sectorNumber < 2 || numSectors <= 0 || ((long)sectorNumber * 4096L) > size) {
+ if (this.canRecalcHeader) {
+ LOGGER.error("Detected invalid header for regionfile " + this.path.toAbsolutePath() + "! Recalculating header...");
+ needsHeaderRecalc = true;
+ break;
+ } else {
+ // location = chunkX | (chunkZ << 5);
+ LOGGER.error("Detected invalid header for regionfile " + this.path.toAbsolutePath() +
+ "! Cannot recalculate, removing local chunk (" + (headerLocation & 31) + "," + (headerLocation >>> 5) + ") from header");
+ if (!hasBackedUp) {
+ hasBackedUp = true;
+ this.backupRegionFile();
+ }
+ this.timestamps.put(headerLocation, 0); // be consistent, delete the timestamp too
+ this.offsets.put(headerLocation, 0); // delete the entry from header
+ continue;
+ }
}
+ boolean failedToAllocate = !this.usedSectors.tryAllocate(sectorNumber, numSectors);
+ if (failedToAllocate) {
+ LOGGER.error("Overlapping allocation by local chunk (" + (headerLocation & 31) + "," + (headerLocation >>> 5) + ") in regionfile " + this.path.toAbsolutePath());
+ }
+ if (failedToAllocate & !this.canRecalcHeader) {
// Spigot end
if (sectorNumber < 2) {
LOGGER.warn("Region file {} has invalid sector at index: {}; sector {} overlaps with header", path, i, sectorNumber);
- this.offsets.put(i, 0);
+ //this.offsets.put(i, 0); // Paper - we catch this, but need it in the header for the summary change
} else if (numSectors == 0) {
LOGGER.warn("Region file {} has an invalid sector at index: {}; size has to be > 0", path, i);
- this.offsets.put(i, 0);
+ //this.offsets.put(i, 0); // Paper - we catch this, but need it in the header for the summary change
} else if (sectorNumber * 4096L > size) {
LOGGER.warn("Region file {} has an invalid sector at index: {}; sector {} is out of bounds", path, i, sectorNumber);
- this.offsets.put(i, 0);
+ //this.offsets.put(i, 0); // Paper - we catch this, but need it in the header for the summary change
} else {
- this.usedSectors.force(sectorNumber, numSectors);
+ //this.usedSectors.force(sectorNumber, numSectors); // Paper - move this down so we can check if it fails to allocate
+ }
+ // Paper start - recalculate header on header corruption
+ if (sectorNumber < 2 || numSectors <= 0 || ((long)sectorNumber * 4096L) > size) {
+ if (this.canRecalcHeader) {
+ LOGGER.error("Detected invalid header for regionfile {}! Recalculating header...", this.path.toAbsolutePath());
+ needsHeaderRecalc = true;
+ break;
+ } else {
+ // location = chunkX | (chunkZ << 5);
+ LOGGER.error("Detected invalid header for regionfile " + this.path.toAbsolutePath() +
+ "! Cannot recalculate, removing local chunk (" + (headerLocation & 31) + "," + (headerLocation >>> 5) + ") from header");
+ LOGGER.error("Detected invalid header for regionfile {}! Cannot recalculate, removing local chunk ({},{}) from header",
+ this.path.toAbsolutePath(), headerLocation & 31, headerLocation >>> 5);
+ if (!hasBackedUp) {
+ hasBackedUp = true;
+ this.backupRegionFile();
@@ -500,21 +483,38 @@ index fc712f9dbc893a6dee8ccecc54d0af07659dee62..b4a59b855b45018853e8122a5829a48e
+ this.offsets.put(headerLocation, 0); // delete the entry from header
+ continue;
+ }
+ needsHeaderRecalc |= failedToAllocate;
+ // Paper end - recalculate header on header corruption
+ }
+ boolean failedToAllocate = !this.usedSectors.tryAllocate(sectorNumber, numSectors);
+ if (failedToAllocate) {
+ LOGGER.error("Overlapping allocation by local chunk ({},{}) in regionfile {}", headerLocation & 31, headerLocation >>> 5, this.path.toAbsolutePath());
}
+ if (failedToAllocate & !this.canRecalcHeader) {
+ // location = chunkX | (chunkZ << 5);
+ LOGGER.error("Detected invalid header for regionfile {}! Cannot recalculate, removing local chunk ({},{}) from header",
+ this.path.toAbsolutePath(), headerLocation & 31, headerLocation >>> 5);
+ if (!hasBackedUp) {
+ hasBackedUp = true;
+ this.backupRegionFile();
+ }
+ this.timestamps.put(headerLocation, 0); // be consistent, delete the timestamp too
+ this.offsets.put(headerLocation, 0); // delete the entry from header
+ continue;
+ }
+ needsHeaderRecalc |= failedToAllocate;
+ // Paper end - recalculate header on header corruption
}
+ // Paper start - recalculate header on header corruption
+ // we move the recalc here so comparison to old header is correct when logging to console
+ if (needsHeaderRecalc) { // true if header gave us overlapping allocations or had other issues
+ LOGGER.error("Recalculating regionfile " + this.path.toAbsolutePath() + ", header gave erroneous offsets & locations");
+ this.recalculateHeader();
+ }
+ // Paper end
}
+ // Paper start - recalculate header on header corruption
+ // we move the recalc here so comparison to old header is correct when logging to console
+ if (needsHeaderRecalc) { // true if header gave us overlapping allocations or had other issues
+ LOGGER.error("Recalculating regionfile {}, header gave erroneous offsets & locations", this.path.toAbsolutePath());
+ this.recalculateHeader();
+ }
+ // Paper end
}
}
@@ -131,10 +535,34 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
@@ -130,10 +534,34 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
}
private Path getExternalChunkPath(final ChunkPos pos) {
@@ -550,77 +550,79 @@ index fc712f9dbc893a6dee8ccecc54d0af07659dee62..b4a59b855b45018853e8122a5829a48e
public synchronized @Nullable DataInputStream getChunkDataInputStream(final ChunkPos pos) throws IOException {
int offset = this.getOffset(pos);
if (offset == 0) {
@@ -155,30 +583,67 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
buffer.flip();
if (buffer.remaining() < 5) {
LOGGER.error("Chunk {} header is truncated: expected {} but read {}", pos, sectorsLength, buffer.remaining());
@@ -155,6 +583,11 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
buffer.flip();
if (buffer.remaining() < 5) {
LOGGER.error("Chunk {} header is truncated: expected {} but read {}", pos, sectorsLength, buffer.remaining());
+ // Paper start - recalculate header on regionfile corruption
+ if (this.canRecalcHeader && this.recalculateHeader()) {
+ return this.getChunkDataInputStream(pos);
+ }
+ // Paper end - recalculate header on regionfile corruption
return null;
}
@@ -162,6 +595,11 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
byte versionId = buffer.get();
if (length == 0) {
LOGGER.warn("Chunk {} is allocated, but stream is missing", pos);
+ // Paper start - recalculate header on regionfile corruption
+ if (this.canRecalcHeader && this.recalculateHeader()) {
+ return this.getChunkDataInputStream(pos);
+ }
+ // Paper end - recalculate header on regionfile corruption
return null;
}
@@ -169,18 +607,45 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
if (isExternalStreamChunk(versionId)) {
if (streamLength != 0) {
LOGGER.warn("Chunk has both internal and external streams");
+ // Paper start - recalculate header on regionfile corruption
+ if (this.canRecalcHeader && this.recalculateHeader()) {
+ return this.getChunkDataInputStream(pos);
+ }
+ // Paper end - recalculate header on regionfile corruption
return null;
} else {
int length = buffer.getInt();
byte versionId = buffer.get();
if (length == 0) {
LOGGER.warn("Chunk {} is allocated, but stream is missing", pos);
+ // Paper start - recalculate header on regionfile corruption
+ if (this.canRecalcHeader && this.recalculateHeader()) {
+ return this.getChunkDataInputStream(pos);
+ }
+ // Paper end - recalculate header on regionfile corruption
return null;
} else {
int streamLength = length - 1;
if (isExternalStreamChunk(versionId)) {
if (streamLength != 0) {
LOGGER.warn("Chunk has both internal and external streams");
+ // Paper start - recalculate header on regionfile corruption
+ if (this.canRecalcHeader && this.recalculateHeader()) {
+ return this.getChunkDataInputStream(pos);
+ }
+ // Paper end - recalculate header on regionfile corruption
}
- return this.createExternalChunkInputStream(pos, getExternalChunkVersion(versionId));
+ // Paper start - recalculate header on regionfile corruption
+ final DataInputStream ret = this.createExternalChunkInputStream(pos, getExternalChunkVersion(versionId));
+ if (ret == null && this.canRecalcHeader && this.recalculateHeader()) {
+ return this.getChunkDataInputStream(pos);
+ }
+ return ret;
+ // Paper end - recalculate header on regionfile corruption
} else if (streamLength > buffer.remaining()) {
LOGGER.error("Chunk {} stream is truncated: expected {} but read {}", pos, streamLength, buffer.remaining());
+ // Paper start - recalculate header on regionfile corruption
+ if (this.canRecalcHeader && this.recalculateHeader()) {
+ return this.getChunkDataInputStream(pos);
+ }
+ // Paper end - recalculate header on regionfile corruption
return null;
} else if (streamLength < 0) {
LOGGER.error("Declared size {} of chunk {} is negative", length, pos);
+ // Paper start - recalculate header on regionfile corruption
+ if (this.canRecalcHeader && this.recalculateHeader()) {
+ return this.getChunkDataInputStream(pos);
+ }
+ // Paper end - recalculate header on regionfile corruption
return null;
} else {
JvmProfiler.INSTANCE.onRegionFileRead(this.info, pos, this.version, streamLength);
- return this.createChunkInputStream(pos, versionId, createStream(buffer, streamLength));
+ // Paper start - recalculate header on regionfile corruption
+ final DataInputStream ret = this.createChunkInputStream(pos, versionId, createStream(buffer, streamLength));
+ if (ret == null && this.canRecalcHeader && this.recalculateHeader()) {
+ return this.getChunkDataInputStream(pos);
+ }
+ return ret;
+ // Paper end - recalculate header on regionfile corruption
}
}
}
@@ -358,9 +823,14 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
- return this.createExternalChunkInputStream(pos, getExternalChunkVersion(versionId));
+ // Paper start - recalculate header on regionfile corruption
+ final DataInputStream ret = this.createExternalChunkInputStream(pos, getExternalChunkVersion(versionId));
+ if (ret == null && this.canRecalcHeader && this.recalculateHeader()) {
+ return this.getChunkDataInputStream(pos);
+ }
+ return ret;
+ // Paper end - recalculate header on regionfile corruption
} else if (streamLength > buffer.remaining()) {
LOGGER.error("Chunk {} stream is truncated: expected {} but read {}", pos, streamLength, buffer.remaining());
+ // Paper start - recalculate header on regionfile corruption
+ if (this.canRecalcHeader && this.recalculateHeader()) {
+ return this.getChunkDataInputStream(pos);
+ }
+ // Paper end - recalculate header on regionfile corruption
return null;
} else if (streamLength < 0) {
LOGGER.error("Declared size {} of chunk {} is negative", length, pos);
+ // Paper start - recalculate header on regionfile corruption
+ if (this.canRecalcHeader && this.recalculateHeader()) {
+ return this.getChunkDataInputStream(pos);
+ }
+ // Paper end - recalculate header on regionfile corruption
return null;
} else {
JvmProfiler.INSTANCE.onRegionFileRead(this.info, pos, this.version, streamLength);
- return this.createChunkInputStream(pos, versionId, createStream(buffer, streamLength));
+ // Paper start - recalculate header on regionfile corruption
+ final DataInputStream ret = this.createChunkInputStream(pos, versionId, createStream(buffer, streamLength));
+ if (ret == null && this.canRecalcHeader && this.recalculateHeader()) {
+ return this.getChunkDataInputStream(pos);
+ }
+ return ret;
+ // Paper end - recalculate header on regionfile corruption
}
}
@@ -357,9 +822,14 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
}
private ByteBuffer createExternalStub() {
@@ -637,7 +639,7 @@ index fc712f9dbc893a6dee8ccecc54d0af07659dee62..b4a59b855b45018853e8122a5829a48e
return stub;
}
diff --git a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
index 5fa64ff30b8252f139ca7f77c14352c384c8c0e5..eae4963b6d18c5f92871477b93f81c2c79331cd9 100644
index fedec4d680934730d38d8c5d609fe216938b668a..e47a91b1869801258d0db20c126722fb06522c80 100644
--- a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+++ b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
@@ -24,6 +24,32 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
@@ -739,28 +741,35 @@ index 5fa64ff30b8252f139ca7f77c14352c384c8c0e5..eae4963b6d18c5f92871477b93f81c2c
}
@org.jetbrains.annotations.Contract("_, false -> !null") private @Nullable RegionFile getRegionFile(final ChunkPos pos, boolean existingOnly) throws IOException { // CraftBukkit
@@ -313,6 +365,19 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
}
@@ -307,7 +359,25 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
// Paper end
var4 = NbtIo.read(regionChunkInputStream);
try (DataInputStream regionChunkInputStream = region.getChunkDataInputStream(pos)) {
- return regionChunkInputStream == null ? null : NbtIo.read(regionChunkInputStream);
+ // Paper start - recover from corrupt regionfile header
+ if (regionChunkInputStream == null) {
+ return null;
+ }
+
+ final CompoundTag serialisedChunkData = NbtIo.read(regionChunkInputStream);
+ if (this.isChunkData) {
+ ChunkPos headerChunkPos = SerializableChunkData.getChunkCoordinate(var4);
+ ChunkPos headerChunkPos = SerializableChunkData.getChunkCoordinate(serialisedChunkData);
+ if (!headerChunkPos.equals(pos)) {
+ net.minecraft.server.MinecraftServer.LOGGER.error("Attempting to read chunk data at " + pos + " but got chunk data for " + headerChunkPos + " instead! Attempting regionfile recalculation for regionfile " + region.getPath().toAbsolutePath());
+ LOGGER.error("Attempting to read chunk data at {} but got chunk data for {} instead! Attempting regionfile recalculation for regionfile {}", pos, headerChunkPos, region.getPath().toAbsolutePath());
+ if (region.recalculateHeader()) {
+ return this.read(pos);
+ }
+ net.minecraft.server.MinecraftServer.LOGGER.error("Can't recalculate regionfile header, regenerating chunk " + pos + " for " + region.getPath().toAbsolutePath());
+ LOGGER.error("Can't recalculate regionfile header, regenerating chunk {} for {}", pos, region.getPath().toAbsolutePath());
+ return null;
+ }
+ }
+ return serialisedChunkData;
+ // Paper end - recover from corrupt regionfile header
}
}
return var4;
diff --git a/net/minecraft/world/level/chunk/storage/RegionFileVersion.java b/net/minecraft/world/level/chunk/storage/RegionFileVersion.java
index 97d3da90c270a06d85e70d261658f71d97ae4e3c..11b65c688144e7ef5b7cba603949843a11ccd084 100644
index ea229f63f81c5638ded74c28e1e13d8948f2e5e0..d991fccfc3d2cc51611f802a6d40429024e21d9a 100644
--- a/net/minecraft/world/level/chunk/storage/RegionFileVersion.java
+++ b/net/minecraft/world/level/chunk/storage/RegionFileVersion.java
@@ -21,7 +21,7 @@ import org.slf4j.Logger;
@@ -771,7 +780,7 @@ index 97d3da90c270a06d85e70d261658f71d97ae4e3c..11b65c688144e7ef5b7cba603949843a
+ public static final Int2ObjectMap<RegionFileVersion> VERSIONS = new Int2ObjectOpenHashMap<>(); // Paper - private -> public
private static final Object2ObjectMap<String, RegionFileVersion> VERSIONS_BY_NAME = new Object2ObjectOpenHashMap<>();
public static final RegionFileVersion VERSION_GZIP = register(
new RegionFileVersion(1, null, in -> new FastBufferedInputStream(new GZIPInputStream(in)), out -> new BufferedOutputStream(new GZIPOutputStream(out)))
new RegionFileVersion(1, "gzip", in -> new FastBufferedInputStream(new GZIPInputStream(in)), out -> new BufferedOutputStream(new GZIPOutputStream(out))) // Paper - Enable for Configurable region compression format
diff --git a/net/minecraft/world/level/chunk/storage/RegionStorageInfo.java b/net/minecraft/world/level/chunk/storage/RegionStorageInfo.java
index 590bd1ca60956e5c5be58481f97401e55f9e1aeb..4e0c9fa1eecfa9f21285b7911e25830bbcbc8d86 100644
--- a/net/minecraft/world/level/chunk/storage/RegionStorageInfo.java
@@ -791,7 +800,7 @@ index 590bd1ca60956e5c5be58481f97401e55f9e1aeb..4e0c9fa1eecfa9f21285b7911e25830b
return new RegionStorageInfo(this.level, this.dimension, this.type + suffix);
}
diff --git a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
index 22a83f312d357176a5489e5b6b71c3ef3e737df7..74158a5d7ea8058c99ab215265987746aa043fc7 100644
index 2d326c3090a5d7621350347e131ee31d4715d3a7..98fb7a282a33f4391de124ed9be9bcf069178516 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(
@@ -813,7 +822,7 @@ index 22a83f312d357176a5489e5b6b71c3ef3e737df7..74158a5d7ea8058c99ab215265987746
// Paper start - Do not let the server load chunks from newer versions
private static final int CURRENT_DATA_VERSION = net.minecraft.SharedConstants.getCurrentVersion().dataVersion().version();
@@ -544,7 +556,7 @@ public record SerializableChunkData(
@@ -542,7 +554,7 @@ public record SerializableChunkData(
tag.putInt("xPos", this.chunkPos.x());
tag.putInt("yPos", this.minSectionY);
tag.putInt("zPos", this.chunkPos.z());
@@ -823,7 +832,7 @@ index 22a83f312d357176a5489e5b6b71c3ef3e737df7..74158a5d7ea8058c99ab215265987746
tag.putString("Status", BuiltInRegistries.CHUNK_STATUS.getKey(this.chunkStatus).toString());
tag.storeNullable("blending_data", BlendingData.Packed.CODEC, this.blendingData);
diff --git a/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java b/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
index 884fa9545a93008fa92863c845aef8f18d88e25a..15da3686bb1b5fddd388a88a7b300aa221c83f58 100644
index df4f03f3a346e0ab0074757b3a077b2407135ac3..0b0265c8d4cb70aef758c1b443d007ef8d6c282b 100644
--- a/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
+++ b/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
@@ -34,6 +34,7 @@ public class SimpleRegionStorage implements ca.spottedleaf.moonrise.patches.chun
@@ -5,10 +5,10 @@ Subject: [PATCH] Incremental chunk and player saving
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 9a014a1a198293c9ce02354d8128cb19666055b8..c6691f02a1b2c1540b959fcc07eb8390906541ec 100644
index 49be7709026cd75cfcb31715550387f0ba954a16..f9da26656b18327e3bcc1537a5f51e84b32cc43a 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -958,7 +958,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -961,7 +961,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
public boolean saveAllChunks(final boolean silent, final boolean flush, final boolean force, final boolean close) {
// Paper end - add close param
@@ -17,7 +17,7 @@ index 9a014a1a198293c9ce02354d8128cb19666055b8..c6691f02a1b2c1540b959fcc07eb8390
boolean result = false;
for (ServerLevel level : this.getAllLevels()) {
@@ -970,13 +970,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -973,13 +973,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
result = true;
}
@@ -32,7 +32,7 @@ index 9a014a1a198293c9ce02354d8128cb19666055b8..c6691f02a1b2c1540b959fcc07eb8390
if (flush) {
for (ServerLevel level : this.getAllLevels()) {
@@ -990,11 +984,25 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -993,10 +987,24 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
return result;
}
@@ -51,14 +51,13 @@ index 9a014a1a198293c9ce02354d8128cb19666055b8..c6691f02a1b2c1540b959fcc07eb8390
+ // Paper end - split out global level data
+
public boolean saveEverything(final boolean silent, final boolean flush, final boolean force) {
boolean var5;
try {
this.isSaving = true;
- this.getPlayerList().saveAll();
+ this.getPlayerList().saveAll(); // Paper - Incremental chunk and player saving; diff on change
boolean result = this.saveAllChunks(silent, flush, force);
this.warnOnLowDiskSpace();
var5 = result;
return result;
@@ -1641,9 +1649,31 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
@@ -94,10 +93,10 @@ index 9a014a1a198293c9ce02354d8128cb19666055b8..c6691f02a1b2c1540b959fcc07eb8390
ProfilerFiller profiler = Profiler.get();
this.server.spark.executeMainThreadTasks(); // Paper - spark
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 2e76ab96c66fd39b5e6619c3dbc75559d5c9b04a..b4a3c61a370939db94d57a911cfa5cfa1008a93a 100644
index 965d9f515d0410df3395d07c6cf5e6349c75c923..49f688e3536fe4fdb052bbfeeec36b5b8666b9b1 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -1449,6 +1449,15 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -1451,6 +1451,15 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
public boolean mayInteract(final Entity entity, final BlockPos pos) {
return !(entity instanceof Player player && (this.server.isUnderSpawnProtection(this, pos, player) || !this.getWorldBorder().isWithinBounds(pos)));
}
@@ -114,10 +113,10 @@ index 2e76ab96c66fd39b5e6619c3dbc75559d5c9b04a..b4a3c61a370939db94d57a911cfa5cfa
public void save(final @Nullable ProgressListener progressListener, final boolean flush, final boolean noSave) {
// Paper start - add close param
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index 41816ca8d4ef3d959d2b88cd4b2eedaaa541e612..82e87270d1ad89ad7aa56e028ced6b998ea0eca5 100644
index f35f88da81abb14b393d6fce04054ee40e8d8397..ec4ad547c2d09f7313390ac6b8a91809baab5f2e 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -205,6 +205,7 @@ import org.slf4j.Logger;
@@ -204,6 +204,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();
@@ -126,10 +125,10 @@ index 41816ca8d4ef3d959d2b88cd4b2eedaaa541e612..82e87270d1ad89ad7aa56e028ced6b99
private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_Y = 10;
private static final int FLY_STAT_RECORDING_SPEED = 25;
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index 0613b053f4a75c0884280011ee092d9d108b3857..b69bcaa1e27a4fbe9a9568f3c9e1843167abe1f5 100644
index eb41400b8948d3eecc6fd89e2fefbe638ccbaad1..3c38b8cadcab6575fca0b7c3cf61287c3793f00f 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -418,6 +418,7 @@ public abstract class PlayerList {
@@ -413,6 +413,7 @@ public abstract class PlayerList {
protected void save(final ServerPlayer player) {
if (!player.getBukkitEntity().isPersistent()) return; // CraftBukkit
@@ -137,7 +136,7 @@ index 0613b053f4a75c0884280011ee092d9d108b3857..b69bcaa1e27a4fbe9a9568f3c9e18431
this.playerIo.save(player);
ServerStatsCounter stats = player.getStats(); // CraftBukkit
if (stats != null) {
@@ -951,10 +952,24 @@ public abstract class PlayerList {
@@ -947,10 +948,24 @@ public abstract class PlayerList {
}
public void saveAll() {
@@ -6,10 +6,10 @@ Subject: [PATCH] Optimise collision checking in player move packet handling
Move collision logic to just the hasNewCollision call instead of getCubes + hasNewCollision
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 05a700ffc844b41f4fa129226f78dc6c52520f32..1d93a864c5fc040b5ec7c48b4db0bd54cd6255ff 100644
index 2e9b47e87a1dc9f0713420414eda71eb6c03d898..3527559c9520e07646c7fb63fe27ad1b14f28da4 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -634,6 +634,7 @@ public class ServerGamePacketListenerImpl
@@ -633,6 +633,7 @@ public class ServerGamePacketListenerImpl
}
vehicle.move(MoverType.PLAYER, new Vec3(xDist, yDist, zDist));
@@ -17,7 +17,7 @@ index 05a700ffc844b41f4fa129226f78dc6c52520f32..1d93a864c5fc040b5ec7c48b4db0bd54
double oyDist = yDist;
xDist = targetX - vehicle.getX();
yDist = targetY - vehicle.getY();
@@ -649,7 +650,17 @@ public class ServerGamePacketListenerImpl
@@ -648,7 +649,17 @@ public class ServerGamePacketListenerImpl
LOGGER.warn("{} (vehicle of {}) moved wrongly! {}", vehicle.getPlainTextName(), this.player.getPlainTextName(), Math.sqrt(movedDist));
}
@@ -36,7 +36,7 @@ index 05a700ffc844b41f4fa129226f78dc6c52520f32..1d93a864c5fc040b5ec7c48b4db0bd54
vehicle.absSnapTo(oldX, oldY, oldZ, targetYRot, targetXRot);
this.send(ClientboundMoveVehiclePacket.fromEntity(vehicle));
vehicle.removeLatestMovementRecording();
@@ -728,9 +739,32 @@ public class ServerGamePacketListenerImpl
@@ -727,9 +738,32 @@ public class ServerGamePacketListenerImpl
}
private boolean noBlocksAround(final Entity entity) {
@@ -72,7 +72,7 @@ index 05a700ffc844b41f4fa129226f78dc6c52520f32..1d93a864c5fc040b5ec7c48b4db0bd54
}
@Override
@@ -1542,7 +1576,7 @@ public class ServerGamePacketListenerImpl
@@ -1541,7 +1575,7 @@ public class ServerGamePacketListenerImpl
}
}
@@ -81,7 +81,7 @@ index 05a700ffc844b41f4fa129226f78dc6c52520f32..1d93a864c5fc040b5ec7c48b4db0bd54
xDist = targetX - this.lastGoodX; // Paper - diff on change, used for checking large move vectors above
yDist = targetY - this.lastGoodY; // Paper - diff on change, used for checking large move vectors above
zDist = targetZ - this.lastGoodZ; // Paper - diff on change, used for checking large move vectors above
@@ -1581,6 +1615,7 @@ public class ServerGamePacketListenerImpl
@@ -1580,6 +1614,7 @@ public class ServerGamePacketListenerImpl
boolean playerStandsOnSomething = this.player.verticalCollisionBelow;
this.player.move(MoverType.PLAYER, new Vec3(xDist, yDist, zDist));
this.player.onGround = packet.isOnGround(); // CraftBukkit - SPIGOT-5810, SPIGOT-5835, SPIGOT-6828: reset by this.player.move
@@ -89,7 +89,7 @@ index 05a700ffc844b41f4fa129226f78dc6c52520f32..1d93a864c5fc040b5ec7c48b4db0bd54
// Paper start - prevent position desync
if (this.awaitingPositionFromClient != null) {
return; // ... thanks Mojang for letting move calls teleport across dimensions.
@@ -1614,7 +1649,17 @@ public class ServerGamePacketListenerImpl
@@ -1613,7 +1648,17 @@ public class ServerGamePacketListenerImpl
}
// Paper start - Add fail move event
@@ -108,7 +108,7 @@ index 05a700ffc844b41f4fa129226f78dc6c52520f32..1d93a864c5fc040b5ec7c48b4db0bd54
if (!allowMovement) {
io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.CLIPPED_INTO_BLOCK,
targetX, targetY, targetZ, targetYRot, targetXRot, false);
@@ -1752,7 +1797,7 @@ public class ServerGamePacketListenerImpl
@@ -1753,7 +1798,7 @@ public class ServerGamePacketListenerImpl
private boolean updateAwaitingTeleport() {
if (this.awaitingPositionFromClient != null) {
@@ -117,7 +117,7 @@ index 05a700ffc844b41f4fa129226f78dc6c52520f32..1d93a864c5fc040b5ec7c48b4db0bd54
this.awaitingTeleportTime = this.tickCount;
this.teleport(
this.awaitingPositionFromClient.x,
@@ -1771,6 +1816,34 @@ public class ServerGamePacketListenerImpl
@@ -1772,6 +1817,34 @@ public class ServerGamePacketListenerImpl
}
}
@@ -13,10 +13,10 @@ average over the last 5 seconds of keepalive transactions.
diff --git a/io/papermc/paper/util/KeepAlive.java b/io/papermc/paper/util/KeepAlive.java
new file mode 100644
index 0000000000000000000000000000000000000000..4a2520f554c2ee74faf86d7c93baccf0f391a6b3
index 0000000000000000000000000000000000000000..66d6af2833d76af28fbeb2ce70c194a559572460
--- /dev/null
+++ b/io/papermc/paper/util/KeepAlive.java
@@ -0,0 +1,67 @@
@@ -0,0 +1,84 @@
+package io.papermc.paper.util;
+
+public class KeepAlive {
@@ -34,6 +34,14 @@ index 0000000000000000000000000000000000000000..4a2520f554c2ee74faf86d7c93baccf0
+ public final PingCalculator pingCalculator1m = new PingCalculator(java.util.concurrent.TimeUnit.MINUTES.toNanos(1L));
+ public final PingCalculator pingCalculator5s = new PingCalculator(java.util.concurrent.TimeUnit.SECONDS.toNanos(5L));
+
+ public KeepAlive copyForListenerHandoff() {
+ KeepAlive copy = new KeepAlive();
+ copy.lastKeepAliveTx = this.lastKeepAliveTx;
+ copy.pingCalculator1m.copyFrom(this.pingCalculator1m);
+ copy.pingCalculator5s.copyFrom(this.pingCalculator5s);
+ return copy;
+ }
+
+ public static final class PingCalculator {
+
+ private final long intervalNS;
@@ -47,6 +55,15 @@ index 0000000000000000000000000000000000000000..4a2520f554c2ee74faf86d7c93baccf0
+ this.intervalNS = intervalNS;
+ }
+
+ private void copyFrom(PingCalculator other) {
+ for (KeepAliveResponse response : other.responses) {
+ this.responses.add(response);
+ }
+ this.timeSumNS = other.timeSumNS;
+ this.timeSumCount = other.timeSumCount;
+ this.lastAverageNS = other.lastAverageNS;
+ }
+
+ public void update(KeepAliveResponse response) {
+ long currTime = response.txTimeNS;
+
@@ -100,7 +117,7 @@ index d87a00ee3cd04ce25dbe46ae6b386b7f0799102c..ace440c2b82a6368aa27a85ff433ba2c
}
}
diff --git a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
index f54d99c59bbca7fb5e0615f8fee9c3f6fbffa18a..d9b6f3826e2126d339e926afed782f6d0e890508 100644
index f9be3e8cbc05fe74ac7816f8b2d81d52f6f23e40..8b194a59f79eea32c9e7a46227034942f6a6e816 100644
--- a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
@@ -39,12 +39,13 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
@@ -121,7 +138,7 @@ index f54d99c59bbca7fb5e0615f8fee9c3f6fbffa18a..d9b6f3826e2126d339e926afed782f6d
private volatile boolean suspendFlushingOnServerThread = false;
// CraftBukkit start
public final org.bukkit.craftbukkit.CraftServer cserver;
@@ -61,13 +62,14 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
@@ -60,13 +61,14 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
public ServerCommonPacketListenerImpl(final MinecraftServer server, final Connection connection, final CommonListenerCookie cookie) {
this.server = server;
this.connection = connection;
@@ -137,7 +154,7 @@ index f54d99c59bbca7fb5e0615f8fee9c3f6fbffa18a..d9b6f3826e2126d339e926afed782f6d
// Paper end
}
@@ -100,13 +102,41 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
@@ -99,13 +101,35 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
@Override
public void handleKeepAlive(final ServerboundKeepAlivePacket packet) {
@@ -167,25 +184,19 @@ index f54d99c59bbca7fb5e0615f8fee9c3f6fbffa18a..d9b6f3826e2126d339e926afed782f6d
+ if (ka.challengeId() == packet.getId()) {
+ itr.remove();
+
+ if (!this.processedDisconnect) {
+ LOGGER.info("Disconnecting {} for sending keepalive response ({}) out-of-order!", this.playerProfile().name(), packet.getId());
+ this.disconnectAsync(TIMEOUT_DISCONNECTION_MESSAGE, io.papermc.paper.connection.DisconnectionReason.TIMEOUT);
+ return;
+ }
+ break;
+ LOGGER.info("Disconnecting {} for sending keepalive response ({}) out-of-order!", this.playerProfile().name(), packet.getId());
+ this.disconnectAsync(TIMEOUT_DISCONNECTION_MESSAGE, io.papermc.paper.connection.DisconnectionReason.TIMEOUT);
+ return;
+ }
}
+
+ if (!this.processedDisconnect) {
+ LOGGER.info("Disconnecting {} for sending keepalive response ({}) without matching challenge!", this.playerProfile().name(), packet.getId());
+ this.disconnectAsync(TIMEOUT_DISCONNECTION_MESSAGE, io.papermc.paper.connection.DisconnectionReason.TIMEOUT);
+ return;
+ }
+ LOGGER.info("Disconnecting {} for sending keepalive response ({}) without matching challenge!", this.playerProfile().name(), packet.getId());
+ this.disconnectAsync(TIMEOUT_DISCONNECTION_MESSAGE, io.papermc.paper.connection.DisconnectionReason.TIMEOUT);
+ // Paper end - improve keepalives
}
@Override
@@ -233,20 +263,23 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
@@ -232,20 +256,23 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
protected void keepConnectionAlive() {
Profiler.get().push("keepAlive");
long now = Util.getMillis();
@@ -194,7 +205,7 @@ index f54d99c59bbca7fb5e0615f8fee9c3f6fbffa18a..d9b6f3826e2126d339e926afed782f6d
- final long elapsedTime = now - this.keepAliveTime;
- if (!this.isSingleplayerOwner() && elapsedTime >= 15000L) { // use vanilla's 15000L between keep alive packets
- if (this.keepAlivePending) {
- if (!this.processedDisconnect && elapsedTime >= KEEPALIVE_LIMIT) { // check keepalive limit, don't fire if already disconnected
- if (elapsedTime >= KEEPALIVE_LIMIT) { // check keepalive limit, don't fire if already disconnected
- this.disconnect(TIMEOUT_DISCONNECTION_MESSAGE, io.papermc.paper.connection.DisconnectionReason.TIMEOUT); // Paper - kick event cause
- }
- // Paper end - give clients a longer time to respond to pings as per pre 1.12.2 timings
@@ -204,7 +215,7 @@ index f54d99c59bbca7fb5e0615f8fee9c3f6fbffa18a..d9b6f3826e2126d339e926afed782f6d
- this.keepAliveChallenge = now;
- this.send(new ClientboundKeepAlivePacket(this.keepAliveChallenge));
+ // Paper start - improve keepalives
+ if (this.checkIfClosed(now) && !this.processedDisconnect) {
+ if (this.checkIfClosed(now)) {
+ long currTime = System.nanoTime();
+
+ if ((currTime - this.keepAlive.lastKeepAliveTx) >= java.util.concurrent.TimeUnit.SECONDS.toNanos(1L)) {
@@ -223,11 +234,21 @@ index f54d99c59bbca7fb5e0615f8fee9c3f6fbffa18a..d9b6f3826e2126d339e926afed782f6d
}
}
@@ -424,6 +457,6 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
@@ -420,6 +447,16 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
}
protected CommonListenerCookie createCookie(final ClientInformation clientInformation) {
- return new CommonListenerCookie(this.playerProfile(), this.latency, clientInformation, this.transferred, this.playerBrand, this.pluginMessagerChannels); // Paper
+ return new CommonListenerCookie(this.playerProfile(), this.latency, clientInformation, this.transferred, this.playerBrand, this.pluginMessagerChannels, this.keepAlive); // Paper // Paper
+ // Paper start - listener handoff should reset pending keepalive expectations
+ return new CommonListenerCookie(
+ this.playerProfile(),
+ this.latency,
+ clientInformation,
+ this.transferred,
+ this.playerBrand,
+ this.pluginMessagerChannels,
+ this.keepAlive.copyForListenerHandoff()
+ );
+ // Paper end - listener handoff should reset pending keepalive expectations
}
}
@@ -20,11 +20,11 @@ index 2bc436cdf5180a7943c45fabb9fbbedae6f7db56..f312a7f5b1b2a777ab36b94ce7cbf387
@Override
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index a3edf217dd2ffce19dc1111402975ba12bb86bad..2b6717833b56c3e33f3ac656a376dd5c841dc1bc 100644
index f9da26656b18327e3bcc1537a5f51e84b32cc43a..e69b540be1e0f0c699a57e07057cee74d4f8a771 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1784,32 +1784,22 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
return new ServerStatus.Players(maxPlayers, players.size(), sample);
}
+ public final io.papermc.paper.threadedregions.EntityScheduler.EntitySchedulerTickList entitySchedulerTickList = new io.papermc.paper.threadedregions.EntityScheduler.EntitySchedulerTickList(); // Paper - optimise Folia entity scheduler
@@ -66,10 +66,10 @@ index a3edf217dd2ffce19dc1111402975ba12bb86bad..2b6717833b56c3e33f3ac656a376dd5c
io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.ADVENTURE_CLICK_MANAGER.handleQueue(this.tickCount); // Paper
io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.DIALOG_CLICK_MANAGER.handleQueue(this.tickCount); // Paper
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 93d3fb313d538cc1067b1db010458169a1320122..9cd46a8619e575a865f4d00ce71d249c9b028b2f 100644
index 5e030ff471248f501d8bca6cfa6cc4161a580d4d..5d4ae462c60723291b1c72dc5b933cd04d31133b 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -5147,6 +5147,11 @@ public abstract class Entity
@@ -5148,6 +5148,11 @@ public abstract class Entity
this.getBukkitEntity().taskScheduler.retire();
}
// Paper end - Folia schedulers
@@ -42,10 +42,10 @@ index d2877c20f389d0131e1dd208b464f590671e5d82..27bdc70d861ca39487ad16cb3afb89d6
updateComponent(root, "minecraft:trim", hiddenComponents);
updateComponent(root, "minecraft:unbreakable", hiddenComponents);
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index ae140a514e1e3200f52c5933f886cc9e820b818a..4a1146cc54bd9272466ffc9bb64f600c40f0c0f4 100644
index 3527559c9520e07646c7fb63fe27ad1b14f28da4..810b352503cdf56091ec6340ad72edde238fe359 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -1820,9 +1820,14 @@ public class ServerGamePacketListenerImpl
@@ -1821,9 +1821,14 @@ public class ServerGamePacketListenerImpl
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<>();
@@ -62,7 +62,7 @@ index ae140a514e1e3200f52c5933f886cc9e820b818a..4a1146cc54bd9272466ffc9bb64f600c
);
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index 8df070a7135411806d42ea692b566c4d8389a47b..06451a21c4bfbcd95063f96246013d63f65af8a3 100644
index 3d20ef3bc4e891e3e6e5f9f6f5efc6c0367369ea..62c8650462a866dcdb3f06f781c7a8c716b0d912 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -1528,7 +1528,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
@@ -5,7 +5,7 @@ Subject: [PATCH] Optional per player mob spawns
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
index be28a41084d656c44b611cf3a01ba89de610d5b2..15d9c2d601a48d2288d5110f9991280129f43087 100644
index 55cfe38c0daa3ca71a71bf8c7d88fd613fabd82d..2afc89ab4404cf04ba1da813f1c4a05baa4f44e3 100644
--- a/net/minecraft/server/level/ChunkMap.java
+++ b/net/minecraft/server/level/ChunkMap.java
@@ -238,11 +238,29 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP
@@ -42,10 +42,10 @@ index be28a41084d656c44b611cf3a01ba89de610d5b2..15d9c2d601a48d2288d5110f99912801
protected ChunkGenerator generator() {
return this.worldGenContext.generator();
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
index fcb23814c2539f4e31ed5191e03cc28f4e7f9c2d..0edfb9866ee2d0ff71394bc74f139fe3b4550c42 100644
index a5685e0b50610cc8213f34c5db62b475bd54789b..a5023f6f7d912560f3488b1823ee22a1dc639203 100644
--- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -537,9 +537,18 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
@@ -535,9 +535,18 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
private void tickChunks(final ProfilerFiller profiler, final long timeDiff) {
profiler.push("naturalSpawnCount");
int chunkCount = this.distanceManager.getNaturalSpawnChunkCount();
@@ -67,7 +67,7 @@ index fcb23814c2539f4e31ed5191e03cc28f4e7f9c2d..0edfb9866ee2d0ff71394bc74f139fe3
this.lastSpawnState = spawnCookie;
boolean doMobSpawning = this.level.getGameRules().get(GameRules.SPAWN_MOBS) && !this.level.players().isEmpty(); // CraftBukkit
int tickSpeed = this.level.getGameRules().get(GameRules.RANDOM_TICK_SPEED);
@@ -567,7 +576,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
@@ -565,7 +574,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
profiler.popPush("shuffleSpawningChunks");
// Paper start - chunk tick iteration optimisation
this.shuffleRandom.setSeed(this.level.getRandom().nextLong());
@@ -77,10 +77,10 @@ index fcb23814c2539f4e31ed5191e03cc28f4e7f9c2d..0edfb9866ee2d0ff71394bc74f139fe3
profiler.popPush("tickSpawningChunks");
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index 82e87270d1ad89ad7aa56e028ced6b998ea0eca5..558abe7248725a58b5aa1a36c7e372824c0fbe2e 100644
index c8dd908ea52305d7ae1235ea430a26c88306f183..85d22ea2e0fb7c59c387df056ace9ef556f489a2 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -430,6 +430,10 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
@@ -411,6 +411,10 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
public boolean queueHealthUpdatePacket;
public net.minecraft.network.protocol.game.@Nullable ClientboundSetHealthPacket queuedHealthUpdatePacket;
// Paper end - cancellable death event
@@ -92,7 +92,7 @@ index 82e87270d1ad89ad7aa56e028ced6b998ea0eca5..558abe7248725a58b5aa1a36c7e37282
public @Nullable String lastKnownName; // Better rename detection
public String displayName;
diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java
index 8ab7bdd487de7dbc5ea19d1f361ec52e367f8251..d9c1a04e53bec4a268ab58bfc8025c00bd3fa8ce 100644
index c0a986932c25d056877e3147d09dd50d1264b13d..b1ecbc50ea908c6c11e9e1ae2f73d1eed669db08 100644
--- a/net/minecraft/world/level/NaturalSpawner.java
+++ b/net/minecraft/world/level/NaturalSpawner.java
@@ -70,6 +70,18 @@ public final class NaturalSpawner {
@@ -6,7 +6,7 @@ Subject: [PATCH] Improve cancelling PreCreatureSpawnEvent with per player mob
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
index 15d9c2d601a48d2288d5110f9991280129f43087..01cbebd70223389c6d952be23e5d739579b96ead 100644
index 2afc89ab4404cf04ba1da813f1c4a05baa4f44e3..42ec67c53c35189838ce4767c2be620e05108159 100644
--- a/net/minecraft/server/level/ChunkMap.java
+++ b/net/minecraft/server/level/ChunkMap.java
@@ -257,8 +257,25 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP
@@ -37,10 +37,10 @@ index 15d9c2d601a48d2288d5110f9991280129f43087..01cbebd70223389c6d952be23e5d7395
// Paper end - Optional per player mob spawns
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
index 0edfb9866ee2d0ff71394bc74f139fe3b4550c42..5a309dcf3c357f6b3a12d74aca2e9592f952318c 100644
index a5023f6f7d912560f3488b1823ee22a1dc639203..0609c4e7ac7d027ed01684ab4fec2a7838f1f30f 100644
--- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -542,7 +542,17 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
@@ -540,7 +540,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()) {
@@ -60,10 +60,10 @@ index 0edfb9866ee2d0ff71394bc74f139fe3b4550c42..5a309dcf3c357f6b3a12d74aca2e9592
spawnCookie = NaturalSpawner.createState(chunkCount, 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 558abe7248725a58b5aa1a36c7e372824c0fbe2e..06b938013f9dbee4fb3b86979d5a42f28a0ea33e 100644
index 85d22ea2e0fb7c59c387df056ace9ef556f489a2..d354517cbfdcee0648daa8f3eb035b2024d5ce72 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -434,6 +434,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
@@ -415,6 +415,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
@@ -72,7 +72,7 @@ index 558abe7248725a58b5aa1a36c7e372824c0fbe2e..06b938013f9dbee4fb3b86979d5a42f2
public @Nullable String lastKnownName; // Better rename detection
public String displayName;
diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java
index d9c1a04e53bec4a268ab58bfc8025c00bd3fa8ce..427573fc142c107d3fcc2be9000316221039ba4c 100644
index b1ecbc50ea908c6c11e9e1ae2f73d1eed669db08..9764163a32b0389afa50ba093d0647de04d5119e 100644
--- a/net/minecraft/world/level/NaturalSpawner.java
+++ b/net/minecraft/world/level/NaturalSpawner.java
@@ -307,6 +307,11 @@ public final class NaturalSpawner {
@@ -48,10 +48,10 @@ index 0000000000000000000000000000000000000000..24a2090e068ad3c0d08705050944abdf
+ }
+}
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index fbd70bcdc2efb42505fd5b3e5a6f703782c4060d..000943733f07cef2e508f78aadf5d28dec70e90c 100644
index e69b540be1e0f0c699a57e07057cee74d4f8a771..cc35c6271c98615a486422d0490145227626bd85 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1836,6 +1836,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1840,6 +1840,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
level.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - BlockPhysicsEvent
level.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent
level.updateLagCompensationTick(); // Paper - lag compensation
@@ -60,10 +60,10 @@ index fbd70bcdc2efb42505fd5b3e5a6f703782c4060d..000943733f07cef2e508f78aadf5d28d
profiler.push("tick");
diff --git a/net/minecraft/world/item/ItemStack.java b/net/minecraft/world/item/ItemStack.java
index ee0fde839f06b6b900053b792617f3738b8000e8..54d67c847c1de9ee216cedb0527136f50223ea2d 100644
index 95e0fb32457f2113de8d3e78fc18398bec83eccf..1d08ac66271a441811a157ed6619b14bf58c403f 100644
--- a/net/minecraft/world/item/ItemStack.java
+++ b/net/minecraft/world/item/ItemStack.java
@@ -807,10 +807,16 @@ public final class ItemStack implements DataComponentHolder, ItemInstance {
@@ -807,11 +807,17 @@ public final class ItemStack implements DataComponentHolder, ItemInstance {
}
public ItemStack copy() {
@@ -76,17 +76,18 @@ index ee0fde839f06b6b900053b792617f3738b8000e8..54d67c847c1de9ee216cedb0527136f5
+ if (!originalItem && this.isEmpty()) {
+ // Paper end - Perf: Optimize Hoppers
return EMPTY;
} else {
- ItemStack copy = new ItemStack(this.typeHolder(), this.count, this.components.copy());
+ ItemStack copy = new ItemStack(originalItem ? this.item : this.typeHolder(), this.count, this.components.copy()); // Paper - Perf: Optimize Hoppers
copy.setPopTime(this.getPopTime());
return copy;
}
- ItemStack copy = new ItemStack(this.typeHolder(), this.count, this.components.copy());
+ ItemStack copy = new ItemStack(originalItem ? this.item : this.typeHolder(), this.count, this.components.copy()); // Paper - Perf: Optimize Hoppers
copy.setPopTime(this.getPopTime());
return copy;
}
diff --git a/net/minecraft/world/level/block/entity/BlockEntity.java b/net/minecraft/world/level/block/entity/BlockEntity.java
index 04cfd03f29b319225011b86d459626a6872a5e87..865f08c92946010a36947b7766b957f4c0517285 100644
index 0e2b537ff5230e6bdb55e9e99d6b2202479b8fd2..2c9fb60cae8065b1cd70d6ec53122cec9cf7f2a7 100644
--- a/net/minecraft/world/level/block/entity/BlockEntity.java
+++ b/net/minecraft/world/level/block/entity/BlockEntity.java
@@ -40,6 +40,7 @@ import org.jspecify.annotations.Nullable;
@@ -39,6 +39,7 @@ import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
public abstract class BlockEntity implements DebugValueSource, TypedInstance<BlockEntityType<?>> {
@@ -94,7 +95,7 @@ index 04cfd03f29b319225011b86d459626a6872a5e87..865f08c92946010a36947b7766b957f4
// CraftBukkit start - data containers
private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry();
public final org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer persistentDataContainer;
@@ -229,6 +230,7 @@ public abstract class BlockEntity implements DebugValueSource, TypedInstance<Blo
@@ -214,6 +215,7 @@ public abstract class BlockEntity implements DebugValueSource, TypedInstance<Blo
public void setChanged() {
if (this.level != null) {
@@ -103,7 +104,7 @@ index 04cfd03f29b319225011b86d459626a6872a5e87..865f08c92946010a36947b7766b957f4
}
}
diff --git a/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/net/minecraft/world/level/block/entity/HopperBlockEntity.java
index 0703e50ca36fbc084ed06811678efd0eb7d26455..085ed0eecc47cf9c0fa9fc26b3f7b8522610a941 100644
index 920eb74888c1692b93eb060f408004f145489d7c..ce6b64e00a0a13ebdceda56d975d79f60c26b86d 100644
--- a/net/minecraft/world/level/block/entity/HopperBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/HopperBlockEntity.java
@@ -142,6 +142,43 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@@ -150,27 +151,29 @@ index 0703e50ca36fbc084ed06811678efd0eb7d26455..085ed0eecc47cf9c0fa9fc26b3f7b852
private static boolean tryMoveItems(
final Level level, final BlockPos pos, final BlockState state, final HopperBlockEntity entity, final BooleanSupplier action
) {
@@ -150,12 +187,13 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
} else {
if (!entity.isOnCooldown() && state.getValue(HopperBlock.ENABLED)) {
boolean changed = false;
- if (!entity.isEmpty()) {
+ final int fullState = getFullState(entity); // Paper - Perf: Optimize Hoppers
+ if (fullState != HOPPER_EMPTY) { // Paper - Perf: Optimize Hoppers
changed = ejectItems(level, pos, entity);
}
@@ -151,11 +188,12 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
- if (!entity.inventoryFull()) {
- changed |= action.getAsBoolean();
+ if (changed || fullState != HOPPER_IS_FULL) { // Paper - Perf: Optimize Hoppers
+ changed |= action.getAsBoolean(); // Paper - note: this is not a validator, it's what adds/sucks in items
}
if (!entity.isOnCooldown() && state.getValue(HopperBlock.ENABLED)) {
boolean changed = false;
- if (!entity.isEmpty()) {
+ final int fullState = getFullState(entity); // Paper - Perf: Optimize Hoppers
+ if (fullState != HOPPER_EMPTY) { // Paper - Perf: Optimize Hoppers
changed = ejectItems(level, pos, entity);
}
if (changed) {
@@ -179,6 +217,206 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
- if (!entity.inventoryFull()) {
+ if (changed || fullState != HOPPER_IS_FULL) { // Paper - Perf: Optimize Hoppers
changed |= action.getAsBoolean();
}
@@ -179,67 +217,218 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
return true;
}
- private static boolean ejectItems(final Level level, final BlockPos blockPos, final HopperBlockEntity self) {
- Container container = getAttachedContainer(level, blockPos, self);
- if (container == null) {
- return false;
+ // Paper start - Perf: Optimize Hoppers
+ public static boolean skipHopperEvents;
+ private static boolean skipPullModeEventFire;
@@ -214,13 +217,16 @@ index 0703e50ca36fbc084ed06811678efd0eb7d26455..085ed0eecc47cf9c0fa9fc26b3f7b852
+ }
+ origItemStack.setCount(originalItemCount);
+ }
+ }
}
+ if (foundItem && level.paperConfig().hopper.cooldownWhenFull) { // Inventory was full - cooldown
+ hopper.setCooldown(level.spigotConfig.hopperTransfer);
+ }
+ return false;
+ }
+
- Direction direction = self.facing.getOpposite();
- if (isFullContainer(container, direction)) {
- 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();
@@ -237,8 +243,27 @@ index 0703e50ca36fbc084ed06811678efd0eb7d26455..085ed0eecc47cf9c0fa9fc26b3f7b852
+ // site for IMIE did not exhibit the same behavior
+ return true;
+ }
+ }
+
}
- for (int slot = 0; slot < self.getContainerSize(); slot++) {
- ItemStack itemStack = self.getItem(slot);
- if (!itemStack.isEmpty()) {
- int originalCount = itemStack.getCount();
- // CraftBukkit start - Call event when pushing items into other inventories
- ItemStack original = itemStack.copy();
- org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(
- self.removeItem(slot, level.spigotConfig.hopperAmount)
- ); // Spigot
-
- org.bukkit.inventory.Inventory destinationInventory;
- // Have to special case large chests as they work oddly
- if (container instanceof final net.minecraft.world.CompoundContainer compoundContainer) {
- destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer);
- } else if (container.getOwner() != null) {
- destinationInventory = container.getOwner().getInventory();
- } else {
- destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventory(container);
- }
+ final ItemStack remainingItem = addItem(container, hopper, movedItem, null);
+ final int remainingItemCount = remainingItem.getCount();
+ if (remainingItemCount != movedItemCount) {
@@ -331,28 +356,49 @@ index 0703e50ca36fbc084ed06811678efd0eb7d26455..085ed0eecc47cf9c0fa9fc26b3f7b852
+ blockEntity.setCooldown(blockEntity.getLevel().spigotConfig.hopperTransfer);
+ }
+ }
+
- org.bukkit.event.inventory.InventoryMoveItemEvent event = new org.bukkit.event.inventory.InventoryMoveItemEvent(
- self.getOwner().getInventory(),
- oitemstack,
- destinationInventory,
- true
- );
- if (!event.callEvent()) {
- self.setItem(slot, original);
- self.setCooldown(level.spigotConfig.hopperTransfer); // Delay hopper checks // Spigot
+ 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 (container instanceof WorldlyContainer worldlyContainer) {
+ for (int slot : worldlyContainer.getSlotsForFace(direction)) {
+ if (!test.test(container.getItem(slot), slot)) {
+ return false;
+ }
return false;
}
- int origCount = event.getItem().getAmount(); // Spigot
- ItemStack result = HopperBlockEntity.addItem(self, container, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()), direction);
- // CraftBukkit end
- if (result.isEmpty()) {
- container.setChanged();
- return true;
+ }
+ } else {
+ int size = container.getContainerSize();
+ for (int slot = 0; slot < size; slot++) {
+ if (!test.test(container.getItem(slot), slot)) {
+ return false;
+ }
}
+ }
+ }
+ return true;
+ }
+
- itemStack.setCount(originalCount);
- // Spigot start
- itemStack.shrink(origCount - result.getCount());
- if (originalCount <= level.spigotConfig.hopperAmount) {
- // Spigot end
- self.setItem(slot, itemStack);
+ 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 (container instanceof WorldlyContainer worldlyContainer) {
+ for (int slot : worldlyContainer.getSlotsForFace(direction)) {
+ if (test.test(container.getItem(slot), slot)) {
+ return true;
+ }
@@ -362,78 +408,32 @@ index 0703e50ca36fbc084ed06811678efd0eb7d26455..085ed0eecc47cf9c0fa9fc26b3f7b852
+ 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(final Level level, final BlockPos blockPos, final HopperBlockEntity self) {
Container container = getAttachedContainer(level, blockPos, self);
if (container == null) {
@@ -188,57 +426,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
if (isFullContainer(container, direction)) {
return false;
} else {
- for (int slot = 0; slot < self.getContainerSize(); slot++) {
- ItemStack itemStack = self.getItem(slot);
- if (!itemStack.isEmpty()) {
- int originalCount = itemStack.getCount();
- // CraftBukkit start - Call event when pushing items into other inventories
- ItemStack original = itemStack.copy();
- org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(
- self.removeItem(slot, level.spigotConfig.hopperAmount)
- ); // Spigot
-
- org.bukkit.inventory.Inventory destinationInventory;
- // Have to special case large chests as they work oddly
- if (container instanceof final net.minecraft.world.CompoundContainer compoundContainer) {
- destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer);
- } else if (container.getOwner() != null) {
- destinationInventory = container.getOwner().getInventory();
- } else {
- destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventory(container);
- }
-
- org.bukkit.event.inventory.InventoryMoveItemEvent event = new org.bukkit.event.inventory.InventoryMoveItemEvent(
- self.getOwner().getInventory(),
- oitemstack,
- destinationInventory,
- true
- );
- if (!event.callEvent()) {
- self.setItem(slot, original);
- self.setCooldown(level.spigotConfig.hopperTransfer); // Delay hopper checks // Spigot
- return false;
- }
- int origCount = event.getItem().getAmount(); // Spigot
- ItemStack result = HopperBlockEntity.addItem(self, container, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()), direction);
- // CraftBukkit end
-
- if (result.isEmpty()) {
- container.setChanged();
- return true;
- }
-
- itemStack.setCount(originalCount);
- // Spigot start
- itemStack.shrink(origCount - result.getCount());
- if (originalCount <= level.spigotConfig.hopperAmount) {
- // Spigot end
- self.setItem(slot, itemStack);
- }
- }
- }
-
- return false;
+ return hopperPush(level, container, direction, self); // Paper - Perf: Optimize Hoppers
}
}
}
+ return true;
+ }
+ private static final java.util.function.BiPredicate<ItemStack, Integer> STACK_SIZE_TEST = (itemStack, _) -> itemStack.getCount() >= itemStack.getMaxStackSize();
+ private static final java.util.function.BiPredicate<ItemStack, Integer> IS_EMPTY_TEST = (itemStack, _) -> itemStack.isEmpty();
+ // Paper end - Perf: Optimize Hoppers
- return false;
+ private static boolean ejectItems(final Level level, final BlockPos blockPos, final HopperBlockEntity self) {
+ Container container = getAttachedContainer(level, blockPos, self);
+ if (container == null) {
+ return false;
+ }
+
+ Direction direction = self.facing.getOpposite();
+ if (isFullContainer(container, direction)) {
+ return false;
+ }
+
+ return hopperPush(level, container, direction, self); // Paper - Perf: Optimize Hoppers
}
@@ -293,6 +481,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
private static int[] getSlots(final Container container, final Direction direction) {
@@ -292,6 +481,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
Container container = getSourceContainer(level, hopper, blockPos, blockState);
if (container != null) {
Direction direction = Direction.DOWN;
@@ -441,7 +441,7 @@ index 0703e50ca36fbc084ed06811678efd0eb7d26455..085ed0eecc47cf9c0fa9fc26b3f7b852
for (int slot : getSlots(container, direction)) {
if (tryTakeInItemFromSlot(hopper, container, slot, direction, level)) { // Spigot
@@ -320,54 +509,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@@ -319,54 +509,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
private static boolean tryTakeInItemFromSlot(final Hopper hopper, final Container container, final int slot, final Direction direction, final Level level) { // Spigot
ItemStack itemStack = container.getItem(slot);
if (!itemStack.isEmpty() && canTakeItemFromContainer(hopper, container, itemStack, slot, direction)) {
@@ -497,7 +497,7 @@ index 0703e50ca36fbc084ed06811678efd0eb7d26455..085ed0eecc47cf9c0fa9fc26b3f7b852
}
return false;
@@ -376,13 +518,15 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@@ -375,13 +518,15 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
public static boolean addItem(final Container container, final ItemEntity entity) {
boolean changed = false;
// CraftBukkit start
@@ -514,7 +514,7 @@ index 0703e50ca36fbc084ed06811678efd0eb7d26455..085ed0eecc47cf9c0fa9fc26b3f7b852
ItemStack copy = entity.getItem().copy();
ItemStack result = addItem(null, container, copy, null);
if (result.isEmpty()) {
@@ -441,7 +585,9 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@@ -440,7 +585,9 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
itemStack = itemStack.split(container.getMaxStackSize());
}
// Spigot end
@@ -524,7 +524,7 @@ index 0703e50ca36fbc084ed06811678efd0eb7d26455..085ed0eecc47cf9c0fa9fc26b3f7b852
itemStack = leftover; // Paper - Make hoppers respect inventory max stack size
success = true;
} else if (canMergeItems(current, itemStack)) {
@@ -527,14 +673,22 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@@ -526,14 +673,22 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
public static @Nullable Container getContainerAt(final Level level, final BlockPos pos) {
@@ -549,7 +549,7 @@ index 0703e50ca36fbc084ed06811678efd0eb7d26455..085ed0eecc47cf9c0fa9fc26b3f7b852
result = getEntityContainer(level, x, y, z);
}
@@ -558,14 +712,14 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@@ -559,14 +714,14 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
private static @Nullable Container getEntityContainer(final Level level, final double x, final double y, final double z) {
@@ -568,7 +568,7 @@ index 0703e50ca36fbc084ed06811678efd0eb7d26455..085ed0eecc47cf9c0fa9fc26b3f7b852
@Override
diff --git a/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java b/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
index 557ef92009f51b54e710f105254eb3e352c6057c..2270b5e5696ef65366c372b9606c5f03b6a11565 100644
index 9ded208a6e3b86e24a36791f8bb74cec08cd6e9c..5019356f5f29be895d1b2ea8ee9a0e0f6f237a6a 100644
--- a/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
@@ -52,7 +52,7 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc
@@ -55,7 +55,7 @@ index cde895c776c3077292c1a8381461075b1fbc45c7..b817e30a23a0030f948ff6c12175767f
if (buffer.writerIndex() != buffer.capacity()) {
diff --git a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
index 488d5654f5414c9950f19e86a791507366366e01..42bfd4b3b626ef66b0f2ef8438add5342b367800 100644
index d7a1dde0452632d46fe594afa158cace71698de8..a52f93d0563f713b1d2b707cdb6ab74e4793a54d 100644
--- a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
+++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
@@ -41,13 +41,23 @@ public class ClientboundLevelChunkPacketData {
@@ -155,10 +155,10 @@ index a94e6ca1d396b6b0781de5d550c4a804274ee003..a1018e9ca9dfb978d6e6633577abd7d5
private ClientboundLevelChunkWithLightPacket(final RegistryFriendlyByteBuf input) {
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index b4a3c61a370939db94d57a911cfa5cfa1008a93a..be0e0b3a70bde1e2217ba4d3f90031e243d86941 100644
index ac3c00ccb3025cd45beb3c6b96f11dad38f78366..a9a00f9be49344b17b27f64834102df3a323f03b 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -630,7 +630,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -631,7 +631,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
savedDataStorage.set(io.papermc.paper.world.saveddata.PaperWorldPDC.TYPE, loadedWorldData.pdc() == null ? io.papermc.paper.world.saveddata.PaperWorldPDC.TYPE.constructor().get() : loadedWorldData.pdc());
final GameRules gameRules = new GameRules(server.getWorldData().enabledFeatures(), savedDataStorage.computeIfAbsent(net.minecraft.world.level.gamerules.GameRuleMap.TYPE));
this.gameRules = gameRules;
@@ -168,10 +168,10 @@ index b4a3c61a370939db94d57a911cfa5cfa1008a93a..be0e0b3a70bde1e2217ba4d3f90031e2
this.weatherData.setLevel(this);
this.typeKey = typeKey;
diff --git a/net/minecraft/server/level/ServerPlayerGameMode.java b/net/minecraft/server/level/ServerPlayerGameMode.java
index f1faa0439bf5bce583e70ce37ade4b7e66daaaa4..f7390b91fe1866c6a11beb93711d0fcde4293164 100644
index 837f8de90fddb5f458d44ee42ea11557463454c3..515dfa9c71333f68d6a42535f98977b5681eb407 100644
--- a/net/minecraft/server/level/ServerPlayerGameMode.java
+++ b/net/minecraft/server/level/ServerPlayerGameMode.java
@@ -321,6 +321,7 @@ public class ServerPlayerGameMode {
@@ -320,6 +320,7 @@ public class ServerPlayerGameMode {
org.bukkit.craftbukkit.event.CraftEventFactory.callBlockDamageAbortEvent(this.player, pos, this.player.getInventory().getSelectedItem()); // CraftBukkit
}
}
@@ -196,10 +196,10 @@ index ec40f02032f965f548b0c0a29aa9d9bbad6f439b..373da9d604cfb1614a0618d856aefd2b
if (io.papermc.paper.event.packet.PlayerChunkLoadEvent.getHandlerList().getRegisteredListeners().length > 0) {
new io.papermc.paper.event.packet.PlayerChunkLoadEvent(new org.bukkit.craftbukkit.CraftChunk(chunk), connection.getPlayer().getBukkitEntity()).callEvent();
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index b69bcaa1e27a4fbe9a9568f3c9e1843167abe1f5..9218322801c41657c00928ef90b0de99df1670f6 100644
index bab8c73be9537a1d531871b1b72bed87d2e0956e..02d19fc610e738f5ed12c23ab92bbdcbabaeced3 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -331,7 +331,7 @@ public abstract class PlayerList {
@@ -330,7 +330,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(level, player.chunkPosition(), plains),
@@ -209,7 +209,7 @@ index b69bcaa1e27a4fbe9a9568f3c9e1843167abe1f5..9218322801c41657c00928ef90b0de99
}
// Paper end - Send empty chunk
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index 630aec19b88a519fa4ec382fb2f5e9aebda59208..f765bc3d805de16909b0407da633208bb0dd9edc 100644
index 5a5f7cbc0217ab7aa034698802f634d149662da1..f4302e835fa9684d0fc7d6b65075ec3ec40f8f9f 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -145,6 +145,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
@@ -230,7 +230,7 @@ index 630aec19b88a519fa4ec382fb2f5e9aebda59208..f765bc3d805de16909b0407da633208b
) {
// Paper start - getblock optimisations - cache world height/sections
final DimensionType dimType = dimensionTypeRegistration.value();
@@ -870,6 +872,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
@@ -871,6 +873,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
this.palettedContainerFactory = PalettedContainerFactory.create(registryAccess);
this.damageSources = new DamageSources(registryAccess);
this.entityLookup = new ca.spottedleaf.moonrise.patches.chunk_system.level.entity.dfl.DefaultEntityLookup(this); // Paper - rewrite chunk system
@@ -238,14 +238,14 @@ index 630aec19b88a519fa4ec382fb2f5e9aebda59208..f765bc3d805de16909b0407da633208b
}
// Paper start - Cancel hit for vanished players
@@ -1084,6 +1087,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
}
// CraftBukkit end - capture blockstates
BlockState oldState = chunk.setBlockState(pos, blockState, updateFlags);
+ this.chunkPacketBlockController.onBlockChange(this, pos, blockState, oldState, updateFlags, updateLimit); // Paper - Anti-Xray
if (oldState == null) {
// CraftBukkit start - remove blockstate if failed (or the same)
if (this.captureBlockStates && captured) {
@@ -1088,6 +1091,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
}
// CraftBukkit end - capture blockstates
BlockState oldState = chunk.setBlockState(pos, blockState, updateFlags);
+ this.chunkPacketBlockController.onBlockChange(this, pos, blockState, oldState, updateFlags, updateLimit); // Paper - Anti-Xray
if (oldState == null) {
// CraftBukkit start - remove blockstate if failed (or the same)
if (this.captureBlockStates && captured) {
diff --git a/net/minecraft/world/level/chunk/ChunkAccess.java b/net/minecraft/world/level/chunk/ChunkAccess.java
index c974b1c276d29610cb59566afaad19f5bcf0c602..c73b9871faa87493e081cb54e1bf1dce487359c6 100644
--- a/net/minecraft/world/level/chunk/ChunkAccess.java
@@ -273,10 +273,10 @@ index c974b1c276d29610cb59566afaad19f5bcf0c602..c73b9871faa87493e081cb54e1bf1dce
}
}
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
index b5b514b292f9fdbe410aba5facec1638682c9075..595e97d2772b504eb6a08e84e4f4a138eb7c5f37 100644
index 536c24ec46bdf571637376728aee06755a6256ef..b381c4d5a8b981f34852d029bbe46374d24f7879 100644
--- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -152,7 +152,7 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
@@ -151,7 +151,7 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
final LevelChunk.@Nullable PostLoadProcessor postLoad,
final @Nullable BlendingData blendingData
) {
@@ -286,10 +286,10 @@ index b5b514b292f9fdbe410aba5facec1638682c9075..595e97d2772b504eb6a08e84e4f4a138
this.gameEventListenerRegistrySections = new Int2ObjectOpenHashMap<>();
diff --git a/net/minecraft/world/level/chunk/LevelChunkSection.java b/net/minecraft/world/level/chunk/LevelChunkSection.java
index 398018ad7037669a756f804403fd76cf333a06de..d274149a977ed828b6bd02d88d2850521ede6c60 100644
index 0e7bb7db9e46b2cade771fa5e94469023d8b47b0..0f807317cc7fa113ee57ab31f91bb6b8df7c4ae0 100644
--- a/net/minecraft/world/level/chunk/LevelChunkSection.java
+++ b/net/minecraft/world/level/chunk/LevelChunkSection.java
@@ -61,8 +61,14 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
@@ -60,8 +60,14 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
this.recalcBlockCounts();
}
@@ -305,7 +305,7 @@ index 398018ad7037669a756f804403fd76cf333a06de..d274149a977ed828b6bd02d88d285052
this.biomes = containerFactory.createForBiomes();
}
@@ -284,11 +290,17 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
@@ -283,11 +289,17 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
this.biomes = biomes;
}
@@ -326,7 +326,7 @@ index 398018ad7037669a756f804403fd76cf333a06de..d274149a977ed828b6bd02d88d285052
public int getSerializedSize() {
diff --git a/net/minecraft/world/level/chunk/PalettedContainer.java b/net/minecraft/world/level/chunk/PalettedContainer.java
index edf2990bfbe3d6b64eed85446eef207d2506efc5..09111c50cb34b643b7717f107a6f5bf6953bf0b2 100644
index 4eeb920e4e71ab91cdb5a41f1eabdd47182c80fb..66a05adab494f8674f0636f8b34533f8ef640028 100644
--- a/net/minecraft/world/level/chunk/PalettedContainer.java
+++ b/net/minecraft/world/level/chunk/PalettedContainer.java
@@ -26,6 +26,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
@@ -421,7 +421,7 @@ index edf2990bfbe3d6b64eed85446eef207d2506efc5..09111c50cb34b643b7717f107a6f5bf6
this.data = this.createOrReuseData(null, 0);
this.data.palette.idFor(initialValue, this);
@@ -123,15 +169,39 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
}
return new PalettedContainer.Data<>(dataConfiguration, storage, palette);
}
+ // Paper start - Anti-Xray
@@ -462,7 +462,7 @@ index edf2990bfbe3d6b64eed85446eef207d2506efc5..09111c50cb34b643b7717f107a6f5bf6
public synchronized T getAndSet(final int x, final int y, final int z, final T value) { // Paper - synchronize
this.acquire();
@@ -203,6 +273,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
@@ -200,6 +270,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
newData.palette.read(buffer, this.strategy.globalMap());
buffer.readFixedSizeLongArray(newData.storage.getRaw());
this.data = newData;
@@ -470,7 +470,7 @@ index edf2990bfbe3d6b64eed85446eef207d2506efc5..09111c50cb34b643b7717f107a6f5bf6
this.updateData(this.data); // Paper - optimise palette reads
} finally {
this.release();
@@ -210,18 +281,31 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
@@ -207,18 +278,31 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
}
@Override
@@ -504,16 +504,16 @@ index edf2990bfbe3d6b64eed85446eef207d2506efc5..09111c50cb34b643b7717f107a6f5bf6
List<T> paletteEntries = discData.paletteEntries();
int entryCount = strategy.entryCount();
Configuration storedConfiguration = strategy.getConfigurationForPaletteSize(paletteEntries.size());
@@ -259,7 +343,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
}
@@ -257,7 +341,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
}
- return DataResult.success(new PalettedContainer<>(strategy, storedConfiguration, storage, palette));
+ return DataResult.success(new PalettedContainer<>(strategy, storedConfiguration, storage, palette, paletteEntries, defaultValue, presetValues)); // Paper - Anti-Xray - Add preset values
}
- return DataResult.success(new PalettedContainer<>(strategy, storedConfiguration, storage, palette));
+ return DataResult.success(new PalettedContainer<>(strategy, storedConfiguration, storage, palette, paletteEntries, defaultValue, presetValues)); // Paper - Anti-Xray - Add preset values
}
@@ -329,12 +413,12 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
@Override
@@ -323,12 +407,12 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
@Override
public PalettedContainer<T> copy() {
@@ -528,7 +528,7 @@ index edf2990bfbe3d6b64eed85446eef207d2506efc5..09111c50cb34b643b7717f107a6f5bf6
}
@Override
@@ -404,9 +488,16 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
@@ -398,9 +482,16 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
return 1 + this.palette.getSerializedSize(globalMap) + this.storage.getRaw().length * 8;
}
@@ -587,7 +587,7 @@ index 9977331d182cbc592f00006aabce23f0f69d2393..cd20a791b101548367ed53080f78e78b
}
}
diff --git a/net/minecraft/world/level/chunk/PalettedContainerRO.java b/net/minecraft/world/level/chunk/PalettedContainerRO.java
index d2a2d435d0eda14a04f2c8582abffb8f98092a59..1498e9a760878f444debac9d4257d096731bb3ba 100644
index 4607d2d7591a35d09d748d1ecf7582947bb86f39..f5f40aa2fd1b478045cb9a28b5ae11bc19f337f0 100644
--- a/net/minecraft/world/level/chunk/PalettedContainerRO.java
+++ b/net/minecraft/world/level/chunk/PalettedContainerRO.java
@@ -14,7 +14,11 @@ public interface PalettedContainerRO<T> {
@@ -603,7 +603,7 @@ index d2a2d435d0eda14a04f2c8582abffb8f98092a59..1498e9a760878f444debac9d4257d096
int getSerializedSize();
diff --git a/net/minecraft/world/level/chunk/Strategy.java b/net/minecraft/world/level/chunk/Strategy.java
index 45ff1447312f239154930f748637f413e2ea8f87..a0f8b23612f8315eef2aaf4c036c2ec759aafa66 100644
index 5c518b6ec7da546460884c37e9af682b6ab9fc22..958be147cd940ac430576ca5b4ada70fa5d051e3 100644
--- a/net/minecraft/world/level/chunk/Strategy.java
+++ b/net/minecraft/world/level/chunk/Strategy.java
@@ -4,7 +4,7 @@ import net.minecraft.core.IdMap;
@@ -616,7 +616,7 @@ index 45ff1447312f239154930f748637f413e2ea8f87..a0f8b23612f8315eef2aaf4c036c2ec7
private static final Palette.Factory HASHMAP_PALETTE_FACTORY = HashMapPalette::create;
private static final Configuration ZERO_BITS = new Configuration.Simple(SINGLE_VALUE_PALETTE_FACTORY, 0);
diff --git a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
index df0bb06176ad5cce3ed8785d22d68b2dcbf5a8ca..9823f20186679d75444eff2b7a7b9cf8bb2ef575 100644
index 98fb7a282a33f4391de124ed9be9bcf069178516..7d29fdc6548324d97ec34d476b5b48ec0bdba0d2 100644
--- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
+++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
@@ -141,6 +141,7 @@ public record SerializableChunkData(
@@ -626,17 +626,17 @@ index df0bb06176ad5cce3ed8785d22d68b2dcbf5a8ca..9823f20186679d75444eff2b7a7b9cf8
+ net.minecraft.server.level.ServerLevel serverLevel = (net.minecraft.server.level.ServerLevel) levelHeight; // 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 (chunkData.getString("Status").isEmpty()) {
return null;
} else {
@@ -201,9 +202,11 @@ public record SerializableChunkData(
int y = sectionTag.getByteOr("Y", (byte)0);
LevelChunkSection section;
if (y >= levelHeight.getMinSectionY() && y <= levelHeight.getMaxSectionY()) {
+ final BlockState[] presetBlockStates = serverLevel.chunkPacketBlockController.getPresetBlockStates(serverLevel, chunkPos, y); // Paper - Anti-Xray - Add preset block states
+ final Codec<PalettedContainer<BlockState>> antiXrayBlockStateCodec = presetBlockStates == null ? blockStatesCodec : PalettedContainer.codecRW(BlockState.CODEC, containerFactory.blockStatesStrategy(), net.minecraft.world.level.block.Blocks.AIR.defaultBlockState(), presetBlockStates); // Paper - Anti-Xray
PalettedContainer<BlockState> blocks = sectionTag.getCompound("block_states")
.map(
- container -> blockStatesCodec.parse(NbtOps.INSTANCE, container)
+ container -> antiXrayBlockStateCodec.parse(NbtOps.INSTANCE, container) // Paper - Anti-Xray
.promotePartial(msg -> logErrors(chunkPos, y, msg))
.getOrThrow(SerializableChunkData.ChunkReadException::new)
)
}
@@ -202,9 +203,11 @@ public record SerializableChunkData(
int y = sectionTag.getByteOr("Y", (byte)0);
LevelChunkSection section;
if (y >= levelHeight.getMinSectionY() && y <= levelHeight.getMaxSectionY()) {
+ final BlockState[] presetBlockStates = serverLevel.chunkPacketBlockController.getPresetBlockStates(serverLevel, chunkPos, y); // Paper - Anti-Xray - Add preset block states
+ final Codec<PalettedContainer<BlockState>> antiXrayBlockStateCodec = presetBlockStates == null ? blockStatesCodec : PalettedContainer.codecRW(BlockState.CODEC, containerFactory.blockStatesStrategy(), net.minecraft.world.level.block.Blocks.AIR.defaultBlockState(), presetBlockStates); // Paper - Anti-Xray
PalettedContainer<BlockState> blocks = sectionTag.getCompound("block_states")
.map(
- container -> blockStatesCodec.parse(NbtOps.INSTANCE, container)
+ container -> antiXrayBlockStateCodec.parse(NbtOps.INSTANCE, container) // Paper - Anti-Xray
.promotePartial(msg -> logErrors(chunkPos, y, msg))
.getOrThrow(SerializableChunkData.ChunkReadException::new)
)
@@ -0,0 +1,119 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Mon, 20 Apr 2026 02:56:22 -0700
Subject: [PATCH] Delay open/close callbacks for chests
The logic for chests may invoke block updates, which may not
be safe to perform. For example, inventory closing due to
player logout (which restricts chunk loading due to being
inside an entity status callback) or during shutdown (which has
chunk loading restrictions due to the chunk system being halted).
diff --git a/net/minecraft/world/level/block/entity/BarrelBlockEntity.java b/net/minecraft/world/level/block/entity/BarrelBlockEntity.java
index b39b8176d6b1a1d3f7058387518bcbb71a3631bb..5ba46020b47edfc886ecd5c33483ebb0fa57e5dc 100644
--- a/net/minecraft/world/level/block/entity/BarrelBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/BarrelBlockEntity.java
@@ -61,6 +61,13 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity {
private static final Component DEFAULT_NAME = Component.translatable("container.barrel");
private NonNullList<ItemStack> items = NonNullList.withSize(27, ItemStack.EMPTY);
public final ContainerOpenersCounter openersCounter = new ContainerOpenersCounter() {
+ // Paper start - delay open/close callbacks
+ @Override
+ public boolean delayCallbacks() {
+ return true;
+ }
+ // Paper end - delay open/close callbacks
+
@Override
protected void onOpen(final Level level, final BlockPos pos, final BlockState state) {
BarrelBlockEntity.this.playSound(state, SoundEvents.BARREL_OPEN);
diff --git a/net/minecraft/world/level/block/entity/ChestBlockEntity.java b/net/minecraft/world/level/block/entity/ChestBlockEntity.java
index 3635bd9e9b6292ff4389984eae1ae89ebca4be51..324f19786c4e298e706184045aea3e8ab98aaf46 100644
--- a/net/minecraft/world/level/block/entity/ChestBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/ChestBlockEntity.java
@@ -30,6 +30,13 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement
public static final Component DEFAULT_NAME = Component.translatable("container.chest");
private NonNullList<ItemStack> items = NonNullList.withSize(27, ItemStack.EMPTY);
public final ContainerOpenersCounter openersCounter = new ContainerOpenersCounter() {
+ // Paper start - delay open/close callbacks
+ @Override
+ public boolean delayCallbacks() {
+ return true;
+ }
+ // Paper end - delay open/close callbacks
+
@Override
protected void onOpen(final Level level, final BlockPos pos, final BlockState blockState) {
if (blockState.getBlock() instanceof ChestBlock chestBlock) {
diff --git a/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java b/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java
index baa8b5aba0500981a191626553d66650c7304b88..52737172ccfa0c7c977e4f2fe1db242e0bb92d25 100644
--- a/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java
+++ b/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java
@@ -37,11 +37,23 @@ public abstract class ContainerOpenersCounter {
protected abstract void openerCountChanged(final Level level, final BlockPos pos, final BlockState blockState, int previous, int current);
+ // Paper start - delay open/close callbacks
+ public boolean delayCallbacks() {
+ return false;
+ }
+ // Paper end - delay open/close callbacks
+
public abstract boolean isOwnContainer(final Player player);
public void incrementOpeners(
final LivingEntity entity, final Level level, final BlockPos pos, final BlockState blockState, final double maxInteractionRange
) {
+ // Paper start - delay open/close callbacks
+ if (this.delayCallbacks()) {
+ scheduleRecheck(level, pos, blockState, 1);
+ return;
+ }
+ // Paper end - delay open/close callbacks
int previous = this.openCount++;
// Paper start - Call BlockRedstoneEvent
@@ -66,6 +78,12 @@ public abstract class ContainerOpenersCounter {
}
public void decrementOpeners(final LivingEntity entity, final Level level, final BlockPos pos, final BlockState blockState) {
+ // Paper start - delay open/close callbacks
+ if (this.delayCallbacks()) {
+ scheduleRecheck(level, pos, blockState, 1);
+ return;
+ }
+ // Paper end - delay open/close callbacks
if (this.openCount == 0) return; // Paper - Prevent ContainerOpenersCounter openCount from going negative
int previous = this.openCount--;
@@ -151,6 +169,11 @@ public abstract class ContainerOpenersCounter {
}
private static void scheduleRecheck(final Level level, final BlockPos blockPos, final BlockState blockState) {
- level.scheduleTick(blockPos, blockState.getBlock(), 5);
+ // Paper start - delay open/close callbacks
+ scheduleRecheck(level, blockPos, blockState, 5);
+ }
+ private static void scheduleRecheck(final Level level, final BlockPos blockPos, final BlockState blockState, final int delay) {
+ level.scheduleTick(blockPos, blockState.getBlock(), delay);
+ // Paper end - delay open/close callbacks
}
}
diff --git a/net/minecraft/world/level/block/entity/EnderChestBlockEntity.java b/net/minecraft/world/level/block/entity/EnderChestBlockEntity.java
index 592d253921d8adb168840edcc1b24b64969e80f2..551149b8888d78fc17b153a59532ed08e7f5724b 100644
--- a/net/minecraft/world/level/block/entity/EnderChestBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/EnderChestBlockEntity.java
@@ -13,6 +13,13 @@ import net.minecraft.world.level.block.state.BlockState;
public class EnderChestBlockEntity extends BlockEntity implements LidBlockEntity {
private final ChestLidController chestLidController = new ChestLidController();
public final ContainerOpenersCounter openersCounter = new ContainerOpenersCounter() {
+ // Paper start - delay open/close callbacks
+ @Override
+ public boolean delayCallbacks() {
+ return true;
+ }
+ // Paper end - delay open/close callbacks
+
@Override
protected void onOpen(final Level level, final BlockPos pos, final BlockState blockState) {
level.playSound(
@@ -8,13 +8,14 @@ and shapeless recipes.
diff --git a/io/papermc/paper/inventory/recipe/ItemOrExact.java b/io/papermc/paper/inventory/recipe/ItemOrExact.java
new file mode 100644
index 0000000000000000000000000000000000000000..ce745e49cd54fe3ae187785563a1bd311d14b5b2
index 0000000000000000000000000000000000000000..9e621873454cc2edc2e59e6c7554d8116cd32bc1
--- /dev/null
+++ b/io/papermc/paper/inventory/recipe/ItemOrExact.java
@@ -0,0 +1,63 @@
@@ -0,0 +1,64 @@
+package io.papermc.paper.inventory.recipe;
+
+import net.minecraft.core.Holder;
+import net.minecraft.core.component.DataComponents;
+import net.minecraft.world.item.ItemStack;
+
+public sealed interface ItemOrExact permits ItemOrExact.Item, ItemOrExact.Exact {
@@ -26,12 +27,12 @@ index 0000000000000000000000000000000000000000..ce745e49cd54fe3ae187785563a1bd31
+ record Item(Holder<net.minecraft.world.item.Item> item) implements ItemOrExact {
+
+ public Item(final ItemStack stack) {
+ this(stack.getItemHolder());
+ this(stack.typeHolder());
+ }
+
+ @Override
+ public int getMaxStackSize() {
+ return this.item.value().getDefaultMaxStackSize();
+ return this.item.components().getOrDefault(DataComponents.MAX_STACK_SIZE, 1);
+ }
+
+ @Override
@@ -150,81 +151,81 @@ index 0000000000000000000000000000000000000000..f47c12e9dd6cfa857ca07a764edc22de
+ }
+}
diff --git a/net/minecraft/recipebook/ServerPlaceRecipe.java b/net/minecraft/recipebook/ServerPlaceRecipe.java
index 6d3e3ec045d5b15a435f7217369968b33e082724..b7a3758af337270737041f84d10eb43784e42279 100644
index da0380782a846dbb2657c58668e398f36f191c6e..d5ce83d5abd4de6f014a2827780170d3341eb927 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);
menu.fillCraftSlotsStackedContents(stackedItemContents);
return serverPlaceRecipe.tryPlaceRecipe(recipe, stackedItemContents);
@@ -99,7 +100,7 @@ public class ServerPlaceRecipe<R extends Recipe<?>> {
@@ -42,6 +42,7 @@ public class ServerPlaceRecipe<R extends Recipe<?>> {
}
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 (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;
StackedItemContents availableItems = new StackedItemContents();
+ availableItems.initializeExtras(recipe.value(), null); // Paper - Improve exact choice recipe ingredients
inventory.fillStackedContents(availableItems);
menu.fillCraftSlotsStackedContents(availableItems);
return placer.tryPlaceRecipe(recipe, availableItems);
@@ -100,7 +101,7 @@ public class ServerPlaceRecipe<R extends Recipe<?>> {
}
while (i2 > 0) {
@@ -129,9 +130,11 @@ public class ServerPlaceRecipe<R extends Recipe<?>> {
int amountToCraft = this.calculateAmountToCraft(biggestCraftableStack, recipeMatchesPlaced);
- List<Holder<Item>> itemsUsedPerIngredient = new ArrayList<>();
+ List<io.papermc.paper.inventory.recipe.ItemOrExact> itemsUsedPerIngredient = new ArrayList<>(); // Paper - Improve exact choice recipe ingredients
if (availableItems.canCraft(recipe.value(), amountToCraft, itemsUsedPerIngredient::add)) {
int adjustedAmountToCraft = clampToMaxStackSize(amountToCraft, itemsUsedPerIngredient);
if (adjustedAmountToCraft != amountToCraft) {
@@ -119,7 +120,7 @@ public class ServerPlaceRecipe<R extends Recipe<?>> {
(ingredientIndex, gridIndex, gridXPos, gridYPos) -> {
if (ingredientIndex != -1) {
Slot targetGridSlot = this.inputGridSlots.get(gridIndex);
- Holder<Item> itemUsed = itemsUsedPerIngredient.get(ingredientIndex);
+ io.papermc.paper.inventory.recipe.ItemOrExact itemUsed = itemsUsedPerIngredient.get(ingredientIndex); // Paper - Improve exact choice recipe ingredients
int remainingCount = adjustedAmountToCraft;
while (remainingCount > 0) {
@@ -134,9 +135,11 @@ public class ServerPlaceRecipe<R extends Recipe<?>> {
}
}
- private static int clampToMaxStackSize(int amount, List<Holder<Item>> items) {
- for (Holder<Item> holder : items) {
- amount = Math.min(amount, holder.value().getDefaultMaxStackSize());
- private static int clampToMaxStackSize(int value, final List<Holder<Item>> items) {
- for (Holder<Item> item : items) {
- value = Math.min(value, item.components().getOrDefault(DataComponents.MAX_STACK_SIZE, 1));
+ // Paper start - Improve exact choice recipe ingredients
+ 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());
+ private static int clampToMaxStackSize(int value, final List<io.papermc.paper.inventory.recipe.ItemOrExact> items) {
+ for (io.papermc.paper.inventory.recipe.ItemOrExact item : items) {
+ value = Math.min(value, item.getMaxStackSize());
+ // Paper end - Improve exact choice recipe ingredients
}
return amount;
@@ -160,7 +163,7 @@ public class ServerPlaceRecipe<R extends Recipe<?>> {
return value;
@@ -167,7 +170,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 item1 = slot.getItem();
int i = this.inventory.findSlotMatchingCraftingIngredient(item, item1);
if (i == -1) {
- private int moveItemToGrid(final Slot targetSlot, final Holder<Item> itemInInventory, final int count) {
+ private int moveItemToGrid(final Slot targetSlot, final io.papermc.paper.inventory.recipe.ItemOrExact itemInInventory, final int count) { // Paper - Improve exact choice recipe ingredients
ItemStack itemInTargetSlot = targetSlot.getItem();
int inventorySlotId = this.inventory.findSlotMatchingCraftingIngredient(itemInInventory, itemInTargetSlot);
if (inventorySlotId == Inventory.NOT_FOUND_INDEX) {
diff --git a/net/minecraft/world/entity/player/Inventory.java b/net/minecraft/world/entity/player/Inventory.java
index 76eda3bd8c513c7e42ceedf1aa605fb6df8b7019..41e59f3739945ca7f6ab710c993b5c0f15fcd529 100644
index 5c083f2ec110c388be951014d8e5a6eb64ded371..cd7714dffcd7e7b6d96d18a97e65fd0914a739d1 100644
--- a/net/minecraft/world/entity/player/Inventory.java
+++ b/net/minecraft/world/entity/player/Inventory.java
@@ -252,12 +252,12 @@ public class Inventory implements Container, Nameable {
return !stack.isDamaged() && !stack.isEnchanted() && !stack.has(DataComponents.CUSTOM_NAME);
return !item.isDamaged() && !item.isEnchanted() && !item.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
- public int findSlotMatchingCraftingIngredient(final Holder<Item> item, final ItemStack existingItem) {
+ public int findSlotMatchingCraftingIngredient(final io.papermc.paper.inventory.recipe.ItemOrExact item, final ItemStack existingItem) { // 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))) {
ItemStack inventoryItemStack = this.items.get(i);
if (!inventoryItemStack.isEmpty()
- && inventoryItemStack.is(item)
- && isUsableForCrafting(inventoryItemStack)
+ && item.matches(inventoryItemStack) // Paper - Improve exact choice recipe ingredients
+ && (!(item instanceof io.papermc.paper.inventory.recipe.ItemOrExact.Item) || Inventory.isUsableForCrafting(inventoryItemStack)) // Paper - Improve exact choice recipe ingredients
&& (existingItem.isEmpty() || ItemStack.isSameItemSameComponents(existingItem, inventoryItemStack))) {
return i;
}
diff --git a/net/minecraft/world/entity/player/StackedContents.java b/net/minecraft/world/entity/player/StackedContents.java
index bc4f7d16365dffc87b1eef8aa5791a5cb595ee78..cbb37480c19b210cf83999247c1d670c9df2d150 100644
index 2a8527a2712de71f4c0d6a2e5af8dd3c01b8ff0a..5982b79f6b391ac9c01c452747fc235faf78369e 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;
@@ -234,35 +235,35 @@ index bc4f7d16365dffc87b1eef8aa5791a5cb595ee78..cbb37480c19b210cf83999247c1d670c
- 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 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<>();
private boolean hasAtLeast(final T item, final int count) {
return this.amounts.getInt(item) >= count;
@@ -53,7 +53,7 @@ public class StackedContents<T> {
private List<T> getUniqueAvailableIngredientItems(final Iterable<? extends StackedContents.IngredientInfo<T>> ingredients) {
List<T> result = new ArrayList<>();
- for (Entry<T> entry : Reference2IntMaps.fastIterable(this.amounts)) {
+ for (it.unimi.dsi.fastutil.objects.Object2IntMap.Entry<T> entry : it.unimi.dsi.fastutil.objects.Object2IntMaps.fastIterable(this.amounts)) { // Paper - Improve exact choice recipe ingredients (don't use "reference" map)
if (entry.getIntValue() > 0 && anyIngredientMatches(ingredients, entry.getKey())) {
list.add(entry.getKey());
- for (Entry<T> availableItem : Reference2IntMaps.fastIterable(this.amounts)) {
+ for (it.unimi.dsi.fastutil.objects.Object2IntMap.Entry<T> availableItem : it.unimi.dsi.fastutil.objects.Object2IntMaps.fastIterable(this.amounts)) { // Paper - Improve exact choice recipe ingredients (don't use "reference" map)
if (availableItem.getIntValue() > 0 && anyIngredientMatches(ingredients, availableItem.getKey())) {
result.add(availableItem.getKey());
}
@@ -71,13 +71,13 @@ public class StackedContents<T> {
@@ -75,13 +75,13 @@ public class StackedContents<T> {
@VisibleForTesting
public int getResultUpperBound(List<? extends StackedContents.IngredientInfo<T>> ingredients) {
int i = Integer.MAX_VALUE;
- ObjectIterable<Entry<T>> objectIterable = Reference2IntMaps.fastIterable(this.amounts);
+ ObjectIterable<it.unimi.dsi.fastutil.objects.Object2IntMap.Entry<T>> objectIterable = it.unimi.dsi.fastutil.objects.Object2IntMaps.fastIterable(this.amounts); // Paper - Improve exact choice recipe ingredients (don't use "reference" map)
public int getResultUpperBound(final List<? extends StackedContents.IngredientInfo<T>> ingredients) {
int min = Integer.MAX_VALUE;
- ObjectIterable<Entry<T>> availableItems = Reference2IntMaps.fastIterable(this.amounts);
+ ObjectIterable<it.unimi.dsi.fastutil.objects.Object2IntMap.Entry<T>> availableItems = it.unimi.dsi.fastutil.objects.Object2IntMaps.fastIterable(this.amounts); // Paper - Improve exact choice recipe ingredients (don't use "reference" map)
label31:
for (StackedContents.IngredientInfo<T> ingredientInfo : ingredients) {
int i1 = 0;
for (StackedContents.IngredientInfo<T> ingredient : ingredients) {
int max = 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 intValue = entry.getIntValue();
if (intValue > i1) {
if (ingredientInfo.acceptsItem(entry.getKey())) {
- for (Entry<T> entry : availableItems) {
+ for (it.unimi.dsi.fastutil.objects.Object2IntMap.Entry<T> entry : availableItems) { // Paper - Improve exact choice recipe ingredients (don't use "reference" map)
int itemCount = entry.getIntValue();
if (itemCount > max) {
if (ingredient.acceptsItem(entry.getKey())) {
diff --git a/net/minecraft/world/entity/player/StackedItemContents.java b/net/minecraft/world/entity/player/StackedItemContents.java
index e0121e9c8439e32b7ef064c06942ccd5dd74987d..00dbc0d125b2c8c208ffa46f9084c6317efaba90 100644
index ffaa8e450a11a01060b6847d1b66a76323f9d8ba..1ace17d8fc7e7b8b4722a1889d49233c0ec55e20 100644
--- a/net/minecraft/world/entity/player/StackedItemContents.java
+++ b/net/minecraft/world/entity/player/StackedItemContents.java
@@ -9,9 +9,27 @@ import net.minecraft.world.item.crafting.Recipe;
@@ -289,115 +290,119 @@ index e0121e9c8439e32b7ef064c06942ccd5dd74987d..00dbc0d125b2c8c208ffa46f9084c631
+ }
+ // Paper end - Improve exact choice recipe ingredients
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);
public void accountSimpleStack(final ItemStack itemStack) {
+ if (this.extrasMap != null && this.extrasMap.accountStack(itemStack, Math.min(itemStack.getMaxStackSize(), itemStack.getCount()))) return; // Paper - Improve exact choice recipe ingredients; Referenced from the accountStack method below
if (Inventory.isUsableForCrafting(itemStack)) {
this.accountStack(itemStack);
}
@@ -24,34 +42,35 @@ 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);
@@ -24,38 +42,39 @@ public class StackedItemContents {
public void accountStack(final ItemStack itemStack, final int maxCount) {
if (!itemStack.isEmpty()) {
int count = Math.min(maxCount, itemStack.getCount());
- this.raw.account(itemStack.typeHolder(), count);
+ if (this.extrasMap != null && !itemStack.getComponentsPatch().isEmpty() && this.extrasMap.accountStack(itemStack, count)) 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(itemStack.typeHolder()), count);
}
}
- public boolean canCraft(Recipe<?> recipe, StackedContents.@Nullable Output<Holder<Item>> output) {
+ public boolean canCraft(Recipe<?> recipe, StackedContents.@Nullable Output<io.papermc.paper.inventory.recipe.ItemOrExact> output) { // Paper - Improve exact choice recipe ingredients
- public boolean canCraft(final Recipe<?> recipe, final StackedContents.@Nullable Output<Holder<Item>> output) {
+ public boolean canCraft(final Recipe<?> recipe, final StackedContents.@Nullable 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, StackedContents.@Nullable Output<Holder<Item>> output) {
+ public boolean canCraft(Recipe<?> recipe, int maxCount, StackedContents.@Nullable Output<io.papermc.paper.inventory.recipe.ItemOrExact> output) { // Paper - Improve exact choice recipe ingredients
- public boolean canCraft(final Recipe<?> recipe, final int amount, final StackedContents.@Nullable Output<Holder<Item>> output) {
+ public boolean canCraft(final Recipe<?> recipe, final int amount, final StackedContents.@Nullable 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(), maxCount, output);
return !placementInfo.isImpossibleToPlace() && this.canCraft(placementInfo.ingredients(), amount, output);
}
- public boolean canCraft(List<? extends StackedContents.IngredientInfo<Holder<Item>>> ingredients, StackedContents.@Nullable Output<Holder<Item>> output) {
+ public boolean canCraft(List<? extends StackedContents.IngredientInfo<io.papermc.paper.inventory.recipe.ItemOrExact>> ingredients, StackedContents.@Nullable Output<io.papermc.paper.inventory.recipe.ItemOrExact> output) { // Paper - Improve exact choice recipe ingredients
return this.canCraft(ingredients, 1, output);
public boolean canCraft(
- final List<? extends StackedContents.IngredientInfo<Holder<Item>>> contents, final StackedContents.@Nullable Output<Holder<Item>> output
+ final List<? extends StackedContents.IngredientInfo<io.papermc.paper.inventory.recipe.ItemOrExact>> contents, final StackedContents.@Nullable Output<io.papermc.paper.inventory.recipe.ItemOrExact> output // Paper - Improve exact choice recipe ingredients
) {
return this.canCraft(contents, 1, output);
}
private boolean canCraft(
- List<? extends StackedContents.IngredientInfo<Holder<Item>>> ingredients, int maxCount, StackedContents.@Nullable Output<Holder<Item>> output
+ List<? extends StackedContents.IngredientInfo<io.papermc.paper.inventory.recipe.ItemOrExact>> ingredients, int maxCount, StackedContents.@Nullable Output<io.papermc.paper.inventory.recipe.ItemOrExact> output // Paper - Improve exact choice recipe ingredients
- final List<? extends StackedContents.IngredientInfo<Holder<Item>>> contents,
+ final List<? extends StackedContents.IngredientInfo<io.papermc.paper.inventory.recipe.ItemOrExact>> contents, // Paper - Improve exact choice recipe ingredients
final int amount,
- final StackedContents.@Nullable Output<Holder<Item>> output
+ final StackedContents.@Nullable Output<io.papermc.paper.inventory.recipe.ItemOrExact> output // Paper - Improve exact choice recipe ingredients
) {
return this.raw.tryPick(ingredients, maxCount, output);
return this.raw.tryPick(contents, amount, output);
}
- public int getBiggestCraftableStack(Recipe<?> recipe, StackedContents.@Nullable Output<Holder<Item>> output) {
+ public int getBiggestCraftableStack(Recipe<?> recipe, StackedContents.@Nullable Output<io.papermc.paper.inventory.recipe.ItemOrExact> output) { // Paper - Improve exact choice recipe ingredients
- public int getBiggestCraftableStack(final Recipe<?> recipe, final StackedContents.@Nullable Output<Holder<Item>> output) {
+ public int getBiggestCraftableStack(final Recipe<?> recipe, final StackedContents.@Nullable 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 maxCount, StackedContents.@Nullable Output<Holder<Item>> output) {
+ public int getBiggestCraftableStack(Recipe<?> recipe, int maxCount, StackedContents.@Nullable Output<io.papermc.paper.inventory.recipe.ItemOrExact> output) { // Paper - Improve exact choice recipe ingredients
return this.raw.tryPickAll(recipe.placementInfo().ingredients(), maxCount, output);
- public int getBiggestCraftableStack(final Recipe<?> recipe, final int maxSize, final StackedContents.@Nullable Output<Holder<Item>> output) {
+ public int getBiggestCraftableStack(final Recipe<?> recipe, final int maxSize, final StackedContents.@Nullable Output<io.papermc.paper.inventory.recipe.ItemOrExact> output) { // Paper - Improve exact choice recipe ingredients
return this.raw.tryPickAll(recipe.placementInfo().ingredients(), maxSize, output);
}
diff --git a/net/minecraft/world/item/crafting/Ingredient.java b/net/minecraft/world/item/crafting/Ingredient.java
index ac4520afe510df9b1dd256d13dcb0768c83edb70..da95f40503f3696c8daa8fd0e947508fe43060ba 100644
index 653fb104bb51fb738aa3ad22093c24f196f33df2..d4601c6a77f89d89ac9f042ee7b8c25461409523 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;
@@ -22,7 +22,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 final class Ingredient implements Predicate<ItemStack>, StackedContents.IngredientInfo<Holder<Item>> {
+public final class Ingredient implements Predicate<ItemStack>, StackedContents.IngredientInfo<io.papermc.paper.inventory.recipe.ItemOrExact> { // Paper - Improve exact choice recipe ingredients
public static final StreamCodec<RegistryFriendlyByteBuf, Ingredient> CONTENTS_STREAM_CODEC = ByteBufCodecs.holderSet(Registries.ITEM)
.map(Ingredient::new, ingredient -> ingredient.values);
.map(Ingredient::new, i -> i.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
@@ -34,19 +34,23 @@ public final class Ingredient implements Predicate<ItemStack>, StackedContents.I
public static final Codec<Ingredient> CODEC = ExtraCodecs.nonEmptyHolderSet(NON_AIR_HOLDER_SET_CODEC).xmap(Ingredient::new, i -> i.values);
public final HolderSet<Item> values;
// CraftBukkit start
@javax.annotation.Nullable
- private java.util.List<ItemStack> itemStacks;
+ private java.util.Set<ItemStack> itemStacks; // Paper - Improve exact choice recipe ingredients
- private java.util.@org.jspecify.annotations.Nullable List<ItemStack> itemStacks;
+ private java.util.@org.jspecify.annotations.Nullable Set<ItemStack> itemStacks; // Paper - Improve exact choice recipe ingredients
public boolean isExact() {
return this.itemStacks != null;
}
@javax.annotation.Nullable
- public java.util.List<ItemStack> itemStacks() {
+ public java.util.Set<ItemStack> itemStacks() { // Paper - Improve exact choice recipe ingredients
- public java.util.@org.jspecify.annotations.Nullable List<ItemStack> itemStacks() {
+ public java.util.@org.jspecify.annotations.Nullable Set<ItemStack> itemStacks() { // Paper - Improve exact choice recipe ingredients
return this.itemStacks;
}
public static Ingredient ofStacks(java.util.List<ItemStack> stacks) {
Ingredient recipe = Ingredient.of(stacks.stream().map(ItemStack::getItem));
- recipe.itemStacks = stacks;
Ingredient ingredient = Ingredient.of(stacks.stream().map(ItemStack::getItem));
- ingredient.itemStacks = stacks;
+ // Paper start - Improve exact choice recipe ingredients
+ recipe.itemStacks = net.minecraft.world.item.ItemStackLinkedSet.createTypeAndComponentsSet();
+ recipe.itemStacks.addAll(stacks);
+ recipe.itemStacks = java.util.Collections.unmodifiableSet(recipe.itemStacks);
+ ingredient.itemStacks = net.minecraft.world.item.ItemStackLinkedSet.createTypeAndComponentsSet();
+ ingredient.itemStacks.addAll(stacks);
+ ingredient.itemStacks = java.util.Collections.unmodifiableSet(ingredient.itemStacks);
+ // Paper end - Improve exact choice recipe ingredients
return recipe;
return ingredient;
}
// CraftBukkit end
@@ -81,21 +85,22 @@ public final class Ingredient implements StackedContents.IngredientInfo<Holder<I
public boolean test(ItemStack stack) {
@@ -81,21 +85,22 @@ public final class Ingredient implements Predicate<ItemStack>, StackedContents.I
public boolean test(final ItemStack input) {
// CraftBukkit start
if (this.isExact()) {
- for (ItemStack itemstack1 : this.itemStacks()) {
- if (itemstack1.getItem() == stack.getItem() && ItemStack.isSameItemSameComponents(stack, itemstack1)) {
- for (ItemStack item : this.itemStacks()) {
- if (item.getItem() == input.getItem() && ItemStack.isSameItemSameComponents(input, item)) {
- return true;
- }
- }
-
- return false;
+ return this.itemStacks.contains(stack); // Paper - Improve exact choice recipe ingredients (hashing FTW!)
+ return this.itemStacks.contains(input); // Paper - Improve exact choice recipe ingredients (hashing FTW!)
}
// CraftBukkit end
return stack.is(this.values);
return input.is(this.values);
}
+ // Paper start - Improve exact choice recipe ingredients
@Override
- public boolean acceptsItem(Holder<Item> item) {
- public boolean acceptsItem(final Holder<Item> item) {
- return this.values.contains(item);
+ public boolean acceptsItem(final io.papermc.paper.inventory.recipe.ItemOrExact itemOrExact) {
+ return switch (itemOrExact) {
@@ -410,26 +415,26 @@ index ac4520afe510df9b1dd256d13dcb0768c83edb70..da95f40503f3696c8daa8fd0e947508f
}
@Override
@@ -120,6 +125,11 @@ public final class Ingredient implements StackedContents.IngredientInfo<Holder<I
@@ -120,6 +125,11 @@ public final class Ingredient implements Predicate<ItemStack>, StackedContents.I
}
public SlotDisplay display() {
+ // Paper start - show exact ingredients in recipe book
+ if (this.isExact()) {
+ return new SlotDisplay.Composite(this.itemStacks().stream().<SlotDisplay>map(SlotDisplay.ItemStackSlotDisplay::new).toList());
+ return new SlotDisplay.Composite(this.itemStacks().stream().map(ItemStackTemplate::fromNonEmptyStack).<SlotDisplay>map(SlotDisplay.ItemStackSlotDisplay::new).toList());
+ }
+ // Paper end - show exact ingredients in recipe book
return (SlotDisplay)this.values
.unwrap()
.map(SlotDisplay.TagSlotDisplay::new, list -> new SlotDisplay.Composite(list.stream().map(Ingredient::displayForSingleItem).toList()));
.map(SlotDisplay.TagSlotDisplay::new, l -> new SlotDisplay.Composite(l.stream().map(Ingredient::displayForSingleItem).toList()));
diff --git a/net/minecraft/world/item/crafting/ShapelessRecipe.java b/net/minecraft/world/item/crafting/ShapelessRecipe.java
index 43c8b7a027c519196852602d136a31f8c392b786..28d085fce206634ee88c492e06fdbf63a618ee12 100644
index d4d4fad66be0f0c0225993026602dc61e10c6bce..53a818f14709d1978dbbc4aa7367be733a8ee47f 100644
--- a/net/minecraft/world/item/crafting/ShapelessRecipe.java
+++ b/net/minecraft/world/item/crafting/ShapelessRecipe.java
@@ -71,13 +71,18 @@ public class ShapelessRecipe implements CraftingRecipe {
@@ -74,13 +74,18 @@ public class ShapelessRecipe extends NormalCraftingRecipe {
@Override
public boolean matches(CraftingInput input, Level level) {
public boolean matches(final CraftingInput input, final Level level) {
+ // Paper start - Improve exact choice recipe ingredients & unwrap ternary
if (input.ingredientCount() != this.ingredients.size()) {
return false;
@@ -1,16 +1,18 @@
--- a/com/mojang/math/OctahedralGroup.java
+++ b/com/mojang/math/OctahedralGroup.java
@@ -99,6 +_,12 @@
@@ -99,6 +_,14 @@
.map(f -> Arrays.stream(values()).filter(s -> f.compose(s) == IDENTITY).findAny().get())
.toArray(OctahedralGroup[]::new);
+ // Paper start - Avoid Lazy Initialization for Enum Fields
+ static {
+ for (OctahedralGroup octahedralGroup : values()) {
+ octahedralGroup.initializeRotationDirections(); // Paper - Avoid Lazy Initialization for Enum Fields
+ octahedralGroup.initializeRotationDirections();
+ }
+ }
+ // Paper end - Avoid Lazy Initialization for Enum Fields
+
private OctahedralGroup(final String name, final SymmetricGroup3 permutation, final boolean invertX, final boolean invertY, final boolean invertZ) {
OctahedralGroup(final String name, final SymmetricGroup3 permutation, final boolean invertX, final boolean invertY, final boolean invertZ) {
this.name = name;
this.invertX = invertX;
@@ -139,7 +_,7 @@
@@ -8,7 +8,7 @@
}
public String getTitle() {
@@ -217,7 +_,7 @@
@@ -214,7 +_,7 @@
}
public static void preload() {
@@ -1,11 +1,11 @@
--- a/net/minecraft/advancements/criterion/LocationPredicate.java
+++ b/net/minecraft/advancements/criterion/LocationPredicate.java
@@ -44,7 +_,7 @@
public boolean matches(final ServerLevel level, final double x, final double y, final double z) {
if (this.position.isPresent() && !this.position.get().matches(x, y, z)) {
@@ -46,7 +_,7 @@
return false;
- } else if (this.dimension.isPresent() && this.dimension.get() != level.dimension()) {
+ } else if (this.dimension.isPresent() && this.dimension.get() != (io.papermc.paper.configuration.GlobalConfiguration.get().misc.strictAdvancementDimensionCheck ? level.dimension() : org.bukkit.craftbukkit.util.CraftDimensionUtil.getMainDimensionKey(level))) { // Paper - Add option for strict advancement dimension checks
}
- if (this.dimension.isPresent() && this.dimension.get() != level.dimension()) {
+ if (this.dimension.isPresent() && this.dimension.get() != (io.papermc.paper.configuration.GlobalConfiguration.get().misc.strictAdvancementDimensionCheck ? level.dimension() : org.bukkit.craftbukkit.util.CraftDimensionUtil.getMainDimensionKey(level))) { // Paper - Add option for strict advancement dimension checks
return false;
} else {
BlockPos pos = BlockPos.containing(x, y, z);
}
@@ -1,6 +1,6 @@
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -157,6 +_,7 @@
@@ -156,6 +_,7 @@
import org.slf4j.Logger;
public class Commands {
@@ -8,7 +8,7 @@
public static final String COMMAND_PREFIX = "/";
private static final ThreadLocal<@Nullable ExecutionContext<CommandSourceStack>> CURRENT_EXECUTION_CONTEXT = new ThreadLocal<>();
private static final Logger LOGGER = LogUtils.getLogger();
@@ -181,6 +_,7 @@
@@ -180,6 +_,7 @@
@Override
public boolean isRestricted(final CommandNode<CommandSourceStack> node) {
@@ -16,7 +16,7 @@
Predicate<CommandSourceStack> requirement = node.getRequirement();
return !requirement.test(this.noPermissionSource);
}
@@ -188,6 +_,11 @@
@@ -187,6 +_,11 @@
private final CommandDispatcher<CommandSourceStack> dispatcher = new CommandDispatcher<>();
public Commands(final Commands.CommandSelection commandSelection, final CommandBuildContext context) {
@@ -28,7 +28,7 @@
AdvancementCommands.register(this.dispatcher);
AttributeCommand.register(this.dispatcher, context);
ExecuteCommand.register(this.dispatcher, context);
@@ -299,6 +_,42 @@
@@ -298,6 +_,42 @@
PublishCommand.register(this.dispatcher);
}
@@ -71,7 +71,7 @@
this.dispatcher.setConsumer(ExecutionCommandSource.resultConsumer());
}
@@ -318,6 +_,13 @@
@@ -317,6 +_,13 @@
}
public void performCommand(final ParseResults<CommandSourceStack> command, final String commandString) {
@@ -85,36 +85,36 @@
CommandSourceStack sender = command.getContext().getSource();
Profiler.get().push(() -> "/" + commandString);
ContextChain<CommandSourceStack> commandChain = finishParsing(command, commandString, sender);
@@ -331,10 +_,12 @@
@@ -330,10 +_,12 @@
)
);
}
- } catch (Exception var12) {
- } catch (Exception e) {
+ // Paper start
+ } catch (Throwable var12) { // always gracefully handle it, no matter how bad:tm:
+ if (throwCommandError) throw var12; // rethrow directly if requested
MutableComponent hover = Component.literal(var12.getMessage() == null ? var12.getClass().getName() : var12.getMessage());
+ } catch (Throwable e) { // always gracefully handle it, no matter how bad:tm:
+ if (throwCommandError) throw e; // rethrow directly if requested
MutableComponent hover = Component.literal(e.getMessage() == null ? e.getClass().getName() : e.getMessage());
- if (LOGGER.isDebugEnabled()) {
- LOGGER.error("Command exception: /{}", commandString, var12);
+ LOGGER.error("Command exception: /{}", commandString, var12); // Paper - always show execution exception in console log
- LOGGER.error("Command exception: /{}", commandString, e);
+ LOGGER.error("Command exception: /{}", commandString, e); // Paper - always show execution exception in console log
+ if (sender.getServer().isDebugging() || LOGGER.isDebugEnabled()) { // Paper - Debugging
StackTraceElement[] stackTrace = var12.getStackTrace();
StackTraceElement[] stackTrace = e.getStackTrace();
for (int i = 0; i < Math.min(stackTrace.length, 3); i++) {
@@ -365,7 +_,11 @@
@@ -364,7 +_,11 @@
return ContextChain.tryFlatten(command.getContext().build(commandString))
.orElseThrow(() -> CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand().createWithContext(command.getReader()));
} catch (CommandSyntaxException var7) {
- sender.sendFailure(ComponentUtils.fromMessage(var7.getRawMessage()));
} catch (CommandSyntaxException e) {
- sender.sendFailure(ComponentUtils.fromMessage(e.getRawMessage()));
+ // Paper start - Add UnknownCommandEvent
+ final net.kyori.adventure.text.TextComponent.Builder builder = net.kyori.adventure.text.Component.text();
+ // sender.sendFailure(ComponentUtils.fromMessage(var7.getRawMessage()));
+ builder.color(net.kyori.adventure.text.format.NamedTextColor.RED).append(io.papermc.paper.command.brigadier.MessageComponentSerializer.message().deserialize(var7.getRawMessage()));
+ builder.color(net.kyori.adventure.text.format.NamedTextColor.RED).append(io.papermc.paper.command.brigadier.MessageComponentSerializer.message().deserialize(e.getRawMessage()));
+ // Paper end - Add UnknownCommandEvent
if (var7.getInput() != null && var7.getCursor() >= 0) {
int cursor = Math.min(var7.getInput().length(), var7.getCursor());
if (e.getInput() != null && e.getCursor() >= 0) {
int cursor = Math.min(e.getInput().length(), e.getCursor());
MutableComponent context = Component.empty()
@@ -382,7 +_,17 @@
@@ -381,7 +_,17 @@
}
context.append(Component.translatable("command.context.here").withStyle(ChatFormatting.RED, ChatFormatting.ITALIC));
@@ -133,7 +133,7 @@
}
return null;
@@ -410,19 +_,112 @@
@@ -409,19 +_,112 @@
}
public void sendCommands(final ServerPlayer player) {
@@ -249,7 +249,7 @@
if (builder.getRedirect() != null) {
builder.redirect(converted.get(builder.getRedirect()));
}
@@ -431,7 +_,7 @@
@@ -430,7 +_,7 @@
converted.put(child, node);
target.addChild(node);
if (!child.getChildren().isEmpty()) {
@@ -1,6 +1,6 @@
--- a/net/minecraft/commands/arguments/EntityArgument.java
+++ b/net/minecraft/commands/arguments/EntityArgument.java
@@ -107,9 +_,14 @@
@@ -106,9 +_,14 @@
}
private EntitySelector parse(final StringReader reader, final boolean allowSelectors) throws CommandSyntaxException {
@@ -16,7 +16,7 @@
if (selector.getMaxResults() > 1 && this.single) {
if (this.playersOnly) {
reader.setCursor(0);
@@ -131,7 +_,13 @@
@@ -130,7 +_,13 @@
if (contextBuilder.getSource() instanceof SharedSuggestionProvider source) {
StringReader reader = new StringReader(builder.getInput());
reader.setCursor(builder.getStart());
@@ -10,7 +10,7 @@
+ private final int adjZ;
+ // Paper end - Perf: Inline shift direction fields
+
private Direction(
Direction(
final int data3d,
final int oppositeIndex,
@@ -81,6 +_,11 @@
@@ -1,19 +1,18 @@
--- a/net/minecraft/core/HolderLookup.java
+++ b/net/minecraft/core/HolderLookup.java
@@ -69,6 +_,9 @@
@@ -68,6 +_,8 @@
}
public interface RegistryLookup<T> extends HolderLookup<T>, HolderOwner<T> {
+
interface RegistryLookup<T> extends HolderLookup<T>, HolderOwner<T> {
+ Optional<T> getValueForCopying(ResourceKey<T> resourceKey); // Paper - add method to get the value for pre-filling builders in the reg mod API
+
ResourceKey<? extends Registry<? extends T>> key();
Lifecycle registryLifecycle();
@@ -83,6 +_,13 @@
Objects.requireNonNull(RegistryLookup.this);
}
@@ -78,6 +_,13 @@
default HolderLookup.RegistryLookup<T> filterElements(final Predicate<T> filter) {
return new HolderLookup.RegistryLookup.Delegate<T>() {
+ // Paper start - add getValueForCopying
+ @Override
+ public Optional<T> getValueForCopying(final ResourceKey<T> resourceKey) {
@@ -24,9 +23,9 @@
@Override
public HolderLookup.RegistryLookup<T> parent() {
return RegistryLookup.this;
@@ -102,6 +_,13 @@
@@ -97,6 +_,13 @@
public interface Delegate<T> extends HolderLookup.RegistryLookup<T> {
interface Delegate<T> extends HolderLookup.RegistryLookup<T> {
HolderLookup.RegistryLookup<T> parent();
+
+ // Paper start - add getValueForCopying
@@ -31,23 +31,23 @@
@Override
public Stream<HolderSet.Named<T>> listTags() {
@@ -114,6 +_,7 @@
this.toId.put(value, newId);
this.registrationInfos.put(key, registrationInfo);
this.registryLifecycle = this.registryLifecycle.add(registrationInfo.lifecycle());
+ this.temporaryUnfrozenMap.put(key.identifier(), value); // Paper - support pre-filling in registry mod API
return holder;
}
@@ -117,6 +_,7 @@
this.toId.put(value, newId);
this.registrationInfos.put(key, registrationInfo);
this.registryLifecycle = this.registryLifecycle.add(registrationInfo.lifecycle());
+ this.temporaryUnfrozenMap.put(key.identifier(), value); // Paper - support pre-filling in registry mod API
return holder;
}
@@ -275,6 +_,7 @@
return this;
} else {
this.frozen = true;
+ this.temporaryUnfrozenMap.clear(); // Paper - support pre-filling in registry mod API
this.byValue.forEach((value, holder) -> holder.bindValue((T)value));
List<Identifier> unboundEntries = this.byKey
.entrySet()
@@ -522,4 +_,13 @@
@@ -278,6 +_,7 @@
}
this.frozen = true;
+ this.temporaryUnfrozenMap.clear(); // Paper - support pre-filling in registry mod API
this.byValue.forEach((value, holder) -> holder.bindValue((T)value));
List<Identifier> unboundEntries = this.byKey
.entrySet()
@@ -512,4 +_,13 @@
Stream<HolderSet.Named<T>> getTags();
}
@@ -1,6 +1,6 @@
--- a/net/minecraft/core/RegistrySetBuilder.java
+++ b/net/minecraft/core/RegistrySetBuilder.java
@@ -42,6 +_,13 @@
@@ -41,6 +_,13 @@
final Map<ResourceKey<T>, Holder.Reference<T>> entries
) {
return new RegistrySetBuilder.EmptyTagRegistryLookup<T>(owner) {
@@ -14,7 +14,7 @@
@Override
public ResourceKey<? extends Registry<? extends T>> key() {
return key;
@@ -128,6 +_,13 @@
@@ -123,6 +_,13 @@
public <T> Optional<RegistryOps.RegistryInfo<T>> lookup(final ResourceKey<? extends Registry<? extends T>> registryKey) {
return getEntry(registryKey).map(Entry::opsInfo);
}
@@ -1,6 +1,6 @@
--- a/net/minecraft/core/Rotations.java
+++ b/net/minecraft/core/Rotations.java
@@ -26,11 +_,22 @@
@@ -26,10 +_,21 @@
output.writeFloat(value.z);
}
};
@@ -12,14 +12,13 @@
+ SKIP_VALIDATION = false;
+ return rotations;
+ }
+ // Paper end - add internal method for skipping validation for plugins using userdev
+ // Paper end - add internal method for skipping validation for plugins using userdev
public Rotations(float x, float y, float z) {
public Rotations {
+ if (!SKIP_VALIDATION) { // Paper - add internal method for skipping validation for plugins using userdev
x = !Float.isInfinite(x) && !Float.isNaN(x) ? x % 360.0F : 0.0F;
y = !Float.isInfinite(y) && !Float.isNaN(y) ? y % 360.0F : 0.0F;
z = !Float.isInfinite(z) && !Float.isNaN(z) ? z % 360.0F : 0.0F;
+ } // Paper - add internal method for skipping validation for plugins using userdev
this.x = x;
this.y = y;
this.z = z;
}
}
@@ -10,5 +10,5 @@
- InteractionResult interact(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, ItemStack itemInHand);
+ InteractionResult interact(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, ItemStack itemInHand, net.minecraft.core.Direction hitDirection); // Paper - add hitDirection
public static class Dispatcher {
class Dispatcher {
private final Map<TagKey<Item>, CauldronInteraction> tags = new HashMap<>();
@@ -32,7 +32,7 @@
state,
level,
pos,
@@ -76,33 +_,43 @@
@@ -76,23 +_,28 @@
itemInHand,
new ItemStack(Items.WATER_BUCKET),
s -> s.getValue(LayeredCauldronBlock.LEVEL) == 3,
@@ -64,23 +64,24 @@
+ WATER.put(Items.POTION, (state, level, pos, player, hand, itemInHand, hitDirection) -> { // Paper - add hitDirection
if (state.getValue(LayeredCauldronBlock.LEVEL) == 3) {
return InteractionResult.TRY_WITH_EMPTY_HAND;
} else {
PotionContents potion = itemInHand.get(DataComponents.POTION_CONTENTS);
if (potion != null && potion.is(Potions.WATER)) {
if (!level.isClientSide()) {
+ // CraftBukkit start
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleCauldronLevelChangeEvent(level, pos, state.cycle(LayeredCauldronBlock.LEVEL), player, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.BOTTLE_EMPTY)) { // Paper - Call CauldronLevelChangeEvent
+ return InteractionResult.SUCCESS;
+ }
+ // CraftBukkit end
player.setItemInHand(hand, ItemUtils.createFilledResult(itemInHand, player, new ItemStack(Items.GLASS_BOTTLE)));
player.awardStat(Stats.USE_CAULDRON);
player.awardStat(Stats.ITEM_USED.get(itemInHand.getItem()));
- level.setBlockAndUpdate(pos, state.cycle(LayeredCauldronBlock.LEVEL));
+ // level.setBlockAndUpdate(pos, state.cycle(LayeredCauldronBlock.LEVEL)); // CraftBukkit
level.playSound(null, pos, SoundEvents.BOTTLE_EMPTY, SoundSource.BLOCKS, 1.0F, 1.0F);
level.gameEvent(null, GameEvent.FLUID_PLACE, pos);
}
}
@@ -100,10 +_,15 @@
PotionContents potion = itemInHand.get(DataComponents.POTION_CONTENTS);
if (potion != null && potion.is(Potions.WATER)) {
if (!level.isClientSide()) {
+ // CraftBukkit start
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleCauldronLevelChangeEvent(level, pos, state.cycle(LayeredCauldronBlock.LEVEL), player, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.BOTTLE_EMPTY)) { // Paper - Call CauldronLevelChangeEvent
+ return InteractionResult.SUCCESS;
+ }
+ // CraftBukkit end
player.setItemInHand(hand, ItemUtils.createFilledResult(itemInHand, player, new ItemStack(Items.GLASS_BOTTLE)));
player.awardStat(Stats.USE_CAULDRON);
player.awardStat(Stats.ITEM_USED.get(itemInHand.getItem()));
- level.setBlockAndUpdate(pos, state.cycle(LayeredCauldronBlock.LEVEL));
+ // level.setBlockAndUpdate(pos, state.cycle(LayeredCauldronBlock.LEVEL)); // CraftBukkit
level.playSound(null, pos, SoundEvents.BOTTLE_EMPTY, SoundSource.BLOCKS, 1.0F, 1.0F);
level.gameEvent(null, GameEvent.FLUID_PLACE, pos);
}
@@ -148,14 +_,14 @@
WATER.put(Items.YELLOW_SHULKER_BOX, CauldronInteractions::shulkerBoxInteraction);
LAVA.put(
@@ -108,7 +109,7 @@
)
);
addDefaultInteractions(POWDER_SNOW);
@@ -187,15 +_,46 @@
@@ -187,16 +_,47 @@
final Predicate<BlockState> canFill,
final SoundEvent soundEvent
) {
@@ -131,31 +132,32 @@
+ // Paper end - add hitDirection
if (!canFill.test(state)) {
return InteractionResult.TRY_WITH_EMPTY_HAND;
} else {
if (!level.isClientSide()) {
+ // Paper start - fire PlayerBucketFillEvent
+ if (hitDirection != null) {
+ org.bukkit.event.player.PlayerBucketEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBucketFillEvent(level, player, pos, pos, hitDirection, itemInHand, newItem.getItem(), hand);
+ if (event.isCancelled()) {
+ return InteractionResult.PASS;
+ }
+ newItem = event.getItemStack() != null ? org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItemStack()) : ItemStack.EMPTY;
}
if (!level.isClientSide()) {
+ // Paper start - fire PlayerBucketFillEvent
+ if (hitDirection != null) {
+ org.bukkit.event.player.PlayerBucketEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBucketFillEvent(level, player, pos, pos, hitDirection, itemInHand, newItem.getItem(), hand);
+ if (event.isCancelled()) {
+ return InteractionResult.PASS;
+ }
+ // Paper end - fire PlayerBucketFillEvent
+ // CraftBukkit start
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleCauldronLevelChangeEvent(level, pos, Blocks.CAULDRON.defaultBlockState(), player, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.BUCKET_FILL)) { // Paper - Call CauldronLevelChangeEvent
+ return InteractionResult.SUCCESS;
+ }
+ // CraftBukkit end
Item itemUsed = itemInHand.getItem();
player.setItemInHand(hand, ItemUtils.createFilledResult(itemInHand, player, newItem));
player.awardStat(Stats.USE_CAULDRON);
player.awardStat(Stats.ITEM_USED.get(itemUsed));
- level.setBlockAndUpdate(pos, Blocks.CAULDRON.defaultBlockState());
+ // level.setBlockAndUpdate(pos, Blocks.CAULDRON.defaultBlockState()); // CraftBukkit
level.playSound(null, pos, soundEvent, SoundSource.BLOCKS, 1.0F, 1.0F);
level.gameEvent(null, GameEvent.FLUID_PICKUP, pos);
}
+ newItem = event.getItemStack() != null ? org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItemStack()) : ItemStack.EMPTY;
+ }
+ // Paper end - fire PlayerBucketFillEvent
+ // CraftBukkit start
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleCauldronLevelChangeEvent(level, pos, Blocks.CAULDRON.defaultBlockState(), player, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.BUCKET_FILL)) { // Paper - Call CauldronLevelChangeEvent
+ return InteractionResult.SUCCESS;
+ }
+ // CraftBukkit end
Item itemUsed = itemInHand.getItem();
player.setItemInHand(hand, ItemUtils.createFilledResult(itemInHand, player, newItem));
player.awardStat(Stats.USE_CAULDRON);
player.awardStat(Stats.ITEM_USED.get(itemUsed));
- level.setBlockAndUpdate(pos, Blocks.CAULDRON.defaultBlockState());
+ // level.setBlockAndUpdate(pos, Blocks.CAULDRON.defaultBlockState()); // CraftBukkit
level.playSound(null, pos, soundEvent, SoundSource.BLOCKS, 1.0F, 1.0F);
level.gameEvent(null, GameEvent.FLUID_PICKUP, pos);
}
@@ -213,12 +_,42 @@
final BlockState newState,
final SoundEvent soundEvent
@@ -218,25 +220,25 @@
- final BlockState state, final Level level, final BlockPos pos, final Player player, final InteractionHand hand, final ItemStack itemInHand
+ final BlockState state, final Level level, final BlockPos pos, final Player player, final InteractionHand hand, final ItemStack itemInHand, final net.minecraft.core.Direction hitDirection // Paper - add hitDirection
) {
return (InteractionResult)(isUnderWater(level, pos)
return isUnderWater(level, pos)
? InteractionResult.CONSUME
- : emptyBucket(level, pos, player, hand, itemInHand, Blocks.LAVA_CAULDRON.defaultBlockState(), SoundEvents.BUCKET_EMPTY_LAVA));
+ : emptyBucket(level, pos, player, hand, itemInHand, Blocks.LAVA_CAULDRON.defaultBlockState(), SoundEvents.BUCKET_EMPTY_LAVA, hitDirection)); // Paper - add hitDirection
- : emptyBucket(level, pos, player, hand, itemInHand, Blocks.LAVA_CAULDRON.defaultBlockState(), SoundEvents.BUCKET_EMPTY_LAVA);
+ : emptyBucket(level, pos, player, hand, itemInHand, Blocks.LAVA_CAULDRON.defaultBlockState(), SoundEvents.BUCKET_EMPTY_LAVA, hitDirection); // Paper - add hitDirection
}
private static InteractionResult fillPowderSnowInteraction(
- final BlockState state, final Level level, final BlockPos pos, final Player player, final InteractionHand hand, final ItemStack itemInHand
+ final BlockState state, final Level level, final BlockPos pos, final Player player, final InteractionHand hand, final ItemStack itemInHand, final net.minecraft.core.Direction hitDirection // Paper - add hitDirection
) {
return (InteractionResult)(isUnderWater(level, pos)
return isUnderWater(level, pos)
? InteractionResult.CONSUME
@@ -254,22 +_,27 @@
@@ -254,12 +_,12 @@
hand,
itemInHand,
Blocks.POWDER_SNOW_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3),
- SoundEvents.BUCKET_EMPTY_POWDER_SNOW
+ SoundEvents.BUCKET_EMPTY_POWDER_SNOW, hitDirection // Paper - add hitDirection
));
);
}
private static InteractionResult shulkerBoxInteraction(
@@ -245,23 +247,23 @@
) {
Block block = Block.byItem(itemInHand.getItem());
if (!(block instanceof ShulkerBoxBlock)) {
return InteractionResult.TRY_WITH_EMPTY_HAND;
} else {
if (!level.isClientSide()) {
+ // CraftBukkit start
+ if (!LayeredCauldronBlock.lowerFillLevel(state, level, pos, player, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.SHULKER_WASH)) {
+ return InteractionResult.SUCCESS;
+ }
+ // CraftBukkit end
ItemStack cleanedShulkerBox = itemInHand.transmuteCopy(Blocks.SHULKER_BOX, 1);
player.setItemInHand(hand, ItemUtils.createFilledResult(itemInHand, player, cleanedShulkerBox, false));
player.awardStat(Stats.CLEAN_SHULKER_BOX);
- LayeredCauldronBlock.lowerFillLevel(state, level, pos);
+ // LayeredCauldronBlock.lowerFillLevel(state, level, pos); // CraftBukkit
}
@@ -267,17 +_,22 @@
}
return InteractionResult.SUCCESS;
@@ -277,18 +_,23 @@
if (!level.isClientSide()) {
+ // CraftBukkit start
+ if (!LayeredCauldronBlock.lowerFillLevel(state, level, pos, player, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.SHULKER_WASH)) {
+ return InteractionResult.SUCCESS;
+ }
+ // CraftBukkit end
ItemStack cleanedShulkerBox = itemInHand.transmuteCopy(Blocks.SHULKER_BOX, 1);
player.setItemInHand(hand, ItemUtils.createFilledResult(itemInHand, player, cleanedShulkerBox, false));
player.awardStat(Stats.CLEAN_SHULKER_BOX);
- LayeredCauldronBlock.lowerFillLevel(state, level, pos);
+ // LayeredCauldronBlock.lowerFillLevel(state, level, pos); // CraftBukkit
}
return InteractionResult.SUCCESS;
}
private static InteractionResult bannerInteraction(
@@ -270,24 +272,24 @@
) {
BannerPatternLayers patterns = itemInHand.getOrDefault(DataComponents.BANNER_PATTERNS, BannerPatternLayers.EMPTY);
if (patterns.layers().isEmpty()) {
return InteractionResult.TRY_WITH_EMPTY_HAND;
} else {
if (!level.isClientSide()) {
+ // CraftBukkit start
+ if (!LayeredCauldronBlock.lowerFillLevel(state, level, pos, player, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.BANNER_WASH)) {
+ return InteractionResult.SUCCESS;
+ }
+ // CraftBukkit end
ItemStack cleanedBanner = itemInHand.copyWithCount(1);
cleanedBanner.set(DataComponents.BANNER_PATTERNS, patterns.removeLast());
player.setItemInHand(hand, ItemUtils.createFilledResult(itemInHand, player, cleanedBanner, false));
player.awardStat(Stats.CLEAN_BANNER);
- LayeredCauldronBlock.lowerFillLevel(state, level, pos);
+ // LayeredCauldronBlock.lowerFillLevel(state, level, pos); // CraftBukkit
}
@@ -285,27 +_,37 @@
}
return InteractionResult.SUCCESS;
@@ -296,15 +_,20 @@
if (!level.isClientSide()) {
+ // CraftBukkit start
+ if (!LayeredCauldronBlock.lowerFillLevel(state, level, pos, player, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.BANNER_WASH)) {
+ return InteractionResult.SUCCESS;
+ }
+ // CraftBukkit end
ItemStack cleanedBanner = itemInHand.copyWithCount(1);
cleanedBanner.set(DataComponents.BANNER_PATTERNS, patterns.removeLast());
player.setItemInHand(hand, ItemUtils.createFilledResult(itemInHand, player, cleanedBanner, false));
player.awardStat(Stats.CLEAN_BANNER);
- LayeredCauldronBlock.lowerFillLevel(state, level, pos);
+ // LayeredCauldronBlock.lowerFillLevel(state, level, pos); // CraftBukkit
}
return InteractionResult.SUCCESS;
}
private static InteractionResult dyedItemIteration(
@@ -296,17 +298,18 @@
) {
if (!itemInHand.has(DataComponents.DYED_COLOR)) {
return InteractionResult.TRY_WITH_EMPTY_HAND;
} else {
if (!level.isClientSide()) {
+ // CraftBukkit start
+ if (!LayeredCauldronBlock.lowerFillLevel(state, level, pos, player, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.ARMOR_WASH)) {
+ return InteractionResult.SUCCESS;
+ }
+ // CraftBukkit end
itemInHand.remove(DataComponents.DYED_COLOR);
player.awardStat(Stats.CLEAN_ARMOR);
- LayeredCauldronBlock.lowerFillLevel(state, level, pos);
+ // LayeredCauldronBlock.lowerFillLevel(state, level, pos); // CraftBukkit
}
}
return InteractionResult.SUCCESS;
if (!level.isClientSide()) {
+ // CraftBukkit start
+ if (!LayeredCauldronBlock.lowerFillLevel(state, level, pos, player, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.ARMOR_WASH)) {
+ return InteractionResult.SUCCESS;
+ }
+ // CraftBukkit end
itemInHand.remove(DataComponents.DYED_COLOR);
player.awardStat(Stats.CLEAN_ARMOR);
- LayeredCauldronBlock.lowerFillLevel(state, level, pos);
+ // LayeredCauldronBlock.lowerFillLevel(state, level, pos); // CraftBukkit
}
return InteractionResult.SUCCESS;
Loaded 100 of 526 files, more files were not shown because too many files have changed in this diff. Show more