Compare commits

..
Author SHA1 Message Date
Jake Potrebic 592f013c5c Add RegistryRetriever for future use in registry API 2026-05-08 21:56:30 +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
222 changed files with 2849 additions and 2277 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 -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> {
}
}
@@ -5,6 +5,7 @@ import io.papermc.paper.registry.tag.Tag;
import io.papermc.paper.registry.tag.TagKey;
import org.bukkit.Keyed;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
/**
* Event object for {@link RegistryEventProvider#compose()}. This
@@ -32,6 +33,19 @@ public interface RegistryComposeEvent<T, B extends RegistryBuilder<T>> extends R
* @param tagKey the tag key
* @return the tag
* @param <V> the tag value type
* @deprecated use {@link #retriever()}
*/
<V extends Keyed> Tag<V> getOrCreateTag(TagKey<V> tagKey); // TODO remove Keyed
@Deprecated(forRemoval = true)
default <V extends Keyed> Tag<V> getOrCreateTag(TagKey<V> tagKey) { // TODO remove Keyed
return this.retriever().getOrCreateTag(tagKey);
}
/**
* Gets the registry retriever, which can be used to retrieve or create
* entries in the registry.
*
* @return the registry retriever
*/
@Contract(pure = true)
RegistryRetriever retriever();
}
@@ -6,6 +6,7 @@ import io.papermc.paper.registry.tag.Tag;
import io.papermc.paper.registry.tag.TagKey;
import org.bukkit.Keyed;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
/**
* Event object for {@link RegistryEventProvider#entryAdd()}. This
@@ -40,6 +41,19 @@ public interface RegistryEntryAddEvent<T, B extends RegistryBuilder<T>> extends
* @param tagKey the tag key
* @return the tag
* @param <V> the tag value type
* @deprecated use {@link #retriever()}
*/
<V extends Keyed> Tag<V> getOrCreateTag(TagKey<V> tagKey);
@Deprecated(forRemoval = true)
default <V extends Keyed> Tag<V> getOrCreateTag(final TagKey<V> tagKey) { // TODO remove Keyed
return this.retriever().getOrCreateTag(tagKey);
}
/**
* Gets the registry retriever, which can be used to retrieve or create
* entries in the registry.
*
* @return the registry retriever
*/
@Contract(pure = true)
RegistryRetriever retriever();
}
@@ -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() {
@@ -0,0 +1,35 @@
package io.papermc.paper.registry.event;
import io.papermc.paper.registry.TypedKey;
import io.papermc.paper.registry.tag.Tag;
import io.papermc.paper.registry.tag.TagKey;
import org.bukkit.Keyed;
import org.jetbrains.annotations.ApiStatus;
@ApiStatus.NonExtendable
public interface RegistryRetriever {
/**
* Gets or creates a tag for the given tag key. This tag
* is then required to be filled either from the built-in or
* custom datapack.
*
* @param tagKey the tag key
* @param <V> the tag value type
* @return the tag
*/
<V extends Keyed> Tag<V> getOrCreateTag(TagKey<V> tagKey); // TODO remove Keyed
/**
* Gets or creates an empty instance of the given {@link TypedKey}.
* <br>
* Can only create instances if the applicable registry still supports adding
* new references. If the registry doesn't, this method will throw an exception.
*
* @param key the key to get or create an instance for
* @param <V> the type of the value associated with the key
* @return an instance of the given key, or a new instance if it does not exist
* @throws IllegalStateException if this registry does not support adding new references and the key does not exist
*/
<V extends Keyed> V getOrCreate(TypedKey<V> key); // TODO remove Keyed
}
@@ -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>
@@ -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.
@@ -3349,8 +3349,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
*/
@@ -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(
+1 -1
View File
@@ -15,7 +15,7 @@ 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")
paperclip("io.papermc:paperclip:3.0.4")
}
paperweight {
@@ -23277,10 +23277,10 @@ index 1cd5901c8e4eb4114ab347a393fc37095c8642d2..fadce1c051e6c7bc0c3d041c0cc8ecd7
+}
diff --git a/io/papermc/paper/command/subcommands/ChunkDebugCommand.java b/io/papermc/paper/command/subcommands/ChunkDebugCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..09f1a74740f898559edc8892ae59aca9b89a6fb3
index 0000000000000000000000000000000000000000..d86bd11e0155f767c3bf66ac05fa4acf6d2bfccb
--- /dev/null
+++ b/io/papermc/paper/command/subcommands/ChunkDebugCommand.java
@@ -0,0 +1,277 @@
@@ -0,0 +1,269 @@
+package io.papermc.paper.command.subcommands;
+
+import ca.spottedleaf.moonrise.common.util.JsonUtil;
@@ -23301,6 +23301,8 @@ index 0000000000000000000000000000000000000000..09f1a74740f898559edc8892ae59aca9
+import net.minecraft.world.level.chunk.LevelChunk;
+import net.minecraft.world.level.chunk.ProtoChunk;
+import org.bukkit.Bukkit;
+import org.bukkit.NamespacedKey;
+import org.bukkit.World;
+import org.bukkit.command.CommandSender;
+import org.bukkit.craftbukkit.CraftWorld;
+import org.checkerframework.checker.nullness.qual.NonNull;
@@ -23327,44 +23329,33 @@ index 0000000000000000000000000000000000000000..09f1a74740f898559edc8892ae59aca9
+
+ @Override
+ public List<String> tabComplete(final CommandSender sender, final String subCommand, final String[] args) {
+ switch (subCommand) {
+ case "debug" -> {
+ return switch (subCommand) {
+ case "debug":
+ if (args.length == 1) {
+ return CommandUtil.getListMatchingLast(sender, args, "help", "chunks");
+ yield CommandUtil.getListMatchingLast(sender, args, "help", "chunks");
+ }
+ }
+ case "holderinfo" -> {
+ List<String> worldNames = new ArrayList<>();
+ worldNames.add("*");
+ for (org.bukkit.World world : Bukkit.getWorlds()) {
+ worldNames.add(world.getKey().toString());
+ case "chunkinfo", "holderinfo":
+ if (args.length >= 1 && (args.length == 1 || !args[0].equals("*"))) {
+ yield CommandUtil.getListMatchingLast(
+ sender,
+ args,
+ CommandUtil.getWorldSuggestions(sender.getServer(), args.length == 1)
+ );
+ }
+ if (args.length == 1) {
+ return CommandUtil.getListMatchingLast(sender, args, worldNames);
+ }
+ }
+ case "chunkinfo" -> {
+ List<String> worldNames = new ArrayList<>();
+ worldNames.add("*");
+ for (org.bukkit.World world : Bukkit.getWorlds()) {
+ worldNames.add(world.getKey().toString());
+ }
+ if (args.length == 1) {
+ return CommandUtil.getListMatchingLast(sender, args, worldNames);
+ }
+ }
+ }
+ return Collections.emptyList();
+ default:
+ yield Collections.emptyList();
+ };
+ }
+
+ private void doChunkInfo(final CommandSender sender, final String[] args) {
+ List<org.bukkit.World> worlds;
+ List<World> worlds;
+ if (args.length < 1 || args[0].equals("*")) {
+ worlds = Bukkit.getWorlds();
+ worlds = Bukkit.getServer().getWorlds();
+ } else {
+ worlds = new ArrayList<>(args.length);
+ for (final String arg : args) {
+ org.bukkit.@Nullable World world = Bukkit.getWorld(arg);
+ @Nullable NamespacedKey key = NamespacedKey.fromString(arg);
+ @Nullable World world = key == null ? null : sender.getServer().getWorld(key);
+ if (world == null) {
+ sender.sendMessage(text("World '" + arg + "' is invalid", RED));
+ return;
@@ -23379,7 +23370,7 @@ index 0000000000000000000000000000000000000000..09f1a74740f898559edc8892ae59aca9
+ int accumulatedTicking = 0;
+ int accumulatedEntityTicking = 0;
+
+ for (final org.bukkit.World bukkitWorld : worlds) {
+ for (final World bukkitWorld : worlds) {
+ final ServerLevel world = ((CraftWorld) bukkitWorld).getHandle();
+
+ int total = 0;
@@ -23390,7 +23381,7 @@ index 0000000000000000000000000000000000000000..09f1a74740f898559edc8892ae59aca9
+
+ for (final NewChunkHolder holder : ((ChunkSystemServerLevel)world).moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolders()) {
+ final NewChunkHolder.ChunkCompletion completion = holder.getLastChunkCompletion();
+ final ChunkAccess chunk = completion == null ? null : completion.chunk();
+ final @Nullable ChunkAccess chunk = completion == null ? null : completion.chunk();
+
+ if (!(chunk instanceof LevelChunk fullChunk)) {
+ continue;
@@ -23424,7 +23415,7 @@ index 0000000000000000000000000000000000000000..09f1a74740f898559edc8892ae59aca9
+ accumulatedTicking += blockTicking;
+ accumulatedEntityTicking += entityTicking;
+
+ sender.sendMessage(text().append(text("Chunks in ", BLUE), text(bukkitWorld.key().toString(), GREEN), text(":")));
+ sender.sendMessage(text().append(text("Chunks in ", BLUE), text(bukkitWorld.key().asString(), GREEN), text(":")));
+ sender.sendMessage(text().color(DARK_AQUA).append(
+ text("Total: ", BLUE), text(total),
+ text(" Inactive: ", BLUE), text(inactive),
@@ -23446,13 +23437,14 @@ index 0000000000000000000000000000000000000000..09f1a74740f898559edc8892ae59aca9
+ }
+
+ private void doHolderInfo(final CommandSender sender, final String[] args) {
+ List<org.bukkit.World> worlds;
+ List<World> worlds;
+ if (args.length < 1 || args[0].equals("*")) {
+ worlds = Bukkit.getWorlds();
+ worlds = Bukkit.getServer().getWorlds();
+ } else {
+ worlds = new ArrayList<>(args.length);
+ for (final String arg : args) {
+ org.bukkit.@Nullable World world = Bukkit.getWorld(arg);
+ @Nullable NamespacedKey key = NamespacedKey.fromString(arg);
+ @Nullable World world = key == null ? null : sender.getServer().getWorld(key);
+ if (world == null) {
+ sender.sendMessage(text("World '" + arg + "' is invalid", RED));
+ return;
@@ -23468,7 +23460,7 @@ index 0000000000000000000000000000000000000000..09f1a74740f898559edc8892ae59aca9
+ int accumulatedProtoChunk = 0;
+ int accumulatedFullChunk = 0;
+
+ for (final org.bukkit.World bukkitWorld : worlds) {
+ for (final World bukkitWorld : worlds) {
+ final ServerLevel world = ((CraftWorld) bukkitWorld).getHandle();
+
+ int total = 0;
@@ -23480,7 +23472,7 @@ index 0000000000000000000000000000000000000000..09f1a74740f898559edc8892ae59aca9
+
+ for (final NewChunkHolder holder : ((ChunkSystemServerLevel)world).moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolders()) {
+ final NewChunkHolder.ChunkCompletion completion = holder.getLastChunkCompletion();
+ final ChunkAccess chunk = completion == null ? null : completion.chunk();
+ final @Nullable ChunkAccess chunk = completion == null ? null : completion.chunk();
+
+ ++total;
+
@@ -23506,7 +23498,7 @@ index 0000000000000000000000000000000000000000..09f1a74740f898559edc8892ae59aca9
+ accumulatedProtoChunk += protoChunk;
+ accumulatedFullChunk += fullChunk;
+
+ sender.sendMessage(text().append(text("Chunks in ", BLUE), text(bukkitWorld.getKey().toString(), GREEN), text(":")));
+ sender.sendMessage(text().append(text("Chunks in ", BLUE), text(bukkitWorld.key().asString(), GREEN), text(":")));
+ sender.sendMessage(text().color(DARK_AQUA).append(
+ text("Total: ", BLUE), text(total),
+ text(" Unloadable: ", BLUE), text(canUnload),
@@ -23838,7 +23830,7 @@ index 3c1490ac7c259da04031db2f170e0c0a5f512191..470d7c770ae9d045b97e2df145cfe3cf
}
}
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 2d9106648f8921ef60c2a24e166d17eb8ce01b48..cea3bd30d326ece39298262401681d77fe064464 100644
index d22f55c63eb950e1b979280612eb9f155e986406..93690b360e5eb1b8d2f8ab2198aa087c9be7ffe2 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -191,7 +191,7 @@ import net.minecraft.world.scores.ScoreboardSaveData;
@@ -23943,7 +23935,7 @@ index 2d9106648f8921ef60c2a24e166d17eb8ce01b48..cea3bd30d326ece39298262401681d77
public MinecraftServer(
// CraftBukkit start
@@ -843,7 +929,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -853,7 +939,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
level.setSpawnSettings(level.isSpawningMonsters()); // Paper - per level difficulty (from setDifficulty(ServerLevel, Difficulty, boolean))
this.updateEffectiveRespawnData();
this.forceTicks = false; // CraftBukkit
@@ -23952,7 +23944,7 @@ index 2d9106648f8921ef60c2a24e166d17eb8ce01b48..cea3bd30d326ece39298262401681d77
new org.bukkit.event.world.WorldLoadEvent(level.getWorld()).callEvent(); // Paper - call WorldLoadEvent
}
@@ -866,6 +952,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -876,6 +962,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
public abstract boolean shouldRconBroadcast();
public boolean saveAllChunks(final boolean silent, final boolean flush, final boolean force) {
@@ -23964,7 +23956,7 @@ index 2d9106648f8921ef60c2a24e166d17eb8ce01b48..cea3bd30d326ece39298262401681d77
this.scoreboard.storeToSaveDataIfDirty(this.getDataStorage().computeIfAbsent(ScoreboardSaveData.TYPE));
boolean result = false;
@@ -874,7 +965,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -884,7 +975,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
LOGGER.info("Saving chunks for level '{}'/{}", level, level.dimension().identifier());
}
@@ -23973,7 +23965,7 @@ index 2d9106648f8921ef60c2a24e166d17eb8ce01b48..cea3bd30d326ece39298262401681d77
result = true;
}
@@ -971,7 +1062,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -981,7 +1072,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
}
@@ -23982,7 +23974,7 @@ index 2d9106648f8921ef60c2a24e166d17eb8ce01b48..cea3bd30d326ece39298262401681d77
this.nextTickTimeNanos = Util.getNanos() + TimeUtil.NANOSECONDS_PER_MILLISECOND;
for (ServerLevel levelx : this.getAllLevels()) {
@@ -982,17 +1073,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -992,17 +1083,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.waitUntilNextTick();
}
@@ -24007,7 +23999,7 @@ index 2d9106648f8921ef60c2a24e166d17eb8ce01b48..cea3bd30d326ece39298262401681d77
this.isSaving = false;
this.savedDataStorage.close();
@@ -1013,6 +1101,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1023,6 +1111,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.services().nameToIdCache().save(false); // Paper - Perf: Async GameProfileCache saving
}
// Spigot end
@@ -24022,7 +24014,7 @@ index 2d9106648f8921ef60c2a24e166d17eb8ce01b48..cea3bd30d326ece39298262401681d77
// Paper start - Improved watchdog support - move final shutdown items here
Util.shutdownExecutors();
this.onServerExit();
@@ -1107,16 +1203,31 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1117,16 +1213,31 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
// execute small amounts of other tasks just in case the number of tasks we are
// draining is large - chunk system and packet processing may be latency sensitive
@@ -24057,7 +24049,7 @@ index 2d9106648f8921ef60c2a24e166d17eb8ce01b48..cea3bd30d326ece39298262401681d77
profiler.pop(); // moonrise:run_all_chunk
profiler.pop(); // moonrise:run_all_tasks
@@ -1417,6 +1528,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1427,6 +1538,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
private boolean pollTaskInternal() {
if (super.pollTask()) {
@@ -24065,7 +24057,7 @@ index 2d9106648f8921ef60c2a24e166d17eb8ce01b48..cea3bd30d326ece39298262401681d77
return true;
} else {
boolean ret = false; // Paper - force execution of all worlds, do not just bias the first
@@ -1558,6 +1670,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1568,6 +1680,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
// Paper - improve tick loop - moved into runAllTasksAtTickStart
this.runAllTasksAtTickStart(); // Paper - improve tick loop
this.tickServer(sprinting ? () -> false : this::haveTime);
@@ -24079,7 +24071,7 @@ index 2d9106648f8921ef60c2a24e166d17eb8ce01b48..cea3bd30d326ece39298262401681d77
this.tickFrame.end();
this.recordEndOfTick(); // Paper - improve tick loop
profiler.pop();
@@ -2600,6 +2719,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -2618,6 +2737,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
}
@@ -26479,7 +26471,7 @@ index 045d4b0ab26622e2763d1583e1e8fcb83ba44427..ddffd36d8213a40dc3f6cbb0ef0f33a0
}
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
index 319335341617051c5b78578465f2224f78c3ea3d..fcb23814c2539f4e31ed5191e03cc28f4e7f9c2d 100644
index bf8496c8cadb29db9c46015e202e7d59e2ed2f5e..f4e033fb5e5b116245dd4a381366c4e749a73f93 100644
--- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -54,7 +54,7 @@ import net.minecraft.world.level.storage.SavedDataStorage;
@@ -27004,7 +26996,7 @@ index de75dda2e6f17f0ae6e7f6a5620fd3eac9d389e5..6774d12d04fa848624fb8bf7a3c2b8bc
List<Entity> passengers = this.entity.getPassengers();
if (!passengers.equals(this.lastPassengers)) {
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01dee9c2a60 100644
index f39d48821d728232472ebc5bd6868e6a0d644721..91d27b90a9c9c348d3f93624ff8dbff8a70f522c 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -182,7 +182,7 @@ import net.minecraft.world.waypoints.WaypointTransmitter;
@@ -27025,7 +27017,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
private final GameEventDispatcher gameEventDispatcher;
public boolean noSave;
private final SleepStatus sleepStatus;
@@ -275,12 +275,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -276,12 +276,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
public final void loadChunksForMoveAsync(AABB box, ca.spottedleaf.concurrentutil.util.Priority priority,
java.util.function.Consumer<List<net.minecraft.world.level.chunk.ChunkAccess>> onLoad) {
@@ -27039,7 +27031,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
int minBlockX = Mth.floor(box.minX - 1.0E-7) - 3;
int minBlockZ = Mth.floor(box.minZ - 1.0E-7) - 3;
@@ -299,30 +294,171 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -300,30 +295,171 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
public final void loadChunks(int minChunkX, int minChunkZ, int maxChunkX, int maxChunkZ,
ca.spottedleaf.concurrentutil.util.Priority priority,
java.util.function.Consumer<List<net.minecraft.world.level.chunk.ChunkAccess>> onLoad) {
@@ -27227,7 +27219,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
}
}
}
@@ -336,16 +472,135 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -337,16 +473,135 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
}
}
}
@@ -27370,7 +27362,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
public ServerLevel(
final MinecraftServer server,
@@ -409,18 +664,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -416,18 +671,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
// CraftBukkit end
boolean syncWrites = server.forceSynchronousWrites();
DataFixer fixerUpper = server.getFixerUpper();
@@ -27390,7 +27382,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
this.chunkSource = new ServerChunkCache(
this,
levelStorage,
@@ -431,7 +675,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -438,7 +682,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
this.spigotConfig.viewDistance, // Spigot
this.spigotConfig.simulationDistance, // Spigot
syncWrites,
@@ -27399,7 +27391,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
() -> server.overworld().getDataStorage()
, savedDataStorage // Paper - initialize SavedDataStorage earlier
);
@@ -471,6 +715,20 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -478,6 +722,20 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
this.waypointManager = new ServerWaypointManager(this); // Paper - optimize ServerWaypointManager with locator bar disabled
this.environmentAttributes = EnvironmentAttributeSystem.builder().addDefaultLayers(this).build();
this.updateSkyBrightness();
@@ -27420,7 +27412,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit
}
@@ -615,8 +873,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -614,8 +872,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
profiler.push("checkDespawn");
entity.checkDespawn();
profiler.pop();
@@ -27430,7 +27422,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
Entity vehicle = entity.getVehicle();
if (vehicle != null) {
if (!vehicle.isRemoved() && vehicle.hasPassenger(entity)) {
@@ -640,7 +897,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -642,7 +899,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
}
profiler.push("entityManagement");
@@ -27439,7 +27431,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
profiler.pop();
profiler.push("debugSynchronizers");
if (this.debugSynchronizers.hasAnySubscriberFor(DebugSubscriptions.NEIGHBOR_UPDATES)) {
@@ -657,7 +914,10 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -659,7 +916,10 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@Override
public boolean shouldTickBlocksAt(final long chunkPos) {
@@ -27451,7 +27443,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
}
protected void tickTime() {
@@ -681,7 +941,60 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -683,7 +943,60 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
this.players.stream().filter(LivingEntity::isSleeping).collect(Collectors.toList()).forEach(player -> player.stopSleepInBed(false, false));
}
@@ -27512,7 +27504,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
ChunkPos chunkPos = chunk.getPos();
int minX = chunkPos.getMinBlockX();
int minZ = chunkPos.getMinBlockZ();
@@ -690,7 +1003,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -692,7 +1005,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
if (!this.paperConfig().environment.disableIceAndSnow) { // Paper - Option to disable ice and snow
for (int i = 0; i < tickSpeed; i++) {
@@ -27521,7 +27513,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
this.tickPrecipitation(this.getBlockRandomPos(minX, 0, minZ, 15));
}
}
@@ -698,31 +1011,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -700,31 +1013,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
profiler.popPush("tickBlocks");
if (tickSpeed > 0) {
@@ -27554,7 +27546,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
}
profiler.pop();
@@ -1045,6 +1334,12 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -1047,6 +1336,12 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
if (fluidState.is(type)) {
fluidState.tick(this, pos, blockState);
}
@@ -27567,7 +27559,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
}
private void tickBlock(final BlockPos pos, final Block type) {
@@ -1052,6 +1347,12 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -1054,6 +1349,12 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
if (state.is(type)) {
state.tick(this, pos, this.random);
}
@@ -27580,7 +27572,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
}
// Paper start - log detailed entity tick information
@@ -1136,6 +1437,11 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -1138,6 +1439,11 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
}
public void save(final @Nullable ProgressListener progressListener, final boolean flush, final boolean noSave) {
@@ -27592,7 +27584,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
ServerChunkCache chunkSource = this.getChunkSource();
if (!noSave) {
new org.bukkit.event.world.WorldSaveEvent(this.getWorld()).callEvent(); // CraftBukkit
@@ -1148,13 +1454,18 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -1150,13 +1456,18 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
progressListener.progressStage(Component.translatable("menu.savingChunks"));
}
@@ -27616,7 +27608,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
}
private void saveLevelData(final boolean sync) {
@@ -1275,7 +1586,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -1277,7 +1588,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
this.removePlayerImmediately((ServerPlayer)existing, Entity.RemovalReason.DISCARDED);
}
@@ -27625,7 +27617,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
}
// CraftBukkit start
@@ -1306,7 +1617,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -1308,7 +1619,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
}
// CraftBukkit end
@@ -27634,7 +27626,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
}
}
@@ -1317,7 +1628,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -1319,7 +1630,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
public boolean tryAddFreshEntityWithPassengers(final Entity entity, final org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) {
// CraftBukkit end
@@ -27643,7 +27635,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
return false;
} else {
this.addFreshEntityWithPassengers(entity, reason); // CraftBukkit
@@ -2111,7 +2422,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -2112,7 +2423,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
}
}
@@ -27652,7 +27644,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
output.write(String.format(Locale.ROOT, "block_entity_tickers: %d\n", this.blockEntityTickers.size()));
output.write(String.format(Locale.ROOT, "block_ticks: %d\n", this.getBlockTicks().count()));
output.write(String.format(Locale.ROOT, "fluid_ticks: %d\n", this.getFluidTicks().count()));
@@ -2129,13 +2440,13 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -2130,13 +2441,13 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
Path chunks = rootDir.resolve("chunks.csv");
try (Writer output = Files.newBufferedWriter(chunks)) {
@@ -27668,7 +27660,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
}
Path entities = rootDir.resolve("entities.csv");
@@ -2230,8 +2541,8 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -2231,8 +2542,8 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
Locale.ROOT,
"players: %s, entities: %s [%s], block_entities: %d [%s], block_ticks: %d, fluid_ticks: %d, chunk_source: %s",
this.players.size(),
@@ -27679,7 +27671,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
this.blockEntityTickers.size(),
getTypeCount(this.blockEntityTickers, TickingBlockEntity::getType),
this.getBlockTicks().count(),
@@ -2264,15 +2575,25 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -2265,15 +2576,25 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@Override
public LevelEntityGetter<Entity> getEntities() {
org.spigotmc.AsyncCatcher.catchOp("Chunk getEntities call"); // Spigot
@@ -27708,7 +27700,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
}
public void startTickingChunk(final LevelChunk levelChunk) {
@@ -2289,8 +2610,8 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -2290,8 +2611,8 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
public void waitForEntities(final ChunkPos centerChunk, final int radius) {
List<ChunkPos> chunks = ChunkPos.rangeClosed(centerChunk, radius).toList();
@@ -27719,7 +27711,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
for (ChunkPos chunk : chunks) {
if (!this.areEntitiesLoaded(chunk.pack())) {
@@ -2311,28 +2632,38 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -2312,28 +2633,38 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@Override
public void close() throws IOException {
super.close();
@@ -27764,7 +27756,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
}
public boolean anyPlayerCloseEnoughForSpawning(final BlockPos pos) {
@@ -2349,7 +2680,10 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -2350,7 +2681,10 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
}
public boolean canSpawnEntitiesInChunk(final ChunkPos pos) {
@@ -27776,7 +27768,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
}
@Override
@@ -2397,7 +2731,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -2398,7 +2732,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
public CrashReportCategory fillReportDetails(final CrashReport report) {
CrashReportCategory category = super.fillReportDetails(report);
WeatherData weatherData = this.getWeatherData();
@@ -28487,11 +28479,11 @@ index 83af9ee3ba150da85b9b694cd76a5fabb5b2d8ef..1fb40837bd02672850ec9adc2797190d
PrepareSpawnTask.this.loadListener.updateFocus(this.spawnLevel.dimension(), spawnChunk);
}
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index 0c4de29676732f5e84d7584e1f89d508addc1cc4..0613b053f4a75c0884280011ee092d9d108b3857 100644
index eff42b94fd182abace988d9649952e04dc35b268..02605ebe6ef10727badadbf7fc4cec52583df965 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -983,8 +983,8 @@ public abstract class PlayerList {
player.connection.send(this.server.clockManager().createFullSyncPacket());
@@ -982,8 +982,8 @@ public abstract class PlayerList {
player.connection.send(level.clockManager().createFullSyncPacket(player)); // Paper - per-world time; per-player time
player.connection.send(new ClientboundSetDefaultSpawnPositionPacket(level.getRespawnData()));
// Paper start - view distances
- player.connection.send(new ClientboundSetChunkCacheRadiusPacket(io.papermc.paper.FeatureHooks.getViewDistance(level)));
@@ -28501,7 +28493,7 @@ index 0c4de29676732f5e84d7584e1f89d508addc1cc4..0613b053f4a75c0884280011ee092d9d
// Paper end - view distances
if (level.isRaining()) {
// CraftBukkit start - handle player weather
@@ -1201,7 +1201,7 @@ public abstract class PlayerList {
@@ -1200,7 +1200,7 @@ public abstract class PlayerList {
public void setViewDistance(final int viewDistance) {
this.viewDistance = viewDistance;
@@ -28510,7 +28502,7 @@ index 0c4de29676732f5e84d7584e1f89d508addc1cc4..0613b053f4a75c0884280011ee092d9d
for (ServerLevel level : this.server.getAllLevels()) {
level.getChunkSource().setViewDistance(viewDistance);
@@ -1210,7 +1210,7 @@ public abstract class PlayerList {
@@ -1209,7 +1209,7 @@ public abstract class PlayerList {
public void setSimulationDistance(final int simulationDistance) {
this.simulationDistance = simulationDistance;
@@ -28893,7 +28885,7 @@ index 1ceb7e4a3abd4d9de5133d182d3267d2164918f6..eb2fa32cff6824c14f865c8731df7d08
+ // Paper end - block counting
}
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 3d7bc43e3e367a2328601ac6b74f99361b4be57e..f7b4da570ee28b7128b4d2cd1c67b89e3980bced 100644
index 29896cb17fcf8ab967a937e9b0e102a8354b6889..e087d5596979044fe7fbcf7f2cccdae4e81a3d3a 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -167,7 +167,7 @@ public abstract class Entity
@@ -29366,7 +29358,7 @@ index 3d7bc43e3e367a2328601ac6b74f99361b4be57e..f7b4da570ee28b7128b4d2cd1c67b89e
}
public InteractionResult interact(final Player player, final InteractionHand hand, final Vec3 location) {
@@ -4364,15 +4601,17 @@ public abstract class Entity
@@ -4363,15 +4600,17 @@ public abstract class Entity
}
public Iterable<Entity> getIndirectPassengers() {
@@ -29392,7 +29384,7 @@ index 3d7bc43e3e367a2328601ac6b74f99361b4be57e..f7b4da570ee28b7128b4d2cd1c67b89e
}
public int countPlayerPassengers() {
@@ -4683,6 +4922,15 @@ public abstract class Entity
@@ -4682,6 +4921,15 @@ public abstract class Entity
}
public final void setPosRaw(double x, double y, double z, boolean forceBoundingBoxUpdate) {
@@ -29408,7 +29400,7 @@ index 3d7bc43e3e367a2328601ac6b74f99361b4be57e..f7b4da570ee28b7128b4d2cd1c67b89e
if (!checkPosition(this, x, y, z)) {
return;
}
@@ -4832,6 +5080,12 @@ public abstract class Entity
@@ -4831,6 +5079,12 @@ public abstract class Entity
@Override
public final void setRemoved(final Entity.RemovalReason reason, org.bukkit.event.entity.EntityRemoveEvent.@Nullable Cause cause) { // CraftBukkit - add Bukkit remove cause
@@ -29421,7 +29413,7 @@ index 3d7bc43e3e367a2328601ac6b74f99361b4be57e..f7b4da570ee28b7128b4d2cd1c67b89e
org.bukkit.craftbukkit.event.CraftEventFactory.callEntityRemoveEvent(this, cause); // CraftBukkit
final boolean alreadyRemoved = this.removalReason != null; // Paper - Folia schedulers
if (this.removalReason == null) {
@@ -4842,7 +5096,7 @@ public abstract class Entity
@@ -4841,7 +5095,7 @@ public abstract class Entity
this.stopRiding();
}
@@ -29430,7 +29422,7 @@ index 3d7bc43e3e367a2328601ac6b74f99361b4be57e..f7b4da570ee28b7128b4d2cd1c67b89e
this.levelCallback.onRemove(reason);
this.onRemoval(reason);
// Paper start - Folia schedulers
@@ -4876,7 +5130,7 @@ public abstract class Entity
@@ -4875,7 +5129,7 @@ public abstract class Entity
public boolean shouldBeSaved() {
return (this.removalReason == null || this.removalReason.shouldSave())
&& !this.isPassenger()
@@ -30053,7 +30045,7 @@ index e07a7bda45146686a6ac2d20507df9fc8630514f..f84e651e2a34e76a0a71993332e4be1b
// Paper start - Affects Spawning API
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index 506efca7cec0e268f03c6c070d69afbc853aaf03..995b51d859316c6128b30d930e58dd47224514b2 100644
index 838bad640b7f8e42fc8972918cfc7c1d9807e2b3..c0f901c4ac61a0aa2b480f102feb3669b111f4c0 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -87,6 +87,7 @@ import net.minecraft.world.level.storage.LevelData;
@@ -30725,10 +30717,10 @@ index 506efca7cec0e268f03c6c070d69afbc853aaf03..995b51d859316c6128b30d930e58dd47
+ this.maxSectionY = this.maxY >> 4;
+ this.sectionsCount = this.maxSectionY - this.minSectionY + 1;
+ // Paper end - getblock optimisations - cache world height/sections
this.spigotConfig = new org.spigotmc.SpigotWorldConfig(bukkitName, CraftNamespacedKey.fromMinecraft(dimension.identifier())); // Spigot
final org.bukkit.NamespacedKey worldKey = CraftNamespacedKey.fromMinecraft(dimension.identifier()); // Paper
this.spigotConfig = new org.spigotmc.SpigotWorldConfig(bukkitName, worldKey); // Spigot
this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config
this.generator = generator;
@@ -230,6 +863,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
@@ -231,6 +864,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
this.registryAccess = registryAccess;
this.palettedContainerFactory = PalettedContainerFactory.create(registryAccess);
this.damageSources = new DamageSources(registryAccess);
@@ -30736,7 +30728,7 @@ index 506efca7cec0e268f03c6c070d69afbc853aaf03..995b51d859316c6128b30d930e58dd47
}
// Paper start - Cancel hit for vanished players
@@ -511,7 +1145,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
@@ -512,7 +1146,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
this.setBlocksDirty(pos, blockState, blockState1);
}
@@ -30745,7 +30737,7 @@ index 506efca7cec0e268f03c6c070d69afbc853aaf03..995b51d859316c6128b30d930e58dd47
this.sendBlockUpdated(pos, blockState, state, flags);
}
@@ -844,6 +1478,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
@@ -846,6 +1480,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
// Paper - Fix MC-117075 use removeAll - remove iterator in favour of indexed for loop, ensuring compile error if something uses iter incorrectly
boolean tickBlockEntities = this.tickRateManager().runsNormally();
@@ -30753,7 +30745,7 @@ index 506efca7cec0e268f03c6c070d69afbc853aaf03..995b51d859316c6128b30d930e58dd47
// Paper start - Fix MC-117075 use removeAll
final it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<@Nullable TickingBlockEntity> toRemove = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>();
toRemove.add(null);
@@ -854,6 +1489,11 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
@@ -856,6 +1491,11 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
toRemove.add(ticker); // Paper - Fix MC-117075 use removeAll
} else if (tickBlockEntities && this.shouldTickBlocksAt(ticker.getPos())) {
ticker.tick();
@@ -30765,7 +30757,7 @@ index 506efca7cec0e268f03c6c070d69afbc853aaf03..995b51d859316c6128b30d930e58dd47
}
}
@@ -873,6 +1513,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
@@ -874,6 +1514,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD);
// Paper end - Prevent block entity and entity crashes
}
@@ -30773,7 +30765,7 @@ index 506efca7cec0e268f03c6c070d69afbc853aaf03..995b51d859316c6128b30d930e58dd47
}
// Paper start - Option to prevent armor stands from doing entity lookups
@@ -880,7 +1521,14 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
@@ -881,7 +1522,14 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
public boolean noCollision(@Nullable Entity entity, AABB box) {
if (entity instanceof net.minecraft.world.entity.decoration.ArmorStand && !entity.level().paperConfig().entities.armorStands.doCollisionEntityLookups)
return false;
@@ -30789,7 +30781,7 @@ index 506efca7cec0e268f03c6c070d69afbc853aaf03..995b51d859316c6128b30d930e58dd47
}
// Paper end - Option to prevent armor stands from doing entity lookups
@@ -1025,7 +1673,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
@@ -1026,7 +1674,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
if (!this.isInValidBounds(pos)) {
return null;
} else {
@@ -30798,7 +30790,7 @@ index 506efca7cec0e268f03c6c070d69afbc853aaf03..995b51d859316c6128b30d930e58dd47
? null
: this.getChunkAt(pos).getBlockEntity(pos, LevelChunk.EntityCreationType.IMMEDIATE);
}
@@ -1105,20 +1753,15 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
@@ -1106,20 +1754,15 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
@Override
public List<Entity> getEntities(final @Nullable Entity except, final AABB bb, final Predicate<? super Entity> selector) {
Profiler.get().incrementCounter("getEntities");
@@ -30826,7 +30818,7 @@ index 506efca7cec0e268f03c6c070d69afbc853aaf03..995b51d859316c6128b30d930e58dd47
}
@Override
@@ -1134,33 +1777,94 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
@@ -1135,33 +1778,94 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
this.getEntities(type, bb, selector, output, Integer.MAX_VALUE);
}
@@ -30941,7 +30933,7 @@ index 506efca7cec0e268f03c6c070d69afbc853aaf03..995b51d859316c6128b30d930e58dd47
public <T extends Entity> boolean hasEntities(final EntityTypeTest<Entity, T> type, final AABB bb, final Predicate<? super T> selector) {
Profiler.get().incrementCounter("hasEntities");
@@ -1469,13 +2173,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
@@ -1466,13 +2170,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
// Paper start - allow patching this logic
public final int getEntityCount() {
@@ -32693,7 +32685,7 @@ index 54d57f80b1cca351cac2aa59477a0e57334816e7..94652b00e18ffcbe02f1ad0578d671e2
public @Nullable BlockEntity getBlockEntity(final BlockPos pos) {
return this.wrapped.getBlockEntity(pos);
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
index 1bbb8ca0c4a5087cf35c8af8362d867ef868cbd3..b5b514b292f9fdbe410aba5facec1638682c9075 100644
index d747e7058f78ac1fd02f3dc8e06670ed484cd42b..c0c212fd50cf97ae311035b370e8b4d40d82229e 100644
--- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -64,7 +64,7 @@ import net.minecraft.world.ticks.TickContainerAccess;
@@ -33593,7 +33585,7 @@ index cc5d366f7b485f98901572b1aaf57d8b650d521f..7adb4be57bd9fdc9980b5b7e91e9d9ca
private final Long2ObjectLinkedOpenHashMap<CompletableFuture<BitSet>> regionCacheForBlender = new Long2ObjectLinkedOpenHashMap<>();
private static final int REGION_CACHE_SIZE = 1024;
diff --git a/net/minecraft/world/level/chunk/storage/RegionFile.java b/net/minecraft/world/level/chunk/storage/RegionFile.java
index 728ec122b7af090427cc7511a168336d539835a1..5149635679ec03d881e63effbd79fc193d7e52c8 100644
index 9409cc5c8dbd12ce6bdf34843da70170ebce41bd..c9acbdce97cb605a45124bb9d3944070396adc03 100644
--- a/net/minecraft/world/level/chunk/storage/RegionFile.java
+++ b/net/minecraft/world/level/chunk/storage/RegionFile.java
@@ -23,7 +23,7 @@ import net.minecraft.world.level.ChunkPos;
@@ -33625,7 +33617,7 @@ index 728ec122b7af090427cc7511a168336d539835a1..5149635679ec03d881e63effbd79fc19
+ // Paper end - rewrite chunk system
+
public RegionFile(final RegionStorageInfo info, final Path path, final Path externalFileDir, final boolean sync) throws IOException {
this(info, path, externalFileDir, RegionFileVersion.getCompressionFormat(), sync); // Paper - Configurable region compression format
this(info, path, externalFileDir, RegionFileVersion.getSelected(), sync);
}
@@ -201,6 +216,15 @@ public class RegionFile implements AutoCloseable {
}
@@ -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 e11862c7e0617acfb36212cfe349bdade3a5164b..7ae2d491a3b2f379df99ccc649b8bb8cbf2304b9 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -299,6 +299,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -28,7 +28,7 @@ and then catch exceptions and close if they fire.
Part of this commit was authored by: Spottedleaf, sandtechnology
diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java
index 0bc6ab5e498f82c5b3cbdf8dddfed6c8406ad511..9cd6ec17981da310be1d35054af080d6b33beff8 100644
index 1511cbd2e6123a00d22694353ee4989d539fe0f0..104976b3eb04d80e5be5bd020cf16c647b1cde27 100644
--- a/net/minecraft/network/Connection.java
+++ b/net/minecraft/network/Connection.java
@@ -65,7 +65,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -40,7 +40,7 @@ index 0bc6ab5e498f82c5b3cbdf8dddfed6c8406ad511..9cd6ec17981da310be1d35054af080d6
public Channel channel;
public SocketAddress address;
// Spigot start
@@ -123,6 +123,10 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -124,6 +124,10 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
public boolean handledLegacyLoginEvent; // Paper - playerloginevent
public net.minecraft.server.level.@Nullable ServerPlayer savedPlayerForLegacyEvents; // Paper - playerloginevent & PlayerSpawnLocationEvent
public org.bukkit.event.player.PlayerResourcePackStatusEvent.@Nullable Status resourcePackStatus; // Paper
@@ -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<?>> {
@@ -394,11 +398,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<?>> {
@@ -407,7 +438,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<?>> {
@@ -421,21 +452,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<?>> {
@@ -447,16 +499,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<?>> {
@@ -526,6 +619,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<?>> {
@@ -715,7 +809,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<?>> {
@@ -726,7 +820,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<?>> {
@@ -761,4 +855,96 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
public void setBandwidthLogger(final LocalSampleLogger bandwidthLogger) {
this.bandwidthDebugMonitor = new BandwidthDebugMonitor(bandwidthLogger);
}
@@ -371,7 +372,7 @@ 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 387ac8a4e67ef07df73ce98d0add0937314ee497..9ae7c79e5088d9e1e3fb7e0dca00bb15fee118b6 100644
--- a/net/minecraft/server/network/ServerConnectionListener.java
+++ b/net/minecraft/server/network/ServerConnectionListener.java
@@ -52,11 +52,13 @@ public class ServerConnectionListener {
@@ -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 91d27b90a9c9c348d3f93624ff8dbff8a70f522c..f4bbccee849ee3711a3b838884e4bcf4e24da039 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 e087d5596979044fe7fbcf7f2cccdae4e81a3d3a..fea6c3b48c4eb162fbdc099fa775bc3161a4dd4e 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 8b0abe9c7a3907419f6e4d55ea22fb9bc749f28b..36c0081e3e8f8776293ae619156cd0828fe5b21d 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
@@ -3372,6 +3372,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 c7c53ef16e104ebc48aeb8719783fb4c45d39fc2..5bbb5cbb51953399c7bce547cd6968eb3c6bd130 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();
+ }
@@ -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 a80e6a23cf29cb44943339101f15565dbc19af1f..26108ac0543c7b4195f149c07d8348b5b15b8bdb 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 {
@@ -816,7 +817,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 c0f901c4ac61a0aa2b480f102feb3669b111f4c0..07c301ba02c7f2c032a29a0ae3bdf95fc522c03b 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
@@ -266,10 +266,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 104976b3eb04d80e5be5bd020cf16c647b1cde27..5a23f95aff982ecc58d2e2bc994f4e495285069c 100644
--- a/net/minecraft/network/Connection.java
+++ b/net/minecraft/network/Connection.java
@@ -738,11 +738,22 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -740,11 +740,22 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
return connection;
}
@@ -296,7 +296,7 @@ index 9cd6ec17981da310be1d35054af080d6b33beff8..d7ed1621709c491525daaf7adc8e9f0d
public boolean isEncrypted() {
return this.encrypted;
@@ -780,16 +791,17 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -782,16 +793,17 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
public void setupCompression(final int threshold, final boolean validateDecompressed) {
if (threshold >= 0) {
@@ -318,7 +318,7 @@ 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 391435cf3d279c8f5a889702b50f57626864c7bb..19a77c8269c3f1998985d533d9099c7e6cf76af4 100644
--- a/net/minecraft/server/network/ServerConnectionListener.java
+++ b/net/minecraft/server/network/ServerConnectionListener.java
@@ -77,6 +77,10 @@ public class ServerConnectionListener {
@@ -333,10 +333,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 c1edad046cbdd1627edba58bc06aef5c058e79eb..3a0d47a716ce5c5345640ed67ac306101791892b 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);
@@ -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 {
@@ -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 f4bbccee849ee3711a3b838884e4bcf4e24da039..0b9fbf0ae658f0b3e9c6106a42fb70948b6fb9be 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,10 +2347,10 @@ 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 07c301ba02c7f2c032a29a0ae3bdf95fc522c03b..7e4cf8a13e0ff2198cb7b6d80fb3fa343339943b 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
@@ -2153,6 +2153,17 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
return this.palettedContainerFactory;
}
@@ -2374,7 +2369,7 @@ index 329aef0e55664fbdd98c84d4d7cee74cd633e9c3..b9e07d5bd36993c5ac83b843866fabbe
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 a00002774755eb90a0b772fe742b30a5e09452cf..1e863e97bd1dc99ed6e2c0602f78f457ac618965 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()) {
@@ -55,7 +55,7 @@ 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 5e77906d77da82b23fbd6e8d16fbd0847c83f4da..e077797cefffcb9db627cab12c7d88245e5baa69 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
@@ -760,7 +760,7 @@ index 5fa64ff30b8252f139ca7f77c14352c384c8c0e5..eae4963b6d18c5f92871477b93f81c2c
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 +771,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 +791,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 1e0afa9590de7fe492cf29aded96be3db498c51e..df0bb06176ad5cce3ed8785d22d68b2dcbf5a8ca 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(
@@ -823,7 +823,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 6e55cb304fd9a5a01bbe7666dddb882e684cdcf1..6d954064f409ddd2cb7cfd0d0c428ac186f5ca4d 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 248a01f06ec822737a741873166aa4e502a7ba07..c4eb56fc227afd7e9631624537dd8e67ae3a8bea 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -958,7 +958,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -968,7 +968,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
@@ -980,13 +980,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
@@ -1000,11 +994,25 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
return result;
}
@@ -59,7 +59,7 @@ index 9a014a1a198293c9ce02354d8128cb19666055b8..c6691f02a1b2c1540b959fcc07eb8390
boolean result = this.saveAllChunks(silent, flush, force);
this.warnOnLowDiskSpace();
var5 = result;
@@ -1641,9 +1649,31 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1651,9 +1659,31 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
this.ticksUntilAutosave--;
@@ -94,10 +94,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 0b9fbf0ae658f0b3e9c6106a42fb70948b6fb9be..bbeb0f673f2b23d1748e33364dc12ac16528d4c4 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)));
}
@@ -126,7 +126,7 @@ 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 02605ebe6ef10727badadbf7fc4cec52583df965..3d62e4d275f32c8604a1bdaefb6b9b1d4ea6e21f 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -418,6 +418,7 @@ public abstract class PlayerList {
@@ -137,7 +137,7 @@ index 0613b053f4a75c0884280011ee092d9d108b3857..b69bcaa1e27a4fbe9a9568f3c9e18431
this.playerIo.save(player);
ServerStatsCounter stats = player.getStats(); // CraftBukkit
if (stats != null) {
@@ -951,10 +952,24 @@ public abstract class PlayerList {
@@ -950,10 +951,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 fad017de5d9fc31988aa4edf97fb3f9bcbee56c8..06132bde0aed9dd9088c884ee31056b9176b6f90 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
@@ -1751,7 +1796,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
@@ -1770,6 +1815,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 abeca4714053fd0a4fa489d496e2e0b844de40a8..7ed50fc00c58c7781c958df1d828a67adf696f3e 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();
@@ -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,10 +20,10 @@ index 2bc436cdf5180a7943c45fabb9fbbedae6f7db56..f312a7f5b1b2a777ab36b94ce7cbf387
@Override
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index a3edf217dd2ffce19dc1111402975ba12bb86bad..2b6717833b56c3e33f3ac656a376dd5c841dc1bc 100644
index 8c4b40362f77cdea6e57315a6639006ac7ab27c7..a5217deb236ee922d020a7a6a03141a5f67acd77 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1784,32 +1784,22 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1794,32 +1794,22 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
}
@@ -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 fea6c3b48c4eb162fbdc099fa775bc3161a4dd4e..82f6582a92f17bdeb3218c4c1fcac86981fd9c37 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -5147,6 +5147,11 @@ public abstract class Entity
@@ -5146,6 +5146,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 06132bde0aed9dd9088c884ee31056b9176b6f90..08374da2d9b84e8ff13c670d7f45867fb0bdd897 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -1820,9 +1820,14 @@ public class ServerGamePacketListenerImpl
@@ -1819,9 +1819,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,10 +62,10 @@ index ae140a514e1e3200f52c5933f886cc9e820b818a..4a1146cc54bd9272466ffc9bb64f600c
);
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index 8df070a7135411806d42ea692b566c4d8389a47b..06451a21c4bfbcd95063f96246013d63f65af8a3 100644
index fa3809be5c0488c5a1d2639579d68619b0a55de9..c332d48f5cac2f3be93201c66f4f7cdba916cbac 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
@@ -1529,7 +1529,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
if (entity instanceof net.minecraft.world.entity.decoration.ArmorStand && !entity.level().paperConfig().entities.armorStands.doCollisionEntityLookups)
return false;
// Paper start - optimise collisions
@@ -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 af1f5e83bd24e671399af2a9aadc8e11959ec5f4..b9efed1df74651c894a416f1d3db320d4ab20401 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1836,6 +1836,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1850,6 +1850,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
@@ -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 4ac7ef5a1ed1fac04a9fd246212f59919deb498c..e562aec4366d7724f915e3dd5ae440a77e0947c5 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;
@@ -196,7 +196,7 @@ 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 5e11abcfd681d86b1c0e282cf901108822766ce2..3020a3208b14bb483b08cf59a796fb83c2403d1c 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -331,7 +331,7 @@ public abstract class PlayerList {
@@ -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 829bb616e5d72b955248bbc150254c239176ce64..4b228b5c563e40b6f301df22b5c8a7f7988cb072 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,7 +238,7 @@ index 630aec19b88a519fa4ec382fb2f5e9aebda59208..f765bc3d805de16909b0407da633208b
}
// Paper start - Cancel hit for vanished players
@@ -1084,6 +1087,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
@@ -1085,6 +1088,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
}
// CraftBukkit end - capture blockstates
BlockState oldState = chunk.setBlockState(pos, blockState, updateFlags);
@@ -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 9eb6cb86ff70f04863cae3def1006f82bcb4fa6b..da5fdf77dd26d97c8b92bac9a6af8d922c31eb9c 100644
--- a/net/minecraft/world/level/block/entity/BarrelBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/BarrelBlockEntity.java
@@ -66,6 +66,13 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity {
Objects.requireNonNull(BarrelBlockEntity.this);
}
+ // 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 81a293cc13653a9a0336cb25d75273969fee4d16..8ebcce4c4971a11d367fb39e9c7fd280f63594e3 100644
--- a/net/minecraft/world/level/block/entity/ChestBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/ChestBlockEntity.java
@@ -35,6 +35,13 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement
Objects.requireNonNull(ChestBlockEntity.this);
}
+ // 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 eeac644f60760570244380d3c454fd33e37b5a7f..751625124a87de63d933d9c7809e46361e3136a5 100644
--- a/net/minecraft/world/level/block/entity/EnderChestBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/EnderChestBlockEntity.java
@@ -18,6 +18,13 @@ public class EnderChestBlockEntity extends BlockEntity implements LidBlockEntity
Objects.requireNonNull(EnderChestBlockEntity.this);
}
+ // 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,119 +151,119 @@ index 0000000000000000000000000000000000000000..f47c12e9dd6cfa857ca07a764edc22de
+ }
+}
diff --git a/net/minecraft/recipebook/ServerPlaceRecipe.java b/net/minecraft/recipebook/ServerPlaceRecipe.java
index 6d3e3ec045d5b15a435f7217369968b33e082724..b7a3758af337270737041f84d10eb43784e42279 100644
index 31f8aa58b48aeb64e57480490a449b6c46913880..b7ee9fe694ea37eeb58b5dded7967e7d0f338d0d 100644
--- a/net/minecraft/recipebook/ServerPlaceRecipe.java
+++ b/net/minecraft/recipebook/ServerPlaceRecipe.java
@@ -40,6 +40,7 @@ public class ServerPlaceRecipe<R extends Recipe<?>> {
@@ -41,6 +41,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<?>> {
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<?>> {
}
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;
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 (i2 > 0) {
@@ -129,9 +130,11 @@ public class ServerPlaceRecipe<R extends Recipe<?>> {
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;
@@ -165,7 +168,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 da8bab81e135a9b240fbeac0e9e1d2aa12c6212d..f004911c438e6f89d6e079437e3f4104d6cbacbd 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 889b8e6c3762203deccf657b505f6f29cc893526..6710653d033a48c4b3fc267496f93a2548b5c966 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;
@@ -14,7 +14,7 @@ import java.util.Objects;
import org.jspecify.annotations.Nullable;
public class StackedContents<T> {
- public final Reference2IntOpenHashMap<T> amounts = new Reference2IntOpenHashMap<>();
+ public final it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap<T> amounts = new it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap<>(); // Paper - Improve exact choice recipe ingredients (don't use "reference" map)
boolean hasAtLeast(T 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;
@@ -54,7 +54,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> {
@@ -76,13 +76,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 3a8a07712a15167dabd12d4bfd258de11706ce93..1deeb3fd148c5d59250346147681b7e8f2147574 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) {
@@ -79,21 +83,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
@@ -118,6 +123,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,11 +0,0 @@
--- a/net/minecraft/gametest/framework/GameTestHelper.java
+++ b/net/minecraft/gametest/framework/GameTestHelper.java
@@ -929,7 +_,7 @@
public void setTime(final long ticks) {
ServerLevel level = this.getLevel();
- Holder<WorldClock> clock = level.dimensionType().defaultClock().orElseThrow();
+ Holder<WorldClock> clock = level.getClock().orElseThrow();
level.clockManager().setTotalTicks(clock, ticks);
}
@@ -12,11 +12,12 @@
private volatile @Nullable PacketListener disconnectListener;
private volatile @Nullable PacketListener packetListener;
private @Nullable DisconnectionDetails disconnectionDetails;
@@ -81,6 +_,43 @@
@@ -81,6 +_,44 @@
private boolean handlingFault;
private volatile @Nullable DisconnectionDetails delayedDisconnect;
private @Nullable BandwidthDebugMonitor bandwidthDebugMonitor;
+ public String hostname = ""; // CraftBukkit - add field
+ public boolean handleConnectionDisconnectOnNextTick = false; // Paper - Force kill connection ticking
+ // Paper start - NetworkClient implementation
+ public int protocolVersion;
+ public java.net.InetSocketAddress virtualHost;
@@ -220,7 +221,7 @@
}
if (this.tickCount++ % 20 == 0) {
@@ -391,12 +_,13 @@
@@ -391,6 +_,7 @@
}
public void disconnect(final DisconnectionDetails details) {
@@ -228,13 +229,6 @@
if (this.channel == null) {
this.delayedDisconnect = details;
}
if (this.isConnected()) {
- this.channel.close().awaitUninterruptibly();
+ this.channel.close(); // We can't wait as this may be called from an event loop.
this.disconnectionDetails = details;
}
}
@@ -543,6 +_,14 @@
}
}
@@ -45,7 +45,7 @@
}
}
@@ -44,8 +_,29 @@
@@ -44,8 +_,28 @@
this.closed = true;
}
@@ -71,7 +71,6 @@
public void handle() {
+ packetProcessing.push(this.listener); // Paper - detailed watchdog information
+ try { // Paper - detailed watchdog information
+ if (this.listener instanceof net.minecraft.server.network.ServerCommonPacketListenerImpl serverCommonPacketListener && serverCommonPacketListener.processedDisconnect) return; // Paper - Don't handle sync packets for kicked players
if (this.listener.shouldHandleMessage(this.packet)) {
try {
this.packet.handle(this.listener);
@@ -14,9 +14,9 @@
+ // Paper start - Validate Identifier
+ // Check for the max network string length (capped at Short.MAX_VALUE) as well as the max bytes of a StringTag (length written as an unsigned short)
+ final String id = namespace + ":" + path;
+ if (id.length() > Short.MAX_VALUE || io.netty.buffer.ByteBufUtil.utf8MaxBytes(id) > 2 * Short.MAX_VALUE + 1) {
+ throw new IdentifierException("Identifier too long: " + id);
+ final int length = namespace.length() + path.length() + 1;
+ if (length > Short.MAX_VALUE || io.netty.buffer.ByteBufUtil.utf8MaxBytes(length) > 2 * Short.MAX_VALUE + 1) {
+ throw new IdentifierException("Identifier too long: " + length);
+ }
+ // Paper end - Validate Identifier
this.namespace = namespace;
@@ -12,7 +12,7 @@
OptionParser parser = new OptionParser();
OptionSpec<Void> nogui = parser.accepts("nogui");
OptionSpec<Void> initSettings = parser.accepts("initSettings", "Initializes 'server.properties' and 'eula.txt', then quits");
@@ -89,39 +_,96 @@
@@ -89,39 +_,95 @@
parser.printHelpOn(System.err);
return;
}
@@ -72,15 +72,14 @@
}
- if (!eula.hasAgreedToEULA()) {
+ // Spigot start
+ // Paper start - eula system property
+ boolean eulaAgreed = Boolean.getBoolean("com.mojang.eula.agree");
+ if (eulaAgreed) {
+ LOGGER.error("You have used the Spigot command line EULA agreement flag.");
+ LOGGER.error("By using this setting you are indicating your agreement to Mojang's EULA (https://aka.ms/MinecraftEULA).");
+ LOGGER.error("If you do not agree to the above EULA please stop your server and remove this flag immediately.");
+ }
+ if (!eula.hasAgreedToEULA() && !eulaAgreed) {
+ // Spigot end
+ LOGGER.warn("You have used the Paper command line EULA agreement flag.");
+ LOGGER.warn("By using this setting you are indicating your agreement to Mojang's EULA (https://aka.ms/MinecraftEULA).");
+ LOGGER.warn("If you do not agree to the above EULA please stop your server and remove this flag immediately.");
+ } else if (!eula.hasAgreedToEULA()) {
+ // Paper end - eula system property
LOGGER.info("You need to agree to the EULA in order to run the server. Go to eula.txt for more info.");
return;
}
@@ -300,7 +300,7 @@
if (profiledDuration != null) {
profiledDuration.finish(true);
}
@@ -422,33 +_,140 @@
@@ -422,33 +_,150 @@
}
}
@@ -430,9 +430,9 @@
+ );
+ }
+ this.addLevel(serverLevel);
+ this.initWorld(serverLevel);
+ this.initWorld(serverLevel, null);
+ }
+ public void initWorld(ServerLevel overworld) {
+ public void initWorld(ServerLevel overworld, org.bukkit.@Nullable WorldCreator worldCreator) {
+ final net.minecraft.world.level.storage.ServerLevelData levelData = overworld.serverLevelData;
+ final WorldOptions worldOptions = overworld.worldGenSettings.options();
+ final boolean isDebug = this.worldData.isDebugWorld();
@@ -447,7 +447,17 @@
if (!levelData.isInitialized()) {
try {
- setInitialSpawn(overworld, levelData, worldOptions.generateBonusChest(), isDebug, this.levelLoadListener);
+ setInitialSpawn(overworld, levelData, worldOptions.generateBonusChest(), isDebug, overworld.levelLoadListener); // Paper - per world level load listener
+ // Paper start - Allow direct setting spawn location
+ if (worldCreator != null && worldCreator.forcedSpawnPosition() != null) {
+ levelData.setSpawn(LevelData.RespawnData.of(overworld.dimension(),
+ io.papermc.paper.util.MCUtil.toBlockPos(worldCreator.forcedSpawnPosition()),
+ Objects.requireNonNullElse(worldCreator.forcedSpawnYaw(), LevelData.RespawnData.DEFAULT.yaw()),
+ Objects.requireNonNullElse(worldCreator.forcedSpawnPitch(), LevelData.RespawnData.DEFAULT.pitch())
+ ));
+ } else {
+ setInitialSpawn(overworld, levelData, worldOptions.generateBonusChest(), isDebug, overworld.levelLoadListener); // Paper - per world level load listener
+ }
+ // Paper end - Allow direct setting spawn location
levelData.setInitialized(true);
if (isDebug) {
- this.setupDebugLevel(this.worldData);
@@ -491,9 +501,9 @@
+ java.util.Random rand = new java.util.Random(level.getSeed());
+ org.bukkit.Location spawn = level.generator.getFixedSpawnLocation(level.getWorld(), rand);
+
+ if (spawn != null) {
+ if (spawn != null && spawn.getWorld() != null) {
+ if (spawn.getWorld() != level.getWorld()) {
+ throw new IllegalStateException("Cannot set spawn point for " + levelData.getLevelName() + " to be in another world (" + io.papermc.paper.util.MCUtil.getLevelName(level) + ")");
+ throw new IllegalStateException("Cannot set spawn point for " + levelData.getLevelName() + " to be in another world (" + spawn.getWorld().key().asString() + ")");
+ } else {
+ levelData.setSpawn(
+ net.minecraft.world.level.storage.LevelData.RespawnData.of(
@@ -1086,7 +1096,7 @@
ObjectArrayList<NameAndId> sample = new ObjectArrayList<>(sampleSize);
int offset = Mth.nextInt(this.random, 0, players.size() - sampleSize);
@@ -1104,6 +_,32 @@
@@ -1104,12 +_,46 @@
protected void tickChildren(final BooleanSupplier haveTime) {
ProfilerFiller profiler = Profiler.get();
this.getPlayerList().getPlayers().forEach(playerx -> playerx.connection.suspendFlushing());
@@ -1119,25 +1129,31 @@
profiler.push("commandFunctions");
this.getFunctions().tick();
profiler.pop();
@@ -1115,14 +_,28 @@
if (this.tickCount % 20 == 0) {
profiler.push("timeSync");
- this.forceGameTimeSynchronization();
+ // Paper start - per-world game time
+ for (final ServerLevel level : this.getAllLevels()) {
+ this.playerList.broadcastAll(new ClientboundSetTimePacket(level.getGameTime(), Map.of()), level);
if (this.tickRateManager.runsNormally()) {
profiler.push("clocks");
- this.clockManager.tick();
+ // Paper start - per-world time
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().time.affectsAllWorlds) {
+ this.clockManager.tick();
+ } else {
+ for (ServerLevel level : this.getAllLevels()) {
+ level.clockManager().tick();
+ }
+ }
+ // Paper end - per-world game time
+ // Paper end - per-world time
profiler.pop();
+ }
+
}
@@ -1119,10 +_,20 @@
profiler.pop();
}
+ // CraftBukkit start
+ // Run tasks that are waiting on processing
+ while (!this.processQueue.isEmpty()) {
+ this.processQueue.remove().run();
}
+ }
+
profiler.push("levels");
this.updateEffectiveRespawnData();
@@ -1174,6 +1190,19 @@
this.effectiveRespawnData = respawnLevel.getWorldBorderAdjustedRespawnData(respawnData);
}
@@ -1179,7 +_,11 @@
public void forceGameTimeSynchronization() {
ProfilerFiller profiler = Profiler.get();
profiler.push("timeSync");
- this.playerList.broadcastAll(new ClientboundSetTimePacket(this.overworld().getGameTime(), Map.of()));
+ // Paper start - per-world time
+ for (ServerLevel level : this.getAllLevels()) {
+ this.playerList.broadcastAll(new ClientboundSetTimePacket(level.getGameTime(), Map.of()), level);
+ }
+ // Paper end - per-world time
profiler.pop();
}
@@ -1207,6 +_,22 @@
return this.levels.get(dimension);
}
@@ -1534,7 +1563,7 @@
player.connection.send(new ClientboundEntityEventPacket(player, event));
}
} else if (rule == GameRules.LIMITED_CRAFTING || rule == GameRules.IMMEDIATE_RESPAWN) {
@@ -2131,18 +_,19 @@
@@ -2131,20 +_,21 @@
? ClientboundGameEventPacket.LIMITED_CRAFTING
: ClientboundGameEventPacket.IMMEDIATE_RESPAWN;
ClientboundGameEventPacket packet = new ClientboundGameEventPacket(eventType, (Boolean)value ? 1.0F : 0.0F);
@@ -1556,8 +1585,11 @@
- this.updateMobSpawningFlags();
+ level.setSpawnSettings(level.isSpawningMonsters()); // Paper - per-world game rules
} else if (rule == GameRules.ADVANCE_TIME) {
this.getPlayerList().broadcastAll(this.clockManager().createFullSyncPacket());
- this.getPlayerList().broadcastAll(this.clockManager().createFullSyncPacket());
+ level.players().forEach(player -> player.connection.send(level.clockManager().createFullSyncPacket(player))); // Paper - per-world time; per-player time
}
}
@@ -2157,12 +_,14 @@
return this.savedDataStorage;
}
@@ -5,7 +5,7 @@
list.deop(player);
count++;
- source.sendSuccess(() -> Component.translatable("commands.deop.success", players.iterator().next().name()), true);
+ source.sendSuccess(() -> Component.translatable("commands.deop.success", player.name()), true); // Paper - fixes MC-253721
+ source.sendSuccess(() -> Component.translatable("commands.deop.success", player.name()), true); // Paper - fixes MC-307513
}
}
@@ -1,56 +1,108 @@
--- a/net/minecraft/server/commands/TimeCommand.java
+++ b/net/minecraft/server/commands/TimeCommand.java
@@ -169,16 +_,24 @@
@@ -119,7 +_,7 @@
final CommandSourceStack source, final SuggestionsBuilder builder, final Holder<WorldClock> clock
) {
return SharedSuggestionProvider.suggestResource(
- source.getServer().clockManager().commandTimeMarkersForClock(clock).map(ResourceKey::identifier), builder
+ source.getLevel().clockManager().commandTimeMarkersForClock(clock).map(ResourceKey::identifier), builder // Paper - per-world time
);
}
@@ -141,7 +_,7 @@
}
private static int queryTime(final CommandSourceStack source, final Holder<WorldClock> clock) {
- ServerClockManager clockManager = source.getServer().clockManager();
+ ServerClockManager clockManager = source.getLevel().clockManager(); // Paper - per-world time
long totalTicks = clockManager.getTotalTicks(clock);
source.sendSuccess(() -> Component.translatable("commands.time.query.absolute", clock.getRegisteredName(), totalTicks), false);
return wrapTime(totalTicks);
@@ -151,7 +_,7 @@
if (!clock.equals(timeline.value().clock())) {
throw ERROR_WRONG_TIMELINE_FOR_CLOCK.create(clock.getRegisteredName(), timeline.getRegisteredName());
} else {
- ServerClockManager clockManager = source.getServer().clockManager();
+ ServerClockManager clockManager = source.getLevel().clockManager(); // Paper - per-world time
long currentTicks = timeline.value().getCurrentTicks(clockManager);
source.sendSuccess(() -> Component.translatable("commands.time.query.timeline", timeline.getRegisteredName(), currentTicks), false);
return wrapTime(currentTicks);
@@ -162,33 +_,53 @@
if (!clock.equals(timeline.value().clock())) {
throw ERROR_WRONG_TIMELINE_FOR_CLOCK.create(clock.getRegisteredName(), timeline.getRegisteredName());
} else {
- ServerClockManager clockManager = source.getServer().clockManager();
+ ServerClockManager clockManager = source.getLevel().clockManager(); // Paper - per-world time
long repetitions = timeline.value().getPeriodCount(clockManager);
source.sendSuccess(() -> Component.translatable("commands.time.query.timeline.repetitions", timeline.getRegisteredName(), repetitions), false);
return wrapTime(repetitions);
}
}
+ // Paper start - time skip event
+ // Paper start - per-world time
private static int setTotalTicks(final CommandSourceStack source, final Holder<WorldClock> clock, final int totalTicks) {
ServerClockManager clockManager = source.getServer().clockManager();
- ServerClockManager clockManager = source.getServer().clockManager();
- clockManager.setTotalTicks(clock, totalTicks);
- source.sendSuccess(() -> Component.translatable("commands.time.set.absolute", clock.getRegisteredName(), totalTicks), true);
- return totalTicks;
+ ServerClockManager clockManager = source.getLevel().clockManager();
+ long currentTotalTicks = clockManager.getTotalTicks(clock);
+ org.bukkit.event.world.ClockTimeSkipEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callTimeSkipEvent(clock, totalTicks - currentTotalTicks);
+ org.bukkit.event.world.ClockTimeSkipEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.createTimeSkipEvent(source, totalTicks - currentTotalTicks);
+ final long newTotalTicks;
+ if (event.callEvent()) {
+ clockManager.setTotalTicks(clock, currentTotalTicks + event.getSkipAmount());
+ newTotalTicks = currentTotalTicks + event.getSkipAmount();
+ clockManager.setTotalTicks(clock, newTotalTicks);
+ } else {
+ newTotalTicks = totalTicks; // just pass it through
+ }
source.sendSuccess(() -> Component.translatable("commands.time.set.absolute", clock.getRegisteredName(), totalTicks), true);
return totalTicks;
+ source.sendSuccess(() -> Component.translatable("commands.time.set.absolute", clock.getRegisteredName(), newTotalTicks), true);
+ return wrapTime(newTotalTicks);
}
private static int addTime(final CommandSourceStack source, final Holder<WorldClock> clock, final int time) {
ServerClockManager clockManager = source.getServer().clockManager();
- ServerClockManager clockManager = source.getServer().clockManager();
- clockManager.addTicks(clock, time);
+ org.bukkit.event.world.ClockTimeSkipEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callTimeSkipEvent(clock, time);
+ ServerClockManager clockManager = source.getLevel().clockManager();
+ org.bukkit.event.world.ClockTimeSkipEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.createTimeSkipEvent(source, time);
+ if (event.callEvent()) {
+ clockManager.addTicks(clock, event.getSkipAmount());
+ }
long totalTicks = clockManager.getTotalTicks(clock);
source.sendSuccess(() -> Component.translatable("commands.time.set.absolute", clock.getRegisteredName(), totalTicks), true);
return wrapTime(totalTicks);
@@ -186,9 +_,16 @@
}
private static int setTimeToTimeMarker(final CommandSourceStack source, final Holder<WorldClock> clock, final ResourceKey<ClockTimeMarker> timeMarkerId) throws CommandSyntaxException {
ServerClockManager clockManager = source.getServer().clockManager();
- ServerClockManager clockManager = source.getServer().clockManager();
- if (!clockManager.moveToTimeMarker(clock, timeMarkerId)) {
+ // Paper start - per-world time
+ ServerClockManager clockManager = source.getLevel().clockManager();
+ java.util.OptionalLong targetTime = clockManager.getTotalTicksToTimeMarker(clock, timeMarkerId);
+ if (targetTime.isEmpty()) {
throw ERROR_NO_TIME_MARKER_FOUND.create(clock.getRegisteredName(), timeMarkerId);
} else {
+ final long currentTime = clockManager.getTotalTicks(clock);
+ final org.bukkit.event.world.ClockTimeSkipEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callTimeSkipEvent(clock, targetTime.getAsLong() - currentTime);
+ final org.bukkit.event.world.ClockTimeSkipEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.createTimeSkipEvent(source, targetTime.getAsLong() - currentTime);
+ if (event.callEvent()) {
+ clockManager.setTotalTicks(clock, currentTime + event.getSkipAmount());
+ }
+ // Paper end - time skip event
+ // Paper end - per-world time
source.sendSuccess(
() -> Component.translatable("commands.time.set.time_marker", clock.getRegisteredName(), timeMarkerId.identifier().toString()), true
);
@@ -214,7 +_,7 @@
private static Holder<WorldClock> getDefaultClock(final CommandSourceStack source) throws CommandSyntaxException {
Holder<DimensionType> dimensionType = source.getLevel().dimensionTypeRegistration();
- return dimensionType.value().defaultClock().orElseThrow(() -> ERROR_NO_DEFAULT_CLOCK.create(dimensionType.getRegisteredName()));
+ return source.getLevel().getClock().orElseThrow(() -> ERROR_NO_DEFAULT_CLOCK.create(dimensionType.getRegisteredName())); // Paper - clock override
@@ -197,13 +_,13 @@
}
private interface ClockGetter {
private static int setPaused(final CommandSourceStack source, final Holder<WorldClock> clock, final boolean paused) {
- source.getServer().clockManager().setPaused(clock, paused);
+ source.getLevel().clockManager().setPaused(clock, paused); // Paper - per-world time
source.sendSuccess(() -> Component.translatable(paused ? "commands.time.pause" : "commands.time.resume", clock.getRegisteredName()), true);
return 1;
}
private static int setRate(final CommandSourceStack source, final Holder<WorldClock> clock, final float rate) {
- source.getServer().clockManager().setRate(clock, rate);
+ source.getLevel().clockManager().setRate(clock, rate); // Paper - per-world time
source.sendSuccess(() -> Component.translatable("commands.time.rate", clock.getRegisteredName(), rate), true);
return 1;
}
@@ -189,6 +189,15 @@
this.ticketStorage.purgeStaleTickets(this.chunkMap);
}
@@ -344,7 +_,7 @@
if (!this.level.isDebug()) {
ProfilerFiller profiler = Profiler.get();
profiler.push("pollingChunks");
- if (this.level.tickRateManager().runsNormally()) {
+ if (this.level.tickRateManager().runsNormally() && this.level.paperConfig().unsupportedSettings.ticking.chunks) { // Paper - option to disable ticking
profiler.push("tickingChunks");
this.tickChunks(profiler, timeDiff);
profiler.pop();
@@ -376,12 +_,20 @@
chunkCount, this.level.getAllEntities(), this::getFullChunk, new LocalMobCapCalculator(this.chunkMap)
);
@@ -9,7 +9,7 @@
private final EntityTickList entityTickList = new EntityTickList();
private final ServerWaypointManager waypointManager;
private EnvironmentAttributeSystem environmentAttributes;
@@ -221,24 +_,192 @@
@@ -221,24 +_,199 @@
private final boolean tickTime;
private final LevelDebugSynchronizers debugSynchronizers = new LevelDebugSynchronizers(this);
@@ -22,6 +22,7 @@
+ private final WeatherData weatherData;
+ public final net.minecraft.world.level.timers.TimerQueue<net.minecraft.server.MinecraftServer> scheduledEvents;
+ public final WorldGenSettings worldGenSettings;
+ private @Nullable ServerClockManager clockManager;
+ public boolean hasPhysicsEvent = true; // Paper - BlockPhysicsEvent
+ public boolean hasEntityMoveEvent; // Paper - Add EntityMoveEvent
+
@@ -184,6 +185,12 @@
this.serverLevelData = levelData;
ChunkGenerator generator = levelStem.generator();
+ // CraftBukkit start
+ // Paper start - per-world time
+ if (!io.papermc.paper.configuration.GlobalConfiguration.get().time.affectsAllWorlds) {
+ this.clockManager = savedDataStorage.computeIfAbsent(net.minecraft.world.clock.ServerClockManager.TYPE); // Paper - per-world time
+ this.clockManager.init(server, this); // Paper - per-world time
+ }
+ // Paper end - per-world time
+ if (loadedWorldData.pdc() != null) {
+ this.getWorld().readBukkitValues(loadedWorldData.pdc().persistentData().toTagCompound());
+ }
@@ -262,48 +269,39 @@
@Deprecated
@VisibleForTesting
@@ -326,6 +_,13 @@
@@ -322,7 +_,7 @@
@Override
public ServerClockManager clockManager() {
- return this.server.clockManager();
+ return io.papermc.paper.configuration.GlobalConfiguration.get().time.affectsAllWorlds ? this.server.clockManager() : this.clockManager; // Paper - per-world time
}
@Override
+ public Optional<Holder<WorldClock>> getClock() {
+ return this.serverLevelData.clock()
+ .<Holder<WorldClock>>map(clock -> this.registryAccess().lookupOrThrow(Registries.WORLD_CLOCK).getOrThrow(clock))
+ .or(() -> this.dimensionType().defaultClock());
+ }
+
+ @Override
public EnvironmentAttributeSystem environmentAttributes() {
return this.environmentAttributes;
}
@@ -353,12 +_,26 @@
@@ -354,11 +_,24 @@
int percentage = this.getGameRules().get(GameRules.PLAYERS_SLEEPING_PERCENTAGE);
if (this.sleepStatus.areEnoughSleeping(percentage) && this.sleepStatus.areEnoughDeepSleeping(percentage, this.players)) {
- Optional<Holder<WorldClock>> defaultClock = this.dimensionType().defaultClock();
- if (this.getGameRules().get(GameRules.ADVANCE_TIME) && defaultClock.isPresent()) {
Optional<Holder<WorldClock>> defaultClock = this.dimensionType().defaultClock();
+ org.bukkit.event.world.ClockTimeSkipEvent event = null; // Paper - per-world time
if (this.getGameRules().get(GameRules.ADVANCE_TIME) && defaultClock.isPresent()) {
- this.server.clockManager().moveToTimeMarker(defaultClock.get(), ClockTimeMarkers.WAKE_UP_FROM_SLEEP);
+ Optional<Holder<WorldClock>> clock = this.getClock();
+ org.bukkit.event.world.TimeSkipEvent event = null; // Paper - time skip event
+ if (this.getGameRules().get(GameRules.ADVANCE_TIME) && clock.isPresent()) {
+ // Paper start - time skip event
+ long currentTime = this.server.clockManager().getTotalTicks(clock.get());
+ long delta = this.server.clockManager().getTotalTicksToTimeMarker(clock.get(), ClockTimeMarkers.WAKE_UP_FROM_SLEEP).orElse(0L) - currentTime;
+ // Paper start - per-world time
+ long currentTime = this.clockManager().getTotalTicks(defaultClock.get());
+ long delta = this.clockManager().getTotalTicksToTimeMarker(defaultClock.get(), ClockTimeMarkers.WAKE_UP_FROM_SLEEP).orElse(0L) - currentTime;
+ event = new org.bukkit.event.world.TimeSkipEvent(
+ this.getWorld(),
+ org.bukkit.craftbukkit.CraftWorldClock.minecraftHolderToBukkit(clock.get()),
+ org.bukkit.event.world.ClockTimeSkipEvent.SkipReason.NIGHT_SKIP,
+ delta
+ );
+
+ if (event.callEvent()) {
+ this.server.clockManager().setTotalTicks(clock.get(), currentTime + event.getSkipAmount());
+ this.clockManager().setTotalTicks(defaultClock.get(), currentTime + event.getSkipAmount());
+ }
+ // Paper end - time skip event
+ // Paper end - per-world time
}
- this.wakeUpAllPlayers();
+ if (event == null || !event.isCancelled()) this.wakeUpAllPlayers(); // Paper - time skip event - only wake up players if time skip event is not cancelled
+ if (event == null || !event.isCancelled()) this.wakeUpAllPlayers(); // Paper - per-world time - only wake up players if time skip event is not cancelled
if (this.getGameRules().get(GameRules.ADVANCE_WEATHER) && this.isRaining()) {
this.resetWeatherCycle();
}
@@ -328,6 +326,19 @@
if (isActive) {
this.resetEmptyTime();
}
@@ -437,9 +_,12 @@
}
}
);
+ if (this.paperConfig().unsupportedSettings.ticking.blockEntities) { // Paper - option to disable ticking
profiler.popPush("blockEntities");
this.tickBlockEntities();
profiler.pop();
+ } // Paper - option to disable ticking
+ this.spigotConfig.currentPrimedTnt = 0; // Spigot
}
profiler.push("entityManagement");
@@ -468,7 +_,7 @@
long time = this.levelData.getGameTime() + 1L;
this.serverLevelData.setGameTime(time);
@@ -1005,16 +1016,15 @@
this.getServer().getDataStorage().set(MapItemSavedData.type(id), data);
}
@@ -1492,7 +_,21 @@
@@ -1492,7 +_,20 @@
@Override
public void setRespawnData(final LevelData.RespawnData respawnData) {
- this.getServer().setRespawnData(respawnData);
+ // Paper start
+ final LevelData.RespawnData previousRespawnData = this.serverLevelData.getRespawnData();
+ this.serverLevelData.setSpawn(respawnData);
+ if (!previousRespawnData.positionEquals(respawnData)) {
+ if (!this.serverLevelData.getRespawnData().positionEquals(respawnData)) {
+ org.bukkit.Location previousLocation = this.getWorld().getSpawnLocation();
+ this.serverLevelData.setSpawn(respawnData);
+ this.server.getPlayerList().broadcastAll(new net.minecraft.network.protocol.game.ClientboundSetDefaultSpawnPositionPacket(respawnData), this.dimension());
+ this.server.updateEffectiveRespawnData();
+ new org.bukkit.event.world.SpawnChangeEvent(this.getWorld(), previousLocation).callEvent();
@@ -9,13 +9,12 @@
private final boolean transferred;
private long keepAliveTime;
private boolean keepAlivePending;
@@ -46,6 +_,17 @@
@@ -46,6 +_,16 @@
private boolean closed = false;
private int latency;
private volatile boolean suspendFlushingOnServerThread = false;
+ // CraftBukkit start
+ public final org.bukkit.craftbukkit.CraftServer cserver;
+ public boolean processedDisconnect;
+ // CraftBukkit end
+ public final java.util.Map<java.util.UUID, net.kyori.adventure.resource.ResourcePackCallback> packCallbacks = new java.util.concurrent.ConcurrentHashMap<>(); // Paper - adventure resource pack callbacks
+ private static final long KEEPALIVE_LIMIT = Long.getLong("paper.playerconnection.keepalive", 30) * 1000; // Paper - provide property to set keepalive limit
@@ -210,7 +209,7 @@
public void send(final Packet<?> packet, final @Nullable ChannelFutureListener listener) {
+ // CraftBukkit start
+ if (packet == null || this.processedDisconnect) { // Spigot
+ if (packet == null) {
+ return;
+ } else if (packet instanceof net.minecraft.network.protocol.game.ClientboundSetDefaultSpawnPositionPacket defaultSpawnPositionPacket && this instanceof ServerGamePacketListenerImpl serverGamePacketListener) {
+ serverGamePacketListener.player.compassTarget = org.bukkit.craftbukkit.util.CraftLocation.toBukkit(defaultSpawnPositionPacket.respawnData().pos(), serverGamePacketListener.getPlayer().level());
@@ -219,7 +218,7 @@
if (packet.isTerminal()) {
this.close();
}
@@ -174,15 +_,111 @@
@@ -174,15 +_,108 @@
}
}
@@ -237,13 +236,13 @@
public void disconnect(final DisconnectionDetails details) {
+ // CraftBukkit start - fire PlayerKickEvent
+ if (this.processedDisconnect) {
+ if (!this.connection.isConnected()) {
+ return;
+ }
+ if (!this.cserver.isPrimaryThread()) {
+ org.bukkit.craftbukkit.util.Waitable waitable = new org.bukkit.craftbukkit.util.Waitable() {
+ @Override
+ protected Object evaluate() {
+ protected @Nullable Object evaluate() {
+ ServerCommonPacketListenerImpl.this.disconnect(details);
+ return null;
+ }
@@ -285,7 +284,7 @@
+ }
+
+ reason = io.papermc.paper.adventure.PaperAdventure.asVanilla(event.reason());
+ leaveMessage = io.papermc.paper.adventure.PaperAdventure.asVanilla(event.leaveMessage());
+ leaveMessage = io.papermc.paper.adventure.PaperAdventure.asVanilla(event.leaveMessage());
+ serverGamePacketListener.player.quitReason = org.bukkit.event.player.PlayerQuitEvent.QuitReason.KICKED; // Paper - Add API for quit reason
+ // Log kick to console *after* event was processed.
+ switch (cause) {
@@ -303,14 +302,11 @@
+ }
+
+ private void disconnect0(final DisconnectionDetails details) {
+ // CraftBukkit end
this.connection.send(new ClientboundDisconnectPacket(details.reason()), PacketSendListener.thenRun(() -> this.connection.disconnect(details)));
+ this.onDisconnect(details); // CraftBukkit - fire quit instantly
this.connection.setReadOnly();
- this.server.executeBlocking(this.connection::handleDisconnection);
- }
+ // CraftBukkit - Don't wait
+ this.server.scheduleOnMain(this.connection::handleDisconnection); // Paper
+ this.connection.handleConnectionDisconnectOnNextTick = true; // Paper - Force kill connection ticking. Let this close the connection
+ }
+
+ // Paper start - add proper async disconnect
@@ -100,7 +100,7 @@
public SocketAddress startMemoryChannel() {
ChannelFuture newChannel;
synchronized (this.channels) {
@@ -151,6 +_,13 @@
@@ -151,12 +_,26 @@
public void tick() {
synchronized (this.connections) {
@@ -114,6 +114,19 @@
Iterator<Connection> iterator = this.connections.iterator();
while (iterator.hasNext()) {
Connection connection = iterator.next();
if (!connection.isConnecting()) {
if (connection.isConnected()) {
+ // Paper start - Force kill connection ticking
+ // also call this multiple times, doesnt matter
+ // See MC-307764
+ if (connection.handleConnectionDisconnectOnNextTick) {
+ continue;
+ }
+ // Paper end - Force kill connection ticking
try {
connection.tick();
} catch (Exception var7) {
@@ -170,6 +_,7 @@
connection.setReadOnly();
}
@@ -83,7 +83,7 @@
public ServerGamePacketListenerImpl(final MinecraftServer server, final Connection connection, final ServerPlayer player, final CommonListenerCookie cookie) {
super(server, connection, cookie);
@@ -284,11 +_,26 @@
@@ -284,8 +_,22 @@
player.connection = this;
player.getTextFilter().join();
this.signedMessageDecoder = SignedMessageChain.Decoder.unsigned(player.getUUID(), server::enforceSecureProfile);
@@ -108,10 +108,6 @@
@Override
public void tick() {
+ if (this.isDisconnected()) return; // Paper
if (this.ackBlockChangesUpTo > -1) {
this.send(new ClientboundBlockChangedAckPacket(this.ackBlockChangesUpTo));
this.ackBlockChangesUpTo = -1;
@@ -298,11 +_,13 @@
this.keepConnectionAlive();
this.chatSpamThrottler.tick();
@@ -1356,17 +1352,9 @@
return;
}
}
@@ -1453,24 +_,50 @@
@@ -1454,23 +_,42 @@
@Override
public void onDisconnect(final DisconnectionDetails details) {
+ // CraftBukkit start - Rarely it would send a disconnect line twice
+ if (this.processedDisconnect) {
+ return;
+ } else {
+ this.processedDisconnect = true;
+ }
+ // CraftBukkit end
LOGGER.info("{} lost connection: {}", this.player.getPlainTextName(), details.reason().getString());
- this.removePlayerFromWorld();
+ // Paper start - Fix kick event leave message not being sent
@@ -2510,7 +2498,7 @@
if (this.player.isModelPartShown(PlayerModelPart.HAT) != wasHatShown) {
this.server.getPlayerList().broadcastAll(new ClientboundPlayerInfoUpdatePacket(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_HAT, this.player));
}
@@ -2082,7 +_,7 @@
@@ -2082,21 +_,21 @@
packet.difficulty().getDisplayName()
);
} else {
@@ -2519,7 +2507,14 @@
}
}
@@ -2096,7 +_,7 @@
@Override
public void handleChangeGameMode(final ServerboundChangeGameModePacket packet) {
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level());
- if (!GameModeCommand.PERMISSION_CHECK.check(this.player.permissions())) {
+ if (!GameModeCommand.PERMISSION_CHECK.check(this.player.permissions()) && !this.player.getBukkitEntity().hasPermission("minecraft.command.gamemode")) { // Paper - add permission check
LOGGER.warn(
"Player {} tried to change game mode to {} without required permissions",
this.player.getGameProfile().name(),
packet.mode().getShortDisplayName().getString()
);
} else {
@@ -2664,7 +2659,7 @@
+ }
+
+ public final boolean isDisconnected() {
+ return (!this.player.joining && !this.connection.isConnected()) || this.processedDisconnect; // Paper - Fix duplication bugs
+ return (!this.player.joining && !this.connection.isConnected());
+ }
+
+ @Override
@@ -13,11 +13,11 @@
public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, TickablePacketListener {
private static final AtomicInteger UNIQUE_THREAD_ID = new AtomicInteger(0);
private static final Logger LOGGER = LogUtils.getLogger();
+ private static final java.util.concurrent.ExecutorService authenticatorPool = java.util.concurrent.Executors.newCachedThreadPool(new com.google.common.util.concurrent.ThreadFactoryBuilder().setNameFormat("User Authenticator #%d").setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(LOGGER)).build()); // Paper - Cache authenticator threads
+ private static final java.util.concurrent.ExecutorService authenticatorPool = java.util.concurrent.Executors.newThreadPerTaskExecutor(Thread.ofVirtual().name("User Authenticator #", 0).uncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(LOGGER)).factory()); // Paper - Virtual authenticator threads
private static final int MAX_TICKS_BEFORE_LOGIN = 600;
private final byte[] challenge;
private final MinecraftServer server;
@@ -56,10 +_,15 @@
@@ -56,10 +_,14 @@
private final ServerActivityMonitor serverActivityMonitor;
public volatile ServerLoginPacketListenerImpl.State state = ServerLoginPacketListenerImpl.State.HELLO;
private int tick;
@@ -31,11 +31,10 @@
+ private int velocityLoginMessageId = -1; // Paper - Add Velocity IP Forwarding Support
+ public java.util.@Nullable UUID requestedUuid; // Paper
+ private final io.papermc.paper.connection.PaperPlayerLoginConnection paperLoginConnection; // Paper - Config API
+ private volatile boolean disconnecting = false; // Paper - Fix disconnect still ticking login
public ServerLoginPacketListenerImpl(final MinecraftServer minecraftserver, final Connection connection, final boolean transferred) {
this.server = minecraftserver;
@@ -67,10 +_,20 @@
@@ -67,10 +_,19 @@
this.serverActivityMonitor = this.server.getServerActivityMonitor();
this.challenge = Ints.toByteArray(RandomSource.create().nextInt());
this.transferred = transferred;
@@ -44,7 +43,6 @@
@Override
public void tick() {
+ if (this.disconnecting) return; // Paper - Fix disconnect being called multiple times due to not awaiting for disconnect
+ // Paper start - login cookie API
+ // Don't block the connection
+ if (this.paperLoginConnection.isAwaitingCookies()) {
@@ -56,7 +54,7 @@
if (this.state == ServerLoginPacketListenerImpl.State.VERIFYING) {
this.verifyLoginAndFinishConnectionSetup(Objects.requireNonNull(this.authenticatedProfile));
}
@@ -80,11 +_,23 @@
@@ -80,16 +_,39 @@
this.finishLoginAndWaitForClient(this.authenticatedProfile);
}
@@ -66,28 +64,37 @@
+ public void tickTimeout() {
+ // Paper end - login cookie API
if (this.tick++ == 600) {
this.disconnect(Component.translatable("multiplayer.disconnect.slow_login"));
- this.disconnect(Component.translatable("multiplayer.disconnect.slow_login"));
+ this.disconnectAsync(Component.translatable("multiplayer.disconnect.slow_login")); // Paper
}
}
+
+ // CraftBukkit start
+ @Deprecated
+ public void disconnect(String reason) {
+ this.disconnect(io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(reason))); // Paper - Fix hex colors not working in some kick messages
+ }
+ // CraftBukkit end
+
@Override
public boolean isAcceptingMessages() {
return this.connection.isConnected();
@@ -95,6 +_,7 @@
}
+ // Paper start
+ public void disconnectAsync(Component reason) {
+ try {
+ LOGGER.info("Disconnecting {}: {}", this.getUserName(), reason.getString());
+ this.connection.send(new ClientboundLoginDisconnectPacket(reason), PacketSendListener.thenRun(() -> this.connection.disconnect(reason)));
+ this.connection.handleConnectionDisconnectOnNextTick = true;
+ } catch (Exception var3) {
+ LOGGER.error("Error whilst disconnecting player", (Throwable)var3);
+ }
+ }
+ // Paper end
public void disconnect(final Component component) {
try {
LOGGER.info("Disconnecting {}: {}", this.getUserName(), component.getString());
this.connection.send(new ClientboundLoginDisconnectPacket(component));
this.connection.disconnect(component);
+ this.disconnecting = true; // Paper - Fix disconnect still ticking login
} catch (Exception var3) {
LOGGER.error("Error whilst disconnecting player", (Throwable)var3);
}
@@ -117,7 +_,14 @@
@Override
public void handleHello(final ServerboundHelloPacket packet) {
@@ -104,7 +111,7 @@
this.requestedUsername = packet.name();
GameProfile singleplayerProfile = this.server.getSingleplayerProfile();
if (singleplayerProfile != null && this.requestedUsername.equalsIgnoreCase(singleplayerProfile.name())) {
@@ -127,7 +_,32 @@
@@ -127,7 +_,30 @@
this.state = ServerLoginPacketListenerImpl.State.KEY;
this.connection.send(new ClientboundHelloPacket("", this.server.getKeyPair().getPublic().getEncoded(), this.challenge, true));
} else {
@@ -119,8 +126,7 @@
+ return;
+ }
+ // Paper end - Add Velocity IP Forwarding Support
+ // CraftBukkit start
+ // Paper start - Cache authenticator threads
+ // Paper start - Virtual authenticator threads
+ authenticatorPool.execute(() -> {
+ try {
+ GameProfile gameprofile = ServerLoginPacketListenerImpl.this.createOfflineProfile(ServerLoginPacketListenerImpl.this.requestedUsername); // Spigot
@@ -133,20 +139,22 @@
+ ServerLoginPacketListenerImpl.this.server.server.getLogger().log(java.util.logging.Level.WARNING, "Exception verifying " + ServerLoginPacketListenerImpl.this.requestedUsername, ex);
+ }
+ });
+ // Paper end - Cache authenticator threads
+ // CraftBukkit end
+ // Paper end - Virtual authenticator threads
}
}
}
@@ -139,7 +_,7 @@
@@ -139,9 +_,9 @@
private void verifyLoginAndFinishConnectionSetup(final GameProfile profile) {
PlayerList playerList = this.server.getPlayerList();
- Component error = playerList.canPlayerLogin(this.connection.getRemoteAddress(), new NameAndId(profile));
+ Component error = org.bukkit.craftbukkit.event.CraftEventFactory.handleLoginResult(playerList.canPlayerLogin(this.connection.getRemoteAddress(), new NameAndId(profile)), this.paperLoginConnection, this.connection, profile, this.server, true); // Paper
if (error != null) {
this.disconnect(error);
- this.disconnect(error);
+ this.disconnectAsync(error); // Paper
} else {
if (this.server.getCompressionThreshold() >= 0 && !this.connection.isMemoryConnection()) {
this.connection
@@ -151,7 +_,7 @@
);
}
@@ -169,7 +177,7 @@
}
- Thread thread = new Thread("User Authenticator #" + UNIQUE_THREAD_ID.incrementAndGet()) {
+ // Paper start - Cache authenticator threads
+ // Paper start - Virtual authenticator threads
+ authenticatorPool.execute(new Runnable() {
{
Objects.requireNonNull(ServerLoginPacketListenerImpl.this);
@@ -222,7 +230,7 @@
- thread.start();
- }
+ });
+ // Paper end - Cache authenticator threads
+ // Paper end - Virtual authenticator threads
+ }
+
+ // CraftBukkit start
@@ -125,7 +125,7 @@
+ );
+ ev.callEvent();
+ spawnPosition = io.papermc.paper.util.MCUtil.toVec3(ev.getSpawnLocation());
+ this.spawnLevel = ((org.bukkit.craftbukkit.CraftWorld) ev.getSpawnLocation().getWorld()).getHandle();
+ if (ev.getSpawnLocation().getWorld() != null) this.spawnLevel = ((org.bukkit.craftbukkit.CraftWorld) ev.getSpawnLocation().getWorld()).getHandle();
+ this.spawnPosition = CompletableFuture.completedFuture(spawnPosition);
+ this.spawnAngle = new Vec2(ev.getSpawnLocation().getYaw(), ev.getSpawnLocation().getPitch());
+ }
@@ -272,7 +272,7 @@
}
@Override
@@ -296,64 +_,148 @@
@@ -296,64 +_,147 @@
}
protected void save(final ServerPlayer player) {
@@ -310,7 +310,6 @@
+ this.cserver.getPluginManager().callEvent(playerQuitEvent);
+ player.getBukkitEntity().disconnect();
+
+ if (this.server.isSameThread()) player.doTick(); // SPIGOT-924 // Paper - Improved watchdog support; don't tick during emergency shutdowns
+ // CraftBukkit end
+
+ // Paper start - Configurable player collision; Remove from collideRule team if needed
@@ -741,9 +740,12 @@
}
public UserWhiteList getWhiteList() {
@@ -654,10 +_,18 @@
@@ -652,12 +_,20 @@
public void sendLevelInfo(final ServerPlayer player, final ServerLevel level) {
WorldBorder worldBorder = level.getWorldBorder();
player.connection.send(new ClientboundInitializeBorderPacket(worldBorder));
player.connection.send(this.server.clockManager().createFullSyncPacket());
- player.connection.send(this.server.clockManager().createFullSyncPacket());
+ player.connection.send(level.clockManager().createFullSyncPacket(player)); // Paper - per-world time; per-player time
player.connection.send(new ClientboundSetDefaultSpawnPositionPacket(level.getRespawnData()));
+ // Paper start - view distances
+ player.connection.send(new ClientboundSetChunkCacheRadiusPacket(io.papermc.paper.FeatureHooks.getViewDistance(level)));
@@ -1,5 +1,35 @@
--- a/net/minecraft/world/clock/ServerClockManager.java
+++ b/net/minecraft/world/clock/ServerClockManager.java
@@ -30,6 +_,7 @@
);
private final PackedClockStates packedClockStates;
private MinecraftServer server;
+ private @org.jspecify.annotations.Nullable ServerLevel level; // Paper - per-world time
private final Map<Holder<WorldClock>, ServerClockManager.ClockInstance> clocks = new HashMap<>();
private ServerClockManager(final PackedClockStates packedClockStates) {
@@ -37,6 +_,12 @@
}
public void init(final MinecraftServer server) {
+ // Paper start - per-world time
+ this.init(server, null);
+ }
+ public void init(final MinecraftServer server, @org.jspecify.annotations.Nullable final ServerLevel level) {
+ this.level = level;
+ // Paper end - per-world time
this.server = server;
server.registryAccess()
.lookupOrThrow(Registries.WORLD_CLOCK)
@@ -61,7 +_,7 @@
}
public void tick() {
- boolean advanceTime = this.server.getGlobalGameRules().get(GameRules.ADVANCE_TIME);
+ boolean advanceTime = this.advanceTime(); // Paper - per-world time
if (advanceTime) {
this.clocks.values().forEach(ServerClockManager.ClockInstance::tick);
this.setDirty();
@@ -97,7 +_,16 @@
return set.booleanValue();
}
@@ -18,42 +48,86 @@
this.modifyClock(clock, instance -> instance.totalTicks = Math.max(instance.totalTicks + ticks, 0L));
}
@@ -113,7 +_,7 @@
@@ -112,13 +_,18 @@
private void modifyClock(final Holder<WorldClock> clock, final Consumer<? super ServerClockManager.ClockInstance> action) {
ServerClockManager.ClockInstance instance = this.getInstance(clock);
action.accept(instance);
Map<Holder<WorldClock>, ClockNetworkState> updates = Map.of(clock, instance.packNetworkState(this.server));
- Map<Holder<WorldClock>, ClockNetworkState> updates = Map.of(clock, instance.packNetworkState(this.server));
- this.server.getPlayerList().broadcastAll(new ClientboundSetTimePacket(this.getGameTime(), updates));
+ this.server.getPlayerList().broadcastAll(new ClientboundSetTimePacket(this.getGameTime(), updates)); // TODO 26.1 per-player time
+ // Paper start - per-world time
+ this.broadcastUpdates(clock, instance);
this.setDirty();
+ if (this.level != null) {
+ this.level.environmentAttributes().invalidateTickCache();
+ } else {
for (ServerLevel level : this.server.getAllLevels()) {
@@ -127,12 +_,29 @@
level.environmentAttributes().invalidateTickCache();
}
+ }
+ // Paper end - per-world time
}
@Override
@@ -127,12 +_,48 @@
}
public ClientboundSetTimePacket createFullSyncPacket() {
+ // TODO - snapshot: 26.1 per-player time
return new ClientboundSetTimePacket(this.getGameTime(), Util.mapValues(this.clocks, clock -> clock.packNetworkState(this.server)));
- return new ClientboundSetTimePacket(this.getGameTime(), Util.mapValues(this.clocks, clock -> clock.packNetworkState(this.server)));
+ // Paper start - per-player time
+ return this.createFullSyncPacket(null);
+ }
+
+ public ClientboundSetTimePacket createFullSyncPacket(final net.minecraft.server.level.ServerPlayer player) {
+ final Map<Holder<WorldClock>, ClockNetworkState> updates = new HashMap<>(this.clocks.size());
+ this.clocks.forEach((clock, instance) -> updates.put(clock, this.packNetworkState(clock, instance, player)));
+ return new ClientboundSetTimePacket(this.getGameTime(), updates);
+ // Paper end - per-player time
}
private long getGameTime() {
return this.server.overworld().getGameTime();
}
+
+ // Paper start
+ // TODO - snapshot: just make the inner class, fields and getter public
+ public boolean isPaused(final Holder<WorldClock> definition) {
+ return this.getInstance(definition).paused;
- return this.server.overworld().getGameTime();
- }
+ return this.level != null ? this.level.getGameTime() : this.server.overworld().getGameTime(); // Paper - per-world time
+ }
+
+ public float partialTick(final Holder<WorldClock> definition) {
+ return this.getInstance(definition).partialTick;
+ // Paper start - per-world time; per-player time
+ private void broadcastUpdates(final Holder<WorldClock> clock, final ServerClockManager.ClockInstance instance) {
+ for (final net.minecraft.server.level.ServerPlayer player : this.level != null ? this.level.players() : this.server.getPlayerList().getPlayers()) {
+ final var packet = new net.minecraft.network.protocol.game.ClientboundSetTimePacket(
+ this.getGameTime(),
+ java.util.Map.of(clock, this.packNetworkState(clock, instance, player))
+ );
+ player.connection.send(packet);
+ }
+ }
+
+ public float rate(final Holder<WorldClock> definition) {
+ return this.getInstance(definition).rate;
+ private ClockNetworkState packNetworkState(
+ final Holder<WorldClock> clock,
+ final ServerClockManager.ClockInstance instance,
+ final net.minecraft.server.level.ServerPlayer player
+ ) {
+ if (player != null && player.level().dimensionType().defaultClock().filter(clock::equals).isPresent()) {
+ final boolean paused = !player.relativeTime || instance.paused || !this.advanceTime();
+ return new ClockNetworkState(player.getPlayerTime(), instance.partialTick, paused ? 0.0F : instance.rate);
+ }
+ return instance.packNetworkState(this.advanceTime());
+ }
+
+ // Paper end
+ private boolean advanceTime() {
+ return this.level != null ? this.level.getGameRules().get(GameRules.ADVANCE_TIME) : this.server.getGlobalGameRules().get(GameRules.ADVANCE_TIME);
+ }
+ // Paper end - per-world time; per-player time
public boolean isAtTimeMarker(final Holder<WorldClock> clock, final ResourceKey<ClockTimeMarker> timeMarkerId) {
ServerClockManager.ClockInstance clockInstance = this.getInstance(clock);
@@ -171,8 +_,7 @@
return new ClockState(this.totalTicks, this.partialTick, this.rate, this.paused);
}
- public ClockNetworkState packNetworkState(final MinecraftServer server) {
- boolean advanceTime = server.getGlobalGameRules().get(GameRules.ADVANCE_TIME);
+ public ClockNetworkState packNetworkState(final boolean advanceTime) { // Paper - per-world time
boolean paused = this.paused || !advanceTime;
return new ClockNetworkState(this.totalTicks, this.partialTick, paused ? 0.0F : this.rate);
}
@@ -1,6 +1,14 @@
--- a/net/minecraft/world/damagesource/CombatTracker.java
+++ b/net/minecraft/world/damagesource/CombatTracker.java
@@ -29,15 +_,24 @@
@@ -22,22 +_,40 @@
private static final Style INTENTIONAL_GAME_DESIGN_STYLE = Style.EMPTY
.withClickEvent(new ClickEvent.OpenUrl(CommonLinks.INTENTIONAL_GAME_DESIGN_BUG))
.withHoverEvent(new HoverEvent.ShowText(Component.literal("MCPE-28723")));
- public final List<CombatEntry> entries = Lists.newArrayList();
+ public final List<CombatEntry> entries = new net.minecraft.util.ArrayListDeque<>(); // Paper - Fix mem leak (MC-301114); Use ArrayListDeque
public final LivingEntity mob;
private int lastDamageTime;
private int combatStartTime;
private int combatEndTime;
public boolean inCombat;
public boolean takingDamage;
@@ -22,6 +30,15 @@
+ public void recordDamageAndCheckCombatState(final CombatEntry entry) {
+ final DamageSource source = entry.source();
+ // Paper end - Combat tracker API
+ // Paper start - Fix mem leak (MC-301114)
+ final io.papermc.paper.configuration.type.number.IntOr.Disabled maxTrackingCombatEntries;
+ if (!this.entries.isEmpty() && (maxTrackingCombatEntries = io.papermc.paper.configuration.GlobalConfiguration.get().misc.maxTrackingCombatEntries).enabled()) {
+ final int maxEntries = maxTrackingCombatEntries.intValue();
+ while (this.entries.size() > maxEntries) {
+ this.entries.removeFirst();
+ }
+ }
+ // Paper end - Fix mem leak (MC-301114)
this.entries.add(entry);
this.lastDamageTime = this.mob.tickCount;
this.takingDamage = true;
@@ -1355,7 +1355,7 @@
this.setTicksFrozen(0);
}
@@ -2762,11 +_,43 @@
@@ -2762,11 +_,42 @@
public void thunderHit(final ServerLevel level, final LightningBolt lightningBolt) {
this.setRemainingFireTicks(this.remainingFireTicks + 1);
@@ -1383,11 +1383,10 @@
+ }
+
+ // CraftBukkit start
+ final DamageSource damageSource = this.damageSources().lightningBolt().eventEntityDamager(lightningBolt);
+ if (thisBukkitEntity instanceof org.bukkit.entity.Hanging) {
+ org.bukkit.event.hanging.HangingBreakByEntityEvent hangingEvent = new org.bukkit.event.hanging.HangingBreakByEntityEvent((org.bukkit.entity.Hanging) thisBukkitEntity, stormBukkitEntity);
+ pluginManager.callEvent(hangingEvent);
+
+ if (hangingEvent.isCancelled()) {
+ org.bukkit.event.hanging.HangingBreakByEntityEvent hangingEvent = new org.bukkit.event.hanging.HangingBreakByEntityEvent((org.bukkit.entity.Hanging) thisBukkitEntity, stormBukkitEntity, new org.bukkit.craftbukkit.damage.CraftDamageSource(damageSource));
+ if (!hangingEvent.callEvent()) {
+ return;
+ }
+ }
@@ -1396,7 +1395,7 @@
+ return;
+ }
+
+ if (!this.hurtServer(level, this.damageSources().lightningBolt().eventEntityDamager(lightningBolt), 5.0F)) { // Paper - fix DamageSource API
+ if (!this.hurtServer(level, damageSource, 5.0F)) { // Paper - fix DamageSource API
+ return;
+ }
+ // CraftBukkit end
@@ -40,7 +40,7 @@
+ // Paper start - Expand EntityUnleashEvent
+ final org.bukkit.event.entity.EntityUnleashEvent event = new org.bukkit.event.entity.EntityUnleashEvent(
+ entity.getBukkitEntity(),
+ !entity.isAlive() ? org.bukkit.event.entity.EntityUnleashEvent.UnleashReason.PLAYER_UNLEASH : org.bukkit.event.entity.EntityUnleashEvent.UnleashReason.HOLDER_GONE,
+ !entity.isAlive() ? org.bukkit.event.entity.EntityUnleashEvent.UnleashReason.LEASHED_GONE : org.bukkit.event.entity.EntityUnleashEvent.UnleashReason.HOLDER_GONE,
+ level.getGameRules().get(GameRules.ENTITY_DROPS) && !entity.pluginRemoved
+ );
+ event.callEvent();
@@ -1053,7 +1053,7 @@
int absorbValue = (this.getEffect(MobEffects.RESISTANCE).getAmplifier() + 1) * 5;
int absorb = 25 - absorbValue;
float v = damage * absorb;
@@ -1915,24 +_,194 @@
@@ -1915,24 +_,181 @@
}
}
@@ -1147,19 +1147,6 @@
+ return false;
+ }
+
+ if (source.getEntity() instanceof net.minecraft.world.entity.player.Player) {
+ // Paper start - PlayerAttackEntityCooldownResetEvent
+ //((net.minecraft.world.entity.player.Player) damagesource.getEntity()).resetOnlyAttackStrengthTicker(); // Moved from Player in order to make the cooldown reset get called after the damage event is fired
+ if (source.getEntity() instanceof ServerPlayer player) {
+ if (new com.destroystokyo.paper.event.player.PlayerAttackEntityCooldownResetEvent(player.getBukkitEntity(), this.getBukkitEntity(), player.getAttackStrengthScale(0F)).callEvent()) {
+ player.resetOnlyAttackStrengthTicker();
+ }
+ } else {
+ ((net.minecraft.world.entity.player.Player)source.getEntity()).resetOnlyAttackStrengthTicker();
+ }
+ // Paper end - PlayerAttackEntityCooldownResetEvent
+ }
+
+ // Resistance
+ if (event.getDamage(DamageModifier.RESISTANCE) < 0) {
+ float damageResisted = (float)-event.getDamage(DamageModifier.RESISTANCE);
@@ -1405,6 +1392,18 @@
profiler.pop();
if (this.isFallFlying()) {
@@ -2865,6 +_,11 @@
}
public void onAttack() {
+ // Paper start
+ onAttack(null);
+ }
+ public void onAttack(Entity entity) {
+ // Paper end
}
public void detectEquipmentUpdates() {
@@ -2879,16 +_,39 @@
private @Nullable Map<EquipmentSlot, ItemStack> collectEquipmentChanges() {
@@ -1,16 +1,5 @@
--- a/net/minecraft/world/entity/ai/village/VillageSiege.java
+++ b/net/minecraft/world/entity/ai/village/VillageSiege.java
@@ -34,8 +_,8 @@
@Override
public void tick(final ServerLevel level, final boolean spawnEnemies) {
if (!level.isBrightOutside() && spawnEnemies) {
- Optional<Holder<WorldClock>> defaultClock = level.dimensionType().defaultClock();
- if (defaultClock.isPresent() && level.clockManager().isAtTimeMarker(defaultClock.get(), ClockTimeMarkers.ROLL_VILLAGE_SIEGE)) {
+ Optional<Holder<WorldClock>> clock = level.getClock();
+ if (clock.isPresent() && level.clockManager().isAtTimeMarker(clock.get(), ClockTimeMarkers.ROLL_VILLAGE_SIEGE)) {
this.siegeState = level.getRandom().nextInt(10) == 0 ? VillageSiege.State.SIEGE_TONIGHT : VillageSiege.State.SIEGE_DONE;
}
@@ -102,11 +_,12 @@
zombie.finalizeSpawn(level, level.getCurrentDifficultyAt(zombie.blockPosition()), EntitySpawnReason.EVENT, null);
} catch (Exception var5) {
@@ -1,6 +1,6 @@
--- a/net/minecraft/world/entity/animal/goat/Goat.java
+++ b/net/minecraft/world/entity/animal/goat/Goat.java
@@ -213,13 +_,22 @@
@@ -213,8 +_,16 @@
public InteractionResult mobInteract(final Player player, final InteractionHand hand) {
ItemStack heldItem = player.getItemInHand(hand);
if (heldItem.is(Items.BUCKET) && !this.isBaby()) {
@@ -18,13 +18,6 @@
player.setItemInHand(hand, bucketOrMilkBucket);
return InteractionResult.SUCCESS;
} else {
+ boolean isFood = this.isFood(heldItem); // Paper - track before stack is possibly decreased to 0 (Fixes MC-244739)
InteractionResult interactionResult = super.mobInteract(player, hand);
- if (interactionResult.consumesAction() && this.isFood(heldItem)) {
+ if (interactionResult.consumesAction() && isFood) { // Paper
this.playEatingSound();
}
@@ -328,8 +_,7 @@
double deltaY = Mth.randomBetween(this.random, 0.3F, 0.7F);
double deltaZ = Mth.randomBetween(this.random, -0.2F, 0.2F);
@@ -1,6 +1,15 @@
--- a/net/minecraft/world/entity/animal/happyghast/HappyGhast.java
+++ b/net/minecraft/world/entity/animal/happyghast/HappyGhast.java
@@ -314,8 +_,12 @@
@@ -300,7 +_,7 @@
@Override
protected void addPassenger(final Entity passenger) {
if (!this.isVehicle()) {
- this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.HARNESS_GOGGLES_DOWN, this.getSoundSource(), 1.0F, 1.0F);
+ this.playSound(SoundEvents.HARNESS_GOGGLES_DOWN, 1.0F, 1.0F); // Paper - don't play sound if silenced (Fixes MC-297169)
}
super.addPassenger(passenger);
@@ -314,16 +_,21 @@
}
@Override
@@ -15,9 +24,11 @@
if (!this.level().isClientSide()) {
this.setServerStillTimeout(10);
}
@@ -324,6 +_,7 @@
if (!this.isVehicle()) {
this.clearHome();
this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.HARNESS_GOGGLES_UP, this.getSoundSource(), 1.0F, 1.0F);
- this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.HARNESS_GOGGLES_UP, this.getSoundSource(), 1.0F, 1.0F);
+ this.playSound(SoundEvents.HARNESS_GOGGLES_UP, 1.0F, 1.0F); // Paper - don't play sound if silenced (Fixes MC-297169)
}
+ return true; // Paper - cancellable passengers
}
@@ -43,15 +43,15 @@
} else {
if (!this.isRemoved()) {
+ // CraftBukkit start - fire break events
+ Entity damager = (!source.isDirect() && source.getEntity() != null) ? source.getEntity() : source.getDirectEntity(); // Paper - fix DamageSource API
+ org.bukkit.event.hanging.HangingBreakEvent event;
+ Entity damager = source.getDirectEntity();
+ final org.bukkit.event.hanging.HangingBreakEvent event;
+ if (damager != null) {
+ event = new org.bukkit.event.hanging.HangingBreakByEntityEvent((org.bukkit.entity.Hanging) this.getBukkitEntity(), damager.getBukkitEntity(), source.is(net.minecraft.tags.DamageTypeTags.IS_EXPLOSION) ? org.bukkit.event.hanging.HangingBreakEvent.RemoveCause.EXPLOSION : org.bukkit.event.hanging.HangingBreakEvent.RemoveCause.ENTITY);
+ event = new org.bukkit.event.hanging.HangingBreakByEntityEvent((org.bukkit.entity.Hanging) this.getBukkitEntity(), damager.getBukkitEntity(), new org.bukkit.craftbukkit.damage.CraftDamageSource(source), source.is(net.minecraft.tags.DamageTypeTags.IS_EXPLOSION) ? org.bukkit.event.hanging.HangingBreakEvent.RemoveCause.EXPLOSION : org.bukkit.event.hanging.HangingBreakEvent.RemoveCause.ENTITY);
+ } else {
+ event = new org.bukkit.event.hanging.HangingBreakEvent((org.bukkit.entity.Hanging) this.getBukkitEntity(), source.is(net.minecraft.tags.DamageTypeTags.IS_EXPLOSION) ? org.bukkit.event.hanging.HangingBreakEvent.RemoveCause.EXPLOSION : org.bukkit.event.hanging.HangingBreakEvent.RemoveCause.DEFAULT);
+ }
+
+ this.level().getCraftServer().getPluginManager().callEvent(event);
+ event.callEvent();
+
+ if (this.isRemoved() || event.isCancelled()) {
+ return true;
@@ -240,8 +240,9 @@
float attackStrengthScale = this.getAttackStrengthScale(0.5F);
float magicBoost = attackStrengthScale * (this.getEnchantedDamage(entity, baseDamage, damageSource) - baseDamage);
baseDamage *= this.baseDamageScaleFactor();
this.onAttack();
- this.onAttack();
- if (!this.deflectProjectile(entity)) {
+ this.onAttack(entity); // Paper
+ final float dmgFinal = magicBoost; // Paper - damage events
+ if (!this.deflectProjectile(entity, () -> !org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(entity, dmgSourceFinal, dmgFinal, false))) {
if (baseDamage > 0.0F || magicBoost > 0.0F) {
@@ -480,15 +481,27 @@
}
}
}
@@ -1808,10 +_,31 @@
@Override
public void onAttack() {
- this.resetOnlyAttackStrengthTicker();
+ // this.resetOnlyAttackStrengthTicker(); // CraftBukkit - Moved to EntityLiving to reset the cooldown after the damage is dealt
super.onAttack();
@@ -1807,10 +_,39 @@
}
@Override
- public void onAttack() {
- this.resetOnlyAttackStrengthTicker();
- super.onAttack();
- }
+ // Paper start
+ public void onAttack(@Nullable Entity entity) {
+ if (entity != null) {
+ if (new com.destroystokyo.paper.event.player.PlayerAttackEntityCooldownResetEvent((org.bukkit.entity.Player) this.getBukkitEntity(), entity.getBukkitEntity(), this.getAttackStrengthScale(0F)).callEvent()) {
+ this.resetOnlyAttackStrengthTicker();
+ }
+ } else {
+ this.resetOnlyAttackStrengthTicker();
+ }
+ super.onAttack(entity);
+ // Paper end
+ }
+
+ // Paper start - Force update attributes.
+ @Override
+ public void detectEquipmentUpdates() {
@@ -509,10 +522,9 @@
+ }
+ }
+ // Paper end - Force update attributes.
+
public void resetOnlyAttackStrengthTicker() {
this.attackStrengthTicker = 0;
}
@@ -1843,17 +_,32 @@
return ImmutableList.of(Pose.STANDING, Pose.CROUCHING, Pose.SWIMMING);
}
@@ -8,7 +8,7 @@
+ this.splash(hitResult);
+ }
+
+ public void splash(final HitResult hitResult) {
+ public void splash(final @org.jspecify.annotations.Nullable HitResult hitResult) { // Paper
+ // Paper end - More projectile API
if (this.level() instanceof ServerLevel level) {
ItemStack potionItemStack = this.getItem();
@@ -37,7 +37,7 @@
- private void onHitAsWater(final ServerLevel level) {
+ private static final Predicate<LivingEntity> APPLY_WATER_GET_ENTITIES_PREDICATE = AbstractThrownPotion.WATER_SENSITIVE_OR_ON_FIRE.or(Axolotl.class::isInstance); // Paper - Fix potions splash events
+
+ private boolean onHitAsWater(final ServerLevel level, final HitResult hitResult) { // Paper - Fix potions splash events
+ private boolean onHitAsWater(final ServerLevel level, final @org.jspecify.annotations.Nullable HitResult hitResult) { // Paper - Fix potions splash events
AABB aabb = this.getBoundingBox().inflate(4.0, 2.0, 4.0);
- for (LivingEntity entity : this.level().getEntitiesOfClass(LivingEntity.class, aabb, WATER_SENSITIVE_OR_ON_FIRE)) {
@@ -88,7 +88,7 @@
}
- protected abstract void onHitAsPotion(ServerLevel level, ItemStack potionItem, HitResult hitResult);
+ protected abstract boolean onHitAsPotion(ServerLevel level, ItemStack potionItem, HitResult hitResult); // Paper - Fix potions splash events & More Projectile API
+ protected abstract boolean onHitAsPotion(ServerLevel level, ItemStack potionItem, @org.jspecify.annotations.Nullable HitResult hitResult); // Paper - Fix potions splash events & More Projectile API
private void dowseFire(final BlockPos pos) {
BlockState blockState = this.level().getBlockState(pos);
@@ -5,7 +5,7 @@
@Override
- public void onHitAsPotion(final ServerLevel level, final ItemStack potionItem, final HitResult hitResult) {
+ public boolean onHitAsPotion(final ServerLevel level, final ItemStack potionItem, final HitResult hitResult) { // Paper - More projectile API
+ public boolean onHitAsPotion(final ServerLevel level, final ItemStack potionItem, final @org.jspecify.annotations.Nullable HitResult hitResult) { // Paper - More projectile API
AreaEffectCloud cloud = new AreaEffectCloud(this.level(), this.getX(), this.getY(), this.getZ());
if (this.getOwner() instanceof LivingEntity owner) {
cloud.setOwner(owner);
@@ -5,11 +5,12 @@
@Override
- public void onHitAsPotion(final ServerLevel level, final ItemStack potionItem, final HitResult hitResult) {
+ public boolean onHitAsPotion(final ServerLevel level, final ItemStack potionItem, final HitResult hitResult) { // Paper - More projectile API
+ public boolean onHitAsPotion(final ServerLevel level, final ItemStack potionItem, final @org.jspecify.annotations.Nullable HitResult hitResult) { // Paper - More projectile API
PotionContents contents = potionItem.getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY);
float durationScale = potionItem.getOrDefault(DataComponents.POTION_DURATION_SCALE, 1.0F);
Iterable<MobEffectInstance> mobEffects = contents.getAllEffects();
AABB potionAabb = this.getBoundingBox().move(hitResult.getLocation().subtract(this.position()));
- AABB potionAabb = this.getBoundingBox().move(hitResult.getLocation().subtract(this.position()));
+ AABB potionAabb = hitResult == null ? this.getBoundingBox() : this.getBoundingBox().move(hitResult.getLocation().subtract(this.position())); // Paper - More projectile API
AABB effectAabb = potionAabb.inflate(4.0, 2.0, 4.0);
List<LivingEntity> entities = this.level().getEntitiesOfClass(LivingEntity.class, effectAabb);
+ java.util.Map<org.bukkit.entity.LivingEntity, Double> affected = new java.util.HashMap<>(); // CraftBukkit
Loaded 100 of 222 files, more files were not shown because too many files have changed in this diff. Show more