Compare commits

...
5 Commits
Author SHA1 Message Date
Nassim Jahnke 806b0db07c Update release workflow
... but don't actually include the branch yet
2026-10-23 18:48:28 +02:00
Nassim Jahnke e13cf8870f Add new sound group method 2026-10-23 18:35:20 +02:00
Nassim Jahnke cb1e4cc603 Update mache 2026-10-23 18:23:59 +02:00
Nassim Jahnke 2fdccd0ddf Fix most compile errors 2026-10-20 20:51:52 +02:00
Nassim Jahnke 755ecce274 26.4 dev 2026-10-20 20:16:16 +02:00
322 changed files with 2458 additions and 2471 deletions

No files matched your search

@@ -1,4 +1,4 @@
name: Release 26.3
name: Release 26.4
on:
workflow_dispatch:
@@ -11,7 +11,7 @@ permissions:
concurrency:
cancel-in-progress: false
group: release-26.3
group: release-26.4
jobs:
release:
+3 -3
View File
@@ -1,4 +1,4 @@
Paper [![Version](https://img.shields.io/maven-metadata/v?metadataUrl=https%3A%2F%2Fartifactory.papermc.io%2Fartifactory%2Funiverse%2Fio%2Fpapermc%2Fpaper%2Fpaper-api%2Fmaven-metadata.xml&strategy=highestVersion&filter=26.3.*&label=version&color=%23344ceb
Paper [![Version](https://img.shields.io/maven-metadata/v?metadataUrl=https%3A%2F%2Fartifactory.papermc.io%2Fartifactory%2Funiverse%2Fio%2Fpapermc%2Fpaper%2Fpaper-api%2Fmaven-metadata.xml&strategy=highestVersion&filter=26.4.*&label=version&color=%23344ceb
)](https://papermc.io/downloads/paper)
[![Paper Build Status](https://img.shields.io/github/actions/workflow/status/PaperMC/Paper/release-26.3.yaml?branch=main)](https://github.com/PaperMC/Paper/actions)
[![Discord](https://img.shields.io/discord/289587909051416579.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/papermc)
@@ -39,7 +39,7 @@ repositories {
}
dependencies {
compileOnly("io.papermc.paper:paper-api:26.3.build.+")
compileOnly("io.papermc.paper:paper-api:26.4.build.+")
}
java {
@@ -59,7 +59,7 @@ java {
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>[26.3.build,)</version>
<version>[26.4.build,)</version>
<scope>provided</scope>
</dependency>
```
+2 -1
View File
@@ -630,14 +630,15 @@ public net.minecraft.world.level.dimension.end.EnderDragonFight spawnNewGateway(
public net.minecraft.world.level.entity.PersistentEntitySectionManager ensureChunkQueuedForLoad(J)V
public net.minecraft.world.level.entity.PersistentEntitySectionManager permanentStorage
public net.minecraft.world.level.gamerules.GameRules rules
public net.minecraft.world.level.levelgen.material.MaterialRuleContext context
public net.minecraft.world.level.levelgen.structure.StructurePiece SHAPE_CHECK_BLOCKS
public net.minecraft.world.level.levelgen.structure.placement.AbstractSpreadingStructurePlacement exclusionZone
public net.minecraft.world.level.levelgen.structure.placement.AbstractSpreadingStructurePlacement frequency
public net.minecraft.world.level.levelgen.structure.placement.AbstractSpreadingStructurePlacement frequencyReductionMethod
public net.minecraft.world.level.levelgen.structure.placement.AbstractSpreadingStructurePlacement locateOffset
public net.minecraft.world.level.levelgen.structure.placement.AbstractSpreadingStructurePlacement salt
public net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate <init>()V
public net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate entityInfoList
public net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate fillFromWorld(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/core/Vec3i;ZLjava/util/List;)V
public net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate palettes
public net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager resourceManagerSource
public net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager structureRepository
+4 -4
View File
@@ -1,12 +1,12 @@
group=io.papermc.paper
mcVersion=26.3
mcVersion=26.4-snapshot-5
# This is the current API version for use in (paper-)plugin.yml files
# During snapshot cycles this should be the anticipated version of the release target
apiVersion=26.3
channel=STABLE
apiVersion=26.4
channel=ALPHA
# Set to true while updating Minecraft version
updatingMinecraft=false
updatingMinecraft=true
org.gradle.configuration-cache=true
org.gradle.caching=true
@@ -1,64 +0,0 @@
package com.destroystokyo.paper.block;
import org.bukkit.Sound;
import org.bukkit.block.Block;
import org.jetbrains.annotations.NotNull;
/**
* Represents the sounds that a {@link Block} makes in certain situations
* <p>
* The sound group includes break, step, place, hit, and fall sounds.
* @deprecated use {@link org.bukkit.SoundGroup}
*/
@Deprecated(forRemoval = true, since = "1.18.2")
public interface BlockSoundGroup {
/**
* Gets the sound that plays when breaking this block
*
* @return The break sound
* @deprecated use {@link org.bukkit.SoundGroup#getBreakSound()}
*/
@NotNull
@Deprecated(forRemoval = true, since = "1.18.2")
Sound getBreakSound();
/**
* Gets the sound that plays when stepping on this block
*
* @return The step sound
* @deprecated use {@link org.bukkit.SoundGroup#getStepSound()}
*/
@NotNull
@Deprecated(forRemoval = true, since = "1.18.2")
Sound getStepSound();
/**
* Gets the sound that plays when placing this block
*
* @return The place sound
* @deprecated use {@link org.bukkit.SoundGroup#getPlaceSound()}
*/
@NotNull
@Deprecated(forRemoval = true, since = "1.18.2")
Sound getPlaceSound();
/**
* Gets the sound that plays when hitting this block
*
* @return The hit sound
* @deprecated use {@link org.bukkit.SoundGroup#getHitSound()}
*/
@NotNull
@Deprecated(forRemoval = true, since = "1.18.2")
Sound getHitSound();
/**
* Gets the sound that plays when this block falls
*
* @return The fall sound
* @deprecated use {@link org.bukkit.SoundGroup#getFallSound()}
*/
@NotNull
@Deprecated(forRemoval = true, since = "1.18.2")
Sound getFallSound();
}
@@ -10,6 +10,7 @@ import org.checkerframework.checker.index.qual.NonNegative;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Unmodifiable;
import org.jspecify.annotations.Nullable;
/**
* Holds the properties for this item for when it is consumed.
@@ -30,7 +31,7 @@ public interface Consumable extends BuildableDataComponent<Consumable, Consumabl
ItemUseAnimation animation();
@Contract(pure = true)
Key sound();
@Nullable Key sound();
@Contract(pure = true)
boolean hasConsumeParticles();
@@ -65,11 +66,11 @@ public interface Consumable extends BuildableDataComponent<Consumable, Consumabl
/**
* Sets the sound played when consuming the item.
*
* @param sound the {@link Key} representing the sound to be used
* @param sound the {@link Key} representing the sound to be used, or {@code null} for no sound
* @return the builder for chaining
*/
@Contract(value = "_ -> this", mutates = "this")
Builder sound(Key sound);
Builder sound(@Nullable Key sound);
/**
* Sets whether consuming the item results in particle effects.
@@ -39,10 +39,10 @@ public interface Equippable extends BuildableDataComponent<Equippable, Equippabl
/**
* Gets the equip sound key.
*
* @return the equip sound key
* @return the equip sound key, or {@code null} if none
*/
@Contract(pure = true)
Key equipSound();
@Nullable Key equipSound();
/**
* Gets the asset id if present.
@@ -112,10 +112,10 @@ public interface Equippable extends BuildableDataComponent<Equippable, Equippabl
/**
* Returns the sound that is played when shearing this equipment off an entity.
*
* @return shear sound
* @return shear sound, or {@code null} if none
*/
@Contract(pure = true)
Key shearSound();
@Nullable Key shearSound();
/**
* Builder for {@link Equippable}.
@@ -126,11 +126,11 @@ public interface Equippable extends BuildableDataComponent<Equippable, Equippabl
/**
* Sets the equip sound key for this item.
*
* @param sound the equip sound key
* @param sound the equip sound key, or {@code null} for no sound
* @return the builder for chaining
*/
@Contract(value = "_ -> this", mutates = "this")
Builder equipSound(Key sound);
Builder equipSound(@Nullable Key sound);
/**
* Sets the asset id for this item.
@@ -207,10 +207,10 @@ public interface Equippable extends BuildableDataComponent<Equippable, Equippabl
/**
* Sets the sound that is played when shearing this equipment off an entity.
*
* @param shearSound the shear sound key
* @param shearSound the shear sound key, or {@code null} for no sound
* @return the builder for chaining
*/
@Contract(value = "_ -> this", mutates = "this")
Builder shearSound(Key shearSound);
Builder shearSound(@Nullable Key shearSound);
}
}
@@ -182,16 +182,16 @@ public interface SulfurCubeArchetypeRegistryEntry {
// todo The sounds should take a RegistryHolder but need more thoughts on the create method
/**
* {@return the sound played once the sulfur cube is knocked}
* {@return the sound played once the sulfur cube is knocked, or {@code null} if none}
*/
@ApiStatus.Experimental
TypedKey<Sound> hitSound();
@Nullable TypedKey<Sound> hitSound();
/**
* {@return the sound played once the sulfur cube is pushed}
* {@return the sound played once the sulfur cube is pushed, or {@code null} if none}
*/
@ApiStatus.Experimental
TypedKey<Sound> pushSound();
@Nullable TypedKey<Sound> pushSound();
/**
* {@return the smallest impulse required to play the push sound}
@@ -214,8 +214,8 @@ public interface SulfurCubeArchetypeRegistryEntry {
*/
@Contract(value = "_, _, _, _ -> new", pure = true)
@ApiStatus.Experimental
static SoundSettings of(final TypedKey<Sound> hitSound, final TypedKey<Sound> pushSound, final float pushSoundImpulseThreshold, final float pushSoundCooldown) {
record Impl(TypedKey<Sound> hitSound, TypedKey<Sound> pushSound, float pushSoundImpulseThreshold, float pushSoundCooldown) implements SoundSettings {
static SoundSettings of(final @Nullable TypedKey<Sound> hitSound, final @Nullable TypedKey<Sound> pushSound, final float pushSoundImpulseThreshold, final float pushSoundCooldown) {
record Impl(@Nullable TypedKey<Sound> hitSound, @Nullable TypedKey<Sound> pushSound, float pushSoundImpulseThreshold, float pushSoundCooldown) implements SoundSettings {
}
return new Impl(hitSound, pushSound, pushSoundImpulseThreshold, pushSoundCooldown);
@@ -1,70 +1,112 @@
package org.bukkit;
import org.jetbrains.annotations.NotNull;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
/**
* Represents a group of sounds for blocks that are played when various actions
* happen (ie stepping, breaking, hitting, etc).
* happen (ie stepping, breaking, hitting, etc.).
*/
@NullMarked
public interface SoundGroup {
/**
* Get the volume these sounds are played at.
*
* <p>
* Note that this volume does not always represent the actual volume
* received by the client.
*
* @return volume
*/
public float getVolume();
float getVolume();
/**
* Gets the pitch these sounds are played at.
*
* <p>
* Note that this pitch does not always represent the actual pitch received
* by the client.
*
* @return pitch
*/
public float getPitch();
float getPitch();
/**
* Gets the corresponding breaking sound for this group.
*
* @return the break sound
* @return the break sound, or {@link Sound#BLOCK_STONE_BREAK} if this group has none
* @deprecated sounds may now be nullable, use {@link #breakSound()} instead.
*/
@NotNull
public Sound getBreakSound();
@Deprecated(since = "26.4")
Sound getBreakSound();
/**
* Gets the corresponding step sound for this group.
*
* @return the step sound
* @return the step sound, or {@link Sound#BLOCK_STONE_STEP} if this group has none
* @deprecated sounds may now be nullable, use {@link #stepSound()} instead.
*/
@NotNull
public Sound getStepSound();
@Deprecated(since = "26.4")
Sound getStepSound();
/**
* Gets the corresponding place sound for this group.
*
* @return the place sound
* @return the place sound, or {@link Sound#BLOCK_STONE_PLACE} if this group has none
* @deprecated sounds may now be nullable, use {@link #placeSound()} instead.
*/
@NotNull
public Sound getPlaceSound();
@Deprecated(since = "26.4")
Sound getPlaceSound();
/**
* Gets the corresponding hit sound for this group.
*
* @return the hit sound
* @return the hit sound, or {@link Sound#BLOCK_STONE_HIT} if this group has none
* @deprecated sounds may now be nullable, use {@link #hitSound()} instead.
*/
@NotNull
public Sound getHitSound();
@Deprecated(since = "26.4")
Sound getHitSound();
/**
* Gets the corresponding fall sound for this group.
*
* @return the fall sound
* @return the fall sound, or {@link Sound#BLOCK_STONE_FALL} if this group has none
* @deprecated sounds may now be nullable, use {@link #fallSound()} instead.
*/
@NotNull
public Sound getFallSound();
@Deprecated(since = "26.4")
Sound getFallSound();
/**
* Gets the corresponding breaking sound for this group.
*
* @return the break sound, or {@code null} if this group has none
*/
@Nullable Sound breakSound();
/**
* Gets the corresponding step sound for this group.
*
* @return the step sound, or {@code null} if this group has none
*/
@Nullable Sound stepSound();
/**
* Gets the corresponding place sound for this group.
*
* @return the place sound, or {@code null} if this group has none
*/
@Nullable Sound placeSound();
/**
* Gets the corresponding hit sound for this group.
*
* @return the hit sound, or {@code null} if this group has none
*/
@Nullable Sound hitSound();
/**
* Gets the corresponding fall sound for this group.
*
* @return the fall sound, or {@code null} if this group has none
*/
@Nullable Sound fallSound();
}
@@ -778,19 +778,6 @@ public interface Block extends Metadatable, Translatable, net.kyori.adventure.tr
*/
boolean canPlace(@NotNull BlockData data);
// Paper start
/**
* Gets the {@link com.destroystokyo.paper.block.BlockSoundGroup} for this block.
* <p>
* This object contains the block, step, place, hit, and fall sounds.
*
* @return the sound group for this block
* @deprecated use {@link #getBlockSoundGroup()}
*/
@NotNull
@Deprecated(forRemoval = true, since = "1.18.2")
com.destroystokyo.paper.block.BlockSoundGroup getSoundGroup();
/**
* Gets the {@link org.bukkit.SoundGroup} for this block.
*
@@ -35,9 +35,9 @@ public interface EquippableComponent extends ConfigurationSerializable {
/**
* Gets the sound to play when the item is equipped.
*
* @return the sound
* @return the sound, or null if none
*/
@NotNull // Paper
@Nullable
Sound getEquipSound();
/**
+2 -2
View File
@@ -14,7 +14,7 @@ plugins {
val paperMavenPublicUrl = "https://repo.papermc.io/repository/maven-public/"
dependencies {
mache("io.papermc:mache:26.3+build.1")
mache("io.papermc:mache:26.4-snapshot-5+build.2")
paperclip("io.papermc:paperclip:3.0.4")
}
@@ -23,7 +23,7 @@ paperweight {
gitFilePatches = false
updatingMinecraft {
//oldPaperCommit = "e5fe71723e2ffde7cc9fafc085ac3bb73e63175e"
oldPaperCommit = "8c0b8396d676f6abef3df8ab992ac726f6e01c9c"
}
}
@@ -1,25 +1,25 @@
--- a/com/mojang/authlib/services/MinecraftServicesDiscoveryService.java
+++ b/com/mojang/authlib/services/MinecraftServicesDiscoveryService.java
@@ -37,7 +_,7 @@
private final boolean servicesKeySetEnabled;
@@ -40,7 +_,7 @@
private final Supplier<DiscoveryResponse> discoverySupplier;
private final String clientVersion;
- private MinecraftServicesDiscoveryService(
+ protected MinecraftServicesDiscoveryService( // Paper - private->protected
final Proxy proxy,
final boolean servicesKeySetEnabled,
final Supplier<DiscoveryResponse> discoverySupplier
@@ -68,7 +_,7 @@
final Supplier<DiscoveryResponse> discoverySupplier,
@@ -75,7 +_,7 @@
);
}
- private static Supplier<DiscoveryResponse> createDiscoverySupplier(final Proxy proxy, final Environment environment) {
+ protected static Supplier<DiscoveryResponse> createDiscoverySupplier(final Proxy proxy, final Environment environment) { // Paper - private->protected
final MinecraftClient client = MinecraftClient.unauthenticated(proxy);
- private static Supplier<DiscoveryResponse> createDiscoverySupplier(final Proxy proxy, final Environment environment, final String clientVersion) {
+ protected static Supplier<DiscoveryResponse> createDiscoverySupplier(final Proxy proxy, final Environment environment, final String clientVersion) { // Paper - private->protected
final MinecraftClient client = MinecraftClient.unauthenticated(proxy, clientVersion);
return RetryableFetch.fetch(
() -> HttpDiscoveryService.constantURL(environment.discoveryUrl()),
@@ -84,7 +_,7 @@
return new MinecraftServicesDiscoveryService(proxy, false, DiscoveryResponse::offline);
@@ -95,7 +_,7 @@
return clientVersion;
}
- private static Environment determineEnvironment() {
@@ -1,6 +1,6 @@
--- a/com/mojang/authlib/services/MinecraftServicesProfileRepository.java
+++ b/com/mojang/authlib/services/MinecraftServicesProfileRepository.java
@@ -42,6 +_,8 @@
@@ -44,6 +_,8 @@
.filter(name -> !Strings.isNullOrEmpty(name))
.collect(Collectors.toSet());
@@ -9,7 +9,7 @@
for (final List<String> request : Iterables.partition(criteria, ENTRIES_PER_PAGE)) {
final List<String> normalizedRequest = request.stream().map(MinecraftServicesProfileRepository::normalizeName).toList();
@@ -73,6 +_,13 @@
@@ -75,6 +_,13 @@
callback.onProfileLookupFailed(name, new ProfileNotFoundException("Server did not find the requested profile"));
}
@@ -23,7 +23,7 @@
try {
Thread.sleep(DELAY_BETWEEN_PAGES);
} catch (final InterruptedException ignored) {
@@ -102,7 +_,15 @@
@@ -104,7 +_,15 @@
try {
return Optional.ofNullable(client.get(HttpDiscoveryService.constantURL(discoveryService.getUrl(Service.PROFILES, "getByName").replace("{name}", normalizeName(name))), NameAndId.class));
} catch (final MinecraftClientException e) {
@@ -9,7 +9,7 @@
this.nodes.remove(node.holder().id());
}
@@ -46,7 +_,7 @@
@@ -47,7 +_,7 @@
}
}
@@ -113,8 +113,8 @@
@Override
public Collection<String> getOnlinePlayerNames() {
- return Lists.newArrayList(this.server.getPlayerNames());
+ return this.entity instanceof ServerPlayer sourcePlayer && !sourcePlayer.getBukkitEntity().hasPermission("paper.bypass-visibility.tab-completion") ? this.getServer().getPlayerList().getPlayers().stream().filter(serverPlayer -> sourcePlayer.getBukkitEntity().canSee(serverPlayer.getBukkitEntity())).map(serverPlayer -> serverPlayer.getGameProfile().name()).toList() : Lists.newArrayList(this.server.getPlayerNames()); // Paper - Make CommandSourceStack respect hidden players
- return List.of(this.server.getPlayerNames());
+ return this.entity instanceof ServerPlayer sourcePlayer && !sourcePlayer.getBukkitEntity().hasPermission("paper.bypass-visibility.tab-completion") ? this.getServer().getPlayerList().getPlayers().stream().filter(serverPlayer -> sourcePlayer.getBukkitEntity().canSee(serverPlayer.getBukkitEntity())).map(serverPlayer -> serverPlayer.getGameProfile().name()).toList() : List.of(this.server.getPlayerNames()); // Paper - Make CommandSourceStack respect hidden players
}
@Override
@@ -1,6 +1,6 @@
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -159,6 +_,7 @@
@@ -160,6 +_,7 @@
import org.slf4j.Logger;
public class Commands {
@@ -8,7 +8,7 @@
public static final String COMMAND_PREFIX = "/";
private static final ThreadLocal<@Nullable ExecutionContext<CommandSourceStack>> CURRENT_EXECUTION_CONTEXT = new ThreadLocal<>();
private static final Logger LOGGER = LogUtils.getLogger();
@@ -183,6 +_,7 @@
@@ -184,6 +_,7 @@
@Override
public boolean isRestricted(final CommandNode<CommandSourceStack> node) {
@@ -16,7 +16,7 @@
Predicate<CommandSourceStack> requirement = node.getRequirement();
return !requirement.test(this.noPermissionSource);
}
@@ -190,6 +_,11 @@
@@ -191,6 +_,11 @@
private final CommandDispatcher<CommandSourceStack> dispatcher = new CommandDispatcher<>();
public Commands(final Commands.CommandSelection commandSelection, final CommandBuildContext context) {
@@ -28,7 +28,7 @@
AdvancementCommands.register(this.dispatcher);
AttributeCommand.register(this.dispatcher, context);
ExecuteCommand.register(this.dispatcher, context);
@@ -304,6 +_,42 @@
@@ -305,6 +_,42 @@
UnpublishCommand.register(this.dispatcher);
}
@@ -71,7 +71,7 @@
this.dispatcher.setConsumer(ExecutionCommandSource.resultConsumer());
}
@@ -323,6 +_,13 @@
@@ -324,6 +_,13 @@
}
public void performCommand(final ParseResults<CommandSourceStack> command, final String commandString) {
@@ -85,7 +85,7 @@
CommandSourceStack sender = command.getContext().getSource();
Profiler.get().push(() -> "/" + commandString);
ContextChain<CommandSourceStack> commandChain = finishParsing(command, commandString, sender);
@@ -336,10 +_,12 @@
@@ -337,10 +_,12 @@
)
);
}
@@ -101,7 +101,7 @@
StackTraceElement[] stackTrace = e.getStackTrace();
for (int i = 0; i < Math.min(stackTrace.length, 3); i++) {
@@ -370,12 +_,16 @@
@@ -371,12 +_,16 @@
return ContextChain.tryFlatten(command.getContext().build(commandString))
.orElseThrow(() -> CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand().createWithContext(command.getReader()));
} catch (CommandSyntaxException e) {
@@ -120,7 +120,7 @@
if (cursor > 10) {
context.append(CommonComponents.ELLIPSIS);
}
@@ -387,7 +_,17 @@
@@ -388,7 +_,17 @@
}
context.append(Component.translatable("command.context.here").withStyle(ChatFormatting.RED, ChatFormatting.ITALIC));
@@ -139,7 +139,7 @@
}
return null;
@@ -415,10 +_,65 @@
@@ -416,10 +_,65 @@
}
public void sendCommands(final ServerPlayer player) {
@@ -207,7 +207,7 @@
player.connection.send(new ClientboundCommandsPacket(root, COMMAND_NODE_INSPECTOR));
}
@@ -426,8 +_,49 @@
@@ -427,8 +_,49 @@
final CommandNode<S> source, final CommandNode<S> target, final S commandFilter, final Map<CommandNode<S>, CommandNode<S>> converted
) {
for (CommandNode<S> child : source.getChildren()) {
@@ -4,8 +4,8 @@
private final StringReader reader;
private final boolean forTesting;
private final boolean allowNbt;
- private final Map<Property<?>, Comparable<?>> properties = Maps.newHashMap();
+ private final Map<Property<?>, Comparable<?>> properties = Maps.newLinkedHashMap(); // CraftBukkit - stable
private final Map<String, String> vagueProperties = Maps.newHashMap();
- private final Map<Property<?>, Comparable<?>> properties = new HashMap<>();
+ private final Map<Property<?>, Comparable<?>> properties = new java.util.LinkedHashMap<>(); // CraftBukkit - stable
private final Map<String, String> vagueProperties = new HashMap<>();
private Identifier id = Identifier.withDefaultNamespace("");
private @Nullable StateDefinition<Block, BlockState> definition;
@@ -1,6 +1,6 @@
--- a/net/minecraft/core/BlockPos.java
+++ b/net/minecraft/core/BlockPos.java
@@ -141,67 +_,84 @@
@@ -139,67 +_,84 @@
@Override
public BlockPos above() {
@@ -98,7 +98,7 @@
}
@Override
@@ -667,9 +_,9 @@
@@ -698,9 +_,9 @@
}
public BlockPos.MutableBlockPos set(final int x, final int y, final int z) {
@@ -111,7 +111,7 @@
return this;
}
@@ -727,19 +_,19 @@
@@ -758,19 +_,19 @@
@Override
public BlockPos.MutableBlockPos setX(final int x) {
@@ -1,11 +0,0 @@
--- a/net/minecraft/core/Holder.java
+++ b/net/minecraft/core/Holder.java
@@ -252,7 +_,7 @@
}
void bindTags(final Collection<TagKey<T>> tags) {
- this.tags = Set.copyOf(tags);
+ this.tags = it.unimi.dsi.fastutil.objects.ReferenceSets.unmodifiable(new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(tags)); // Paper - use reference set because TagKey are interned
}
public void bindComponents(final DataComponentMap components) {
@@ -1,6 +1,6 @@
--- a/net/minecraft/core/MappedRegistry.java
+++ b/net/minecraft/core/MappedRegistry.java
@@ -33,17 +_,25 @@
@@ -34,17 +_,25 @@
public class MappedRegistry<T> implements WritableRegistry<T> {
private final ResourceKey<? extends Registry<T>> key;
private final ObjectList<Holder.Reference<T>> byId = new ObjectArrayList<>(256);
@@ -31,7 +31,7 @@
@Override
public Stream<HolderSet.Named<T>> listTags() {
@@ -117,6 +_,7 @@
@@ -118,6 +_,7 @@
this.toId.put(value, newId);
this.registrationInfos.put(key, registrationInfo);
this.registryLifecycle = this.registryLifecycle.add(registrationInfo.lifecycle());
@@ -39,7 +39,7 @@
return holder;
}
@@ -278,6 +_,7 @@
@@ -279,6 +_,7 @@
}
this.frozen = true;
@@ -47,7 +47,7 @@
this.byValue.forEach((value, holder) -> holder.bindValue((T)value));
List<Identifier> unboundEntries = this.byKey
.entrySet()
@@ -517,4 +_,13 @@
@@ -518,4 +_,13 @@
Stream<HolderSet.Named<T>> getTags();
}
@@ -1,6 +1,6 @@
--- a/net/minecraft/core/Vec3i.java
+++ b/net/minecraft/core/Vec3i.java
@@ -22,9 +_,9 @@
@@ -20,9 +_,9 @@
ByteBufCodecs.VAR_INT, Vec3i::getX, ByteBufCodecs.VAR_INT, Vec3i::getY, ByteBufCodecs.VAR_INT, Vec3i::getZ, Vec3i::new
);
public static final Vec3i ZERO = new Vec3i(0, 0, 0);
@@ -13,7 +13,7 @@
public static Codec<Vec3i> offsetCodec(final int maxOffsetPerAxis) {
return CODEC.validate(
@@ -41,12 +_,12 @@
@@ -39,12 +_,12 @@
}
@Override
@@ -28,7 +28,7 @@
return (this.getY() + this.getZ() * 31) * 31 + this.getX();
}
@@ -59,15 +_,15 @@
@@ -57,15 +_,15 @@
}
}
@@ -47,7 +47,7 @@
return this.z;
}
@@ -251,4 +_,11 @@
@@ -249,4 +_,11 @@
public String toShortString() {
return this.getX() + ", " + this.getY() + ", " + this.getZ();
}
@@ -236,8 +236,8 @@
hand,
itemInHand,
Blocks.POWDER_SNOW_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3),
- SoundEvents.BUCKET_EMPTY_POWDER_SNOW
+ SoundEvents.BUCKET_EMPTY_POWDER_SNOW, hitDirection // Paper - add hitDirection
- SoundEvents.BUCKET_EMPTY_POWDER_SNOW.value()
+ SoundEvents.BUCKET_EMPTY_POWDER_SNOW.value(), hitDirection // Paper - add hitDirection
);
}
@@ -1,6 +1,6 @@
--- a/net/minecraft/core/component/BlockTransformer.java
+++ b/net/minecraft/core/component/BlockTransformer.java
@@ -68,6 +_,14 @@
@@ -64,6 +_,14 @@
BlockState updatedShape = transformData.updateFromNeighbors ? Block.updateFromNeighbourShapes(newBlockState, level, pos) : newBlockState;
Player player = context.getPlayer();
ItemStack itemInHand = context.getItemInHand();
@@ -1,6 +1,6 @@
--- a/net/minecraft/core/dispenser/DispenseItemBehavior.java
+++ b/net/minecraft/core/dispenser/DispenseItemBehavior.java
@@ -80,12 +_,38 @@
@@ -83,13 +_,39 @@
Direction direction = source.state().getValue(DispenserBlock.FACING);
BlockPos pos = source.pos().relative(direction);
ServerLevel serverLevel = source.level();
@@ -36,17 +36,17 @@
);
ArmorStand armorStand = EntityTypes.ARMOR_STAND.spawn(serverLevel, postSpawnConfig, pos, EntitySpawnReason.DISPENSER, false, false);
if (armorStand != null) {
ArmorStandItem.playSound(serverLevel, armorStand);
- dispensed.shrink(1);
+ if (shrink) dispensed.shrink(1); // Paper
}
return dispensed;
@@ -103,8 +_,33 @@
@@ -107,9 +_,34 @@
.getEntitiesOfClass(AbstractChestedHorse.class, new AABB(pos), entity -> entity.isAlive() && !entity.hasChest())) {
if (abstractChestedHorse.isTamed()) {
SlotAccess slot = abstractChestedHorse.getSlot(AbstractHorse.CHEST_SLOT_OFFSET);
- if (slot != null && slot.set(dispensed)) {
- dispensed.shrink(1);
+ // CraftBukkit start
+ if (slot != null/* && slot.set(dispensed)*/) {
+ ItemStack singleCopy = dispensed.copyWithCount(1);
@@ -72,12 +72,14 @@
+ }
+ slot.set(org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()));
+ // CraftBukkit end
abstractChestedHorse.playChestEquipsSound();
- dispensed.shrink(1);
+
+ if (shrink) dispensed.shrink(1); // Paper - actually handle here
this.setSuccess(true);
return dispensed;
}
@@ -145,8 +_,45 @@
@@ -150,8 +_,45 @@
DispensibleContainerItem bucket = (DispensibleContainerItem)dispensed.getItem();
BlockPos target = source.pos().relative(source.state().getValue(DispenserBlock.FACING));
Level level = source.level();
@@ -88,7 +90,7 @@
+ /* Taken from SolidBucketItem#emptyContents */
+ boolean willEmptyContentsSolidBucketItem = bucket instanceof net.minecraft.world.item.SolidBucketItem && level.isInWorldBounds(target) && state.isAir();
+ /* Taken from BucketItem#emptyContents */
+ boolean willEmptyBucketItem = bucket instanceof final net.minecraft.world.item.BucketItem bucketItem && bucketItem.getContent() instanceof net.minecraft.world.level.material.FlowingFluid && (state.isAir() || state.canBeReplaced(bucketItem.getContent()) || (state.getBlock() instanceof net.minecraft.world.level.block.LiquidBlockContainer liquidBlockContainer && liquidBlockContainer.canPlaceLiquid(null, level, target, state, bucketItem.getContent())));
+ boolean willEmptyBucketItem = bucket instanceof final net.minecraft.world.item.BucketItem bucketItem && bucketItem.getContent() instanceof net.minecraft.world.level.material.FlowingFluid && (state.isAir() || state.canBeReplaced(bucketItem.getContent()) || (state.getBlock() instanceof net.minecraft.world.level.block.LiquidBlockContainer liquidBlockContainer && liquidBlockContainer.canPlaceLiquid(null, level, target, state, bucketItem.getContent().defaultFluidState())));
+ if (willEmptyContentsSolidBucketItem || willEmptyBucketItem) {
+ // Paper end - correctly check if the bucket place will succeed
+ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, source.pos());
@@ -124,7 +126,7 @@
return this.consumeWithRemainder(source, dispensed, new ItemStack(Items.BUCKET));
} else {
return this.defaultDispenseItemBehavior.dispense(source, dispensed);
@@ -170,13 +_,20 @@
@@ -175,7 +_,7 @@
BlockPos target = source.pos().relative(source.state().getValue(DispenserBlock.FACING));
BlockState blockState = level.getBlockState(target);
if (blockState.getBlock() instanceof BucketPickup bucket) {
@@ -133,7 +135,8 @@
if (pickup.isEmpty()) {
return super.execute(source, dispensed);
}
@@ -183,6 +_,13 @@
bucket.getPickupSound().ifPresent(soundEvent -> level.playSound(null, target, soundEvent, SoundSource.BLOCKS));
level.gameEvent(null, GameEvent.FLUID_PICKUP, target);
Item targetType = pickup.getItem();
+ // Paper start - Call BlockDispenseEvent
@@ -146,7 +149,7 @@
return this.consumeWithRemainder(source, dispensed, new ItemStack(targetType));
} else {
return super.execute(source, dispensed);
@@ -190,11 +_,54 @@
@@ -196,11 +_,54 @@
this.setSuccess(true);
Level level = source.level();
BlockPos target = source.pos().relative(source.state().getValue(DispenserBlock.FACING));
@@ -201,7 +204,7 @@
return dispensed;
}
@@ -209,15 +_,41 @@
@@ -215,15 +_,41 @@
}
BlockPos target = source.pos().relative(source.state().getValue(DispenserBlock.FACING));
@@ -250,7 +253,7 @@
this.setSuccess(true);
return dispensed;
}
@@ -230,6 +_,13 @@
@@ -236,6 +_,13 @@
Level level = source.level();
Direction direction = source.state().getValue(DispenserBlock.FACING);
BlockPos target = source.pos().relative(direction);
@@ -264,7 +267,7 @@
if (level.isEmptyBlock(target) && WitherSkullBlock.canSpawnMob(level, target, dispensed)) {
level.setBlockAndUpdate(
target, Blocks.WITHER_SKELETON_SKULL.defaultBlockState().setValue(SkullBlock.ROTATION, RotationSegment.convertToSegment(direction))
@@ -242,7 +_,7 @@
@@ -248,7 +_,7 @@
dispensed.shrink(1);
this.setSuccess(true);
} else {
@@ -273,7 +276,7 @@
}
return dispensed;
@@ -255,6 +_,13 @@
@@ -261,6 +_,13 @@
Level level = source.level();
BlockPos target = source.pos().relative(source.state().getValue(DispenserBlock.FACING));
CarvedPumpkinBlock pumpkinBlock = (CarvedPumpkinBlock)Blocks.CARVED_PUMPKIN;
@@ -287,7 +290,7 @@
if (level.isEmptyBlock(target) && pumpkinBlock.canSpawnGolem(level, target)) {
if (!level.isClientSide()) {
level.setBlockAndUpdate(target, pumpkinBlock.defaultBlockState());
@@ -264,7 +_,7 @@
@@ -270,7 +_,7 @@
dispensed.shrink(1);
this.setSuccess(true);
} else {
@@ -296,7 +299,7 @@
}
return dispensed;
@@ -287,6 +_,12 @@
@@ -296,6 +_,12 @@
ServerLevel level = source.level();
BlockPos target = source.pos().relative(source.state().getValue(DispenserBlock.FACING));
BlockState state = level.getBlockState(target);
@@ -306,10 +309,10 @@
+ return result;
+ }
+ // Paper end - Call BlockDispenseEvent
if (state.is(BlockTags.BEEHIVES, s -> s.hasProperty(BeehiveBlock.HONEY_LEVEL) && s.getBlock() instanceof BeehiveBlock)
&& state.getValue(BeehiveBlock.HONEY_LEVEL) >= 5) {
if (state.is(BlockTags.BEEHIVES, s -> s.getBlock() instanceof BeehiveBlock) && state.getValueOrElse(BeehiveBlock.HONEY_LEVEL, 0) >= 5) {
((BeehiveBlock)state.getBlock())
@@ -312,6 +_,13 @@
.releaseBeesAndResetHoneyLevel(level, state, target, null, BeehiveBlockEntity.BeeReleaseStatus.BEE_RELEASED);
@@ -320,6 +_,13 @@
this.setSuccess(true);
if (blockState.is(Blocks.RESPAWN_ANCHOR)) {
if (blockState.getValue(RespawnAnchorBlock.CHARGE) != 4) {
@@ -323,7 +326,7 @@
RespawnAnchorBlock.charge(null, level, pos, blockState);
dispensed.shrink(1);
} else {
@@ -336,6 +_,28 @@
@@ -344,6 +_,28 @@
return dispensed;
}
@@ -352,7 +355,7 @@
for (Armadillo armadillo : armadillos) {
if (armadillo.brushOffScute(null, dispensed)) {
dispensed.hurtAndBreak(16, level, null, item -> {});
@@ -355,6 +_,13 @@
@@ -363,6 +_,13 @@
BlockState blockState = level.getBlockState(pos);
Optional<BlockState> maybeWaxed = HoneycombItem.getWaxed(blockState);
if (maybeWaxed.isPresent()) {
@@ -366,7 +369,7 @@
level.setBlockAndUpdate(pos, maybeWaxed.get());
level.levelEvent(LevelEvent.PARTICLES_WAX_ON, pos, 0);
level.playSound(null, pos, SoundEvents.HONEYCOMB_WAX_ON, SoundSource.BLOCKS, 1.0F, 1.0F);
@@ -385,6 +_,12 @@
@@ -393,6 +_,12 @@
return this.defaultDispenseItemBehavior.dispense(source, dispensed);
}
@@ -1,7 +1,7 @@
--- a/net/minecraft/core/dispenser/FlintAndSteelDispenseItemBehavior.java
+++ b/net/minecraft/core/dispenser/FlintAndSteelDispenseItemBehavior.java
@@ -24,16 +_,27 @@
this.setSuccess(true);
@@ -24,8 +_,15 @@
ServerLevel level = source.level();
Direction facing = source.state().getValue(DispenserBlock.FACING);
BlockPos targetPos = source.pos().relative(facing);
+ // Paper start - Call BlockDispenseEvent
@@ -12,20 +12,34 @@
+ }
+ // Paper end - Call BlockDispenseEvent
BlockState target = level.getBlockState(targetPos);
if (!tryIgniteExplosiveEntities(level, targetPos)) {
if (BaseFireBlock.canBePlacedAt(level, targetPos, facing)) {
+ // CraftBukkit start - Ignition by dispensing flint and steel
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(level, targetPos, source.pos()).isCancelled()) {
level.setBlockAndUpdate(targetPos, BaseFireBlock.getState(level, targetPos));
level.gameEvent(null, GameEvent.BLOCK_PLACE, targetPos);
+ }
+ // CraftBukkit end
} else if (CampfireBlock.canLight(target) || CandleBlock.canLight(target) || CandleCakeBlock.canLight(target)) {
level.setBlockAndUpdate(targetPos, target.setValue(BlockStateProperties.LIT, true));
level.gameEvent(null, GameEvent.BLOCK_CHANGE, targetPos);
} else if (target.getBlock() instanceof TntBlock) {
- if (TntBlock.prime(level, targetPos, null, dispensed)) {
+ if (TntBlock.prime(level, targetPos, null, dispensed, () -> org.bukkit.craftbukkit.event.CraftEventFactory.callTNTPrimeEvent(level, targetPos, org.bukkit.event.block.TNTPrimeEvent.PrimeCause.DISPENSER, null, source.pos()))) { // CraftBukkit - TNTPrimeEvent
level.removeBlock(targetPos, false);
} else {
this.setSuccess(false);
- boolean ignited = tryIgnite(dispensed, level, targetPos, facing, target);
+ boolean ignited = tryIgnite(dispensed, level, targetPos, facing, target, source.pos()); // CraftBukkit
this.setSuccess(ignited);
if (this.isSuccess()) {
FlintAndSteelItem.playSound(level, targetPos);
@@ -36,19 +_,23 @@
}
private static boolean tryIgnite(
- final ItemStack dispensed, final ServerLevel level, final BlockPos targetPos, final Direction facing, final BlockState target
+ final ItemStack dispensed, final ServerLevel level, final BlockPos targetPos, final Direction facing, final BlockState target, final BlockPos sourcePos // CraftBukkit
) {
if (tryIgniteExplosiveEntities(level, targetPos)) {
return true;
} else if (BaseFireBlock.canBePlacedAt(level, targetPos, facing)) {
+ // CraftBukkit start - Ignition by dispensing flint and steel
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(level, targetPos, sourcePos).isCancelled()) {
level.setBlockAndUpdate(targetPos, BaseFireBlock.getState(level, targetPos));
level.gameEvent(null, GameEvent.BLOCK_PLACE, targetPos);
+ }
+ // CraftBukkit end
return true;
} else if (CampfireBlock.canLight(target) || CandleBlock.canLight(target) || CandleCakeBlock.canLight(target)) {
level.setBlockAndUpdate(targetPos, target.setValue(BlockStateProperties.LIT, true));
level.gameEvent(null, GameEvent.BLOCK_CHANGE, targetPos);
return true;
- } else if (target.getBlock() instanceof TntBlock && TntBlock.prime(level, targetPos, null, dispensed)) {
+ } else if (target.getBlock() instanceof TntBlock && TntBlock.prime(level, targetPos, null, dispensed, () -> org.bukkit.craftbukkit.event.CraftEventFactory.callTNTPrimeEvent(level, targetPos, org.bukkit.event.block.TNTPrimeEvent.PrimeCause.DISPENSER, null, sourcePos))) { // CraftBukkit - TNTPrimeEvent
level.removeBlock(targetPos, false);
return true;
} else {
@@ -1,65 +1,23 @@
--- a/net/minecraft/core/registries/BuiltInRegistries.java
+++ b/net/minecraft/core/registries/BuiltInRegistries.java
@@ -367,6 +_,11 @@
public static final Registry<ContextKeySet> CONTEXT_KEY_SET = registerSimple(Registries.CONTEXT_KEY_SET, LootContextParamSets::bootstrap);
public static final Registry<Consumer<GameTestHelper>> TEST_FUNCTION = registerSimple(Registries.TEST_FUNCTION, BuiltinTestFunctions::bootstrap);
public static final Registry<? extends Registry<?>> REGISTRY = WRITABLE_REGISTRY;
@@ -373,9 +_,19 @@
public static final Registry<ContextKeySet> CONTEXT_KEY_SET = BUILDER.registerSimple(Registries.CONTEXT_KEY_SET, LootContextParamSets::bootstrap);
public static final Registry<Consumer<GameTestHelper>> TEST_FUNCTION = BUILDER.registerSimple(Registries.TEST_FUNCTION, BuiltinTestFunctions::bootstrap);
public static final Registry<? extends Registry<?>> REGISTRY = BUILDER.build();
+ // Paper start - add built-in registry conversions
+ public static final io.papermc.paper.registry.data.util.Conversions STATIC_ACCESS_CONVERSIONS = new io.papermc.paper.registry.data.util.Conversions(
+ new net.minecraft.resources.RegistryOps.HolderLookupAdapter(net.minecraft.server.RegistryLayer.STATIC_ACCESS)
+ );
+ // Paper end - add built-in registry conversions
private static <T> Registry<T> registerSimple(final ResourceKey<? extends Registry<T>> name, final BuiltInRegistries.RegistryBootstrap<T> loader) {
return internalRegister(name, new MappedRegistry<>(name, Lifecycle.stable(), false), loader);
@@ -394,6 +_,7 @@
final ResourceKey<? extends Registry<T>> name, final R registry, final BuiltInRegistries.RegistryBootstrap<T> loader
) {
Bootstrap.checkBootstrapCalled(() -> "registry " + name.identifier());
+ io.papermc.paper.registry.PaperRegistryAccess.instance().registerRegistry(registry); // Paper - initialize API registry
Identifier key = name.identifier();
LOADERS.put(key, () -> loader.run(registry));
WRITABLE_REGISTRY.register((ResourceKey)name, registry, RegistrationInfo.BUILT_IN);
@@ -401,16 +_,34 @@
}
public static void bootStrap() {
- BUILDER.bootStrap();
+ // Paper start
+ bootStrap(() -> {});
+ }
+ public static void bootStrap(Runnable runnable) {
+ BUILDER.bootStrap(runnable);
+ // Paper end
+ REGISTRY.freeze(); // Paper - freeze main registry early
createContents();
+ runnable.run(); // Paper
freeze();
validate(REGISTRY);
}
private static void createContents() {
+ // Paper start - class-load org.bukkit.Registry
+ // we have to class-load Registry here to create all the CraftRegistry instances
+ // that will be created when Registry is class-loaded before RegistryAccess#getRegistry
+ // would try to create them in lockReferenceHolder
+ try {
+ Class.forName(org.bukkit.Registry.class.getName());
+ } catch (final ClassNotFoundException ex) {
+ throw new RuntimeException(ex);
+ }
+ // Paper end - class-load org.bukkit.Registry
LOADERS.forEach((key, value) -> {
if (value.get() == null) {
LOGGER.error("Unable to bootstrap registry '{}'", key);
}
+ io.papermc.paper.registry.PaperRegistryAccess.instance().lockReferenceHolders(ResourceKey.createRegistryKey(key)); // Paper - lock reference holder creation
});
}
@@ -419,6 +_,7 @@
for (Registry<?> registry : REGISTRY) {
bindBootstrappedTagsToEmpty(registry);
+ io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.runFreezeListeners(registry.key(), STATIC_ACCESS_CONVERSIONS); // Paper
registry.freeze();
}
}
public static <T> HolderGetter<T> acquireBootstrapRegistrationLookup(final Registry<T> registry) {
@@ -0,0 +1,53 @@
--- a/net/minecraft/core/registries/BuiltinRegistryBuilder.java
+++ b/net/minecraft/core/registries/BuiltinRegistryBuilder.java
@@ -53,6 +_,7 @@
final ResourceKey<? extends Registry<T>> name, final R registry, final BuiltinRegistryBuilder.RegistryBootstrap<T> loader
) {
Bootstrap.checkBootstrapCalled(() -> "registry " + name.identifier());
+ io.papermc.paper.registry.PaperRegistryAccess.instance().registerRegistry(registry); // Paper - initialize API registry
Identifier key = name.identifier();
this.loaders.put(key, () -> loader.run(registry));
this.registry.register((ResourceKey)name, registry, RegistrationInfo.BUILT_IN);
@@ -60,16 +_,34 @@
}
public void bootStrap() {
+ // Paper start
+ this.bootStrap(() -> {});
+ }
+ public void bootStrap(Runnable runnable) {
+ // Paper end
+ this.registry.freeze(); // Paper - freeze main registry early
this.createContents();
+ runnable.run(); // Paper
this.freeze();
validate(this.registry);
}
private void createContents() {
+ // Paper start - class-load org.bukkit.Registry
+ // we have to class-load Registry here to create all the CraftRegistry instances
+ // that will be created when Registry is class-loaded before RegistryAccess#getRegistry
+ // would try to create them in lockReferenceHolder
+ try {
+ Class.forName(org.bukkit.Registry.class.getName());
+ } catch (final ClassNotFoundException ex) {
+ throw new RuntimeException(ex);
+ }
+ // Paper end - class-load org.bukkit.Registry
this.loaders.forEach((key, value) -> {
if (value.get() == null) {
LOGGER.error("Unable to bootstrap registry '{}'", key);
}
+ io.papermc.paper.registry.PaperRegistryAccess.instance().lockReferenceHolders(ResourceKey.createRegistryKey(key)); // Paper - lock reference holder creation
});
}
@@ -78,6 +_,7 @@
for (Registry<?> registry : this.registry) {
bindBootstrappedTagsToEmpty(registry);
+ io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.runFreezeListeners(registry.key(), BuiltInRegistries.STATIC_ACCESS_CONVERSIONS); // Paper
registry.freeze();
}
}
@@ -1,6 +1,6 @@
--- a/net/minecraft/gametest/framework/GameTestInfo.java
+++ b/net/minecraft/gametest/framework/GameTestInfo.java
@@ -246,7 +_,7 @@
@@ -247,7 +_,7 @@
this.finish();
AABB bounds = this.getStructureBounds();
List<Entity> entities = this.getLevel().getEntitiesOfClass(Entity.class, bounds.inflate(1.0), mob -> !(mob instanceof Player));
@@ -1,15 +1,15 @@
--- a/net/minecraft/nbt/CompoundTag.java
+++ b/net/minecraft/nbt/CompoundTag.java
@@ -51,7 +_,7 @@
@@ -50,7 +_,7 @@
private static CompoundTag loadCompound(final DataInput input, final NbtAccounter accounter) throws IOException {
accounter.accountBytes(48L);
- Map<String, Tag> values = Maps.newHashMap();
- Map<String, Tag> values = new HashMap<>();
+ it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<String, Tag> values = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(8, 0.8f); // Paper - Reduce memory footprint of CompoundTag
byte tagType;
while ((tagType = input.readByte()) != 0) {
@@ -165,7 +_,7 @@
@@ -164,7 +_,7 @@
}
public CompoundTag() {
@@ -18,7 +18,7 @@
}
@Override
@@ -394,9 +_,16 @@
@@ -393,9 +_,16 @@
@Override
public CompoundTag copy() {
@@ -38,7 +38,7 @@
}
@Override
@@ -517,22 +_,47 @@
@@ -516,22 +_,47 @@
this.merge((CompoundTag)codec.encoder().encodeStart(ops, value).getOrThrow());
}
@@ -1,6 +1,6 @@
--- a/net/minecraft/nbt/SnbtGrammar.java
+++ b/net/minecraft/nbt/SnbtGrammar.java
@@ -619,7 +_,7 @@
@@ -607,7 +_,7 @@
mapEntries, Term.repeatedWithTrailingSeparator(mapEntryRule, mapEntries, StringReaderTerms.character(',')), scope -> scope.getOrThrow(mapEntries)
);
Atom<T> mapLiteral = Atom.of("map_literal");
@@ -9,7 +9,7 @@
List<Entry<String, T>> entries = scope.getOrThrow(mapEntries);
if (entries.isEmpty()) {
return emptyMapValue;
@@ -660,9 +_,11 @@
@@ -648,9 +_,11 @@
listLiteral,
Term.sequence(
StringReaderTerms.character('['),
@@ -1,7 +1,7 @@
--- a/net/minecraft/network/Connection.java
+++ b/net/minecraft/network/Connection.java
@@ -67,6 +_,11 @@
private final Queue<Consumer<Connection>> pendingActions = Queues.newConcurrentLinkedQueue();
private final Queue<Consumer<Connection>> pendingActions = new ConcurrentLinkedQueue<>();
public Channel channel;
public SocketAddress address;
+ // Spigot start
@@ -112,20 +112,15 @@
this.disconnect(Component.translatable("disconnect.timeout"));
} else {
Component reason = Component.translatable("disconnect.genericReason", "Internal Exception: " + cause);
@@ -120,9 +_,11 @@
@@ -120,6 +_,7 @@
details = new DisconnectionDetails(reason);
}
+ if (player != null) player.quitReason = org.bukkit.event.player.PlayerQuitEvent.QuitReason.ERRONEOUS_STATE; // Paper - Add API for quit reason
if (isFirstFault) {
LOGGER.debug("Failed to sent packet", cause);
- if (this.getSending() == PacketFlow.CLIENTBOUND) {
+ boolean doesDisconnectExist = this.packetListener.protocol() != ConnectionProtocol.STATUS && this.packetListener.protocol() != ConnectionProtocol.HANDSHAKING; // Paper
+ if (this.getSending() == PacketFlow.CLIENTBOUND && doesDisconnectExist) { // Paper
Packet<?> packet = this.sendLoginDisconnect
? new ClientboundLoginDisconnectPacket(reason)
: new ClientboundDisconnectPacket(reason);
@@ -139,6 +_,7 @@
Packet<?> disconnectPacket = null;
@@ -145,6 +_,7 @@
}
}
}
@@ -133,7 +128,7 @@
}
@Override
@@ -148,11 +_,60 @@
@@ -154,11 +_,60 @@
if (packetListener == null) {
throw new IllegalStateException("Received a packet before the packet listener was initialized");
}
@@ -195,7 +190,7 @@
} catch (RejectedExecutionException ignored) {
this.disconnect(Component.translatable("multiplayer.disconnect.server_shutdown"));
} catch (ClassCastException exception) {
@@ -295,6 +_,47 @@
@@ -298,6 +_,47 @@
}
}
@@ -243,7 +238,7 @@
private void sendPacket(final Packet<?> packet, final @Nullable ChannelFutureListener listener, final boolean flush) {
this.sentPackets++;
if (this.channel.eventLoop().inEventLoop()) {
@@ -342,10 +_,30 @@
@@ -345,10 +_,30 @@
}
}
@@ -274,7 +269,7 @@
}
if (!this.isConnected() && !this.disconnectionHandled) {
@@ -353,7 +_,7 @@
@@ -356,7 +_,7 @@
}
if (this.channel != null) {
@@ -283,7 +278,7 @@
}
if (this.tickCount++ % 20 == 0) {
@@ -389,6 +_,7 @@
@@ -392,6 +_,7 @@
}
public void disconnect(final DisconnectionDetails details) {
@@ -291,7 +286,7 @@
if (this.channel == null) {
this.delayedDisconnect = details;
}
@@ -541,6 +_,14 @@
@@ -552,10 +_,19 @@
}
}
@@ -305,16 +300,13 @@
+
public void setupCompression(final int threshold, final boolean validateDecompressed) {
if (threshold >= 0) {
if (this.channel.pipeline().get(HandlerNames.DECOMPRESS) instanceof CompressionDecoder compressionDecoder) {
@@ -554,6 +_,7 @@
} else {
this.channel.pipeline().addAfter(HandlerNames.PREPENDER, HandlerNames.COMPRESS, new CompressionEncoder(threshold));
}
this.setupInboundCompression(threshold, validateDecompressed);
this.setupOutboundCompression(threshold);
+ this.channel.pipeline().fireUserEventTriggered(io.papermc.paper.network.ConnectionEvent.COMPRESSION_THRESHOLD_SET); // Paper - Add Channel initialization listeners
} else {
if (this.channel.pipeline().get(HandlerNames.DECOMPRESS) instanceof CompressionDecoder) {
this.channel.pipeline().remove(HandlerNames.DECOMPRESS);
@@ -562,6 +_,7 @@
@@ -564,6 +_,7 @@
if (this.channel.pipeline().get(HandlerNames.COMPRESS) instanceof CompressionEncoder) {
this.channel.pipeline().remove(HandlerNames.COMPRESS);
}
@@ -322,7 +314,7 @@
}
}
@@ -579,6 +_,26 @@
@@ -597,6 +_,26 @@
);
disconnectListener.onDisconnect(details);
}
@@ -340,7 +332,7 @@
+ case WAITING_FOR_DUPE_DISCONNECT:
+ case PROTOCOL_SWITCHING:
+ case ACCEPTED:
+ final com.mojang.authlib.GameProfile profile = loginListener.authenticatedProfile; /* Should be non-null at this stage */
+ final com.mojang.authlib.GameProfile profile = loginListener.resolvedUserProfile; /* Should be non-null at this stage */
+ new com.destroystokyo.paper.event.player.PlayerConnectionCloseEvent(profile.id(), profile.name(),
+ this.address, false).callEvent();
+ }
@@ -24,7 +24,7 @@
return Thread.currentThread() == this.runningThread;
}
public <T extends PacketListener> void scheduleIfPossible(final T listener, final Packet<T> packet) {
public <T extends PacketListener & PacketProcessor.Handler> void scheduleIfPossible(final T listener, final Packet<? super T> packet) {
if (this.closed) {
- throw new RejectedExecutionException("Server already shutting down");
- }
@@ -47,8 +47,8 @@
}
public void processQueuedPackets() {
@@ -44,8 +_,28 @@
this.closed = true;
@@ -48,8 +_,28 @@
void onPacketError(Packet<?> packet, Exception cause) throws ReportedException;
}
+ // Paper start - detailed watchdog information
@@ -69,14 +69,14 @@
+ }
+ // Paper end - detailed watchdog information
+
private record ListenerAndPacket<T extends PacketListener>(T listener, Packet<T> packet) {
private record ListenerAndPacket<T extends PacketListener & PacketProcessor.Handler>(T listener, Packet<? super T> packet) {
public void handle() {
+ packetProcessing.push(this.listener); // Paper - detailed watchdog information
+ try { // Paper - detailed watchdog information
if (this.listener.shouldHandleMessage(this.packet)) {
try {
this.packet.handle(this.listener);
@@ -59,6 +_,12 @@
@@ -63,6 +_,12 @@
} else {
PacketProcessor.LOGGER.debug("Ignoring packet due to disconnection: {}", this.packet);
}
@@ -1,6 +1,6 @@
--- a/net/minecraft/network/chat/Component.java
+++ b/net/minecraft/network/chat/Component.java
@@ -27,7 +_,19 @@
@@ -26,7 +_,19 @@
import net.minecraft.world.level.ChunkPos;
import org.jspecify.annotations.Nullable;
@@ -1,6 +1,6 @@
--- a/net/minecraft/network/codec/ByteBufCodecs.java
+++ b/net/minecraft/network/codec/ByteBufCodecs.java
@@ -377,7 +_,13 @@
@@ -378,7 +_,13 @@
return new StreamCodec<ByteBuf, Tag>() {
@Override
public Tag decode(final ByteBuf input) {
@@ -15,7 +15,7 @@
if (result == null) {
throw new DecoderException("Expected non-null compound tag");
} else {
@@ -424,7 +_,7 @@
@@ -425,7 +_,7 @@
@Override
public void encode(final B output, final V value) {
@@ -24,7 +24,7 @@
original.encode(output, payload);
}
};
@@ -454,13 +_,55 @@
@@ -455,13 +_,55 @@
@Override
public void encode(final RegistryFriendlyByteBuf output, final T value) {
@@ -81,7 +81,7 @@
static <B extends ByteBuf, V> StreamCodec<B, Optional<V>> optional(final StreamCodec<? super B, V> original) {
return new StreamCodec<B, Optional<V>>() {
@Override
@@ -681,7 +_,7 @@
@@ -682,7 +_,7 @@
}
static <V> StreamCodec.CodecOperation<RegistryFriendlyByteBuf, V, V> registryFriendlyLengthPrefixed(final int maxSize) {
@@ -1,10 +1,14 @@
--- a/net/minecraft/network/protocol/common/ServerboundCustomPayloadPacket.java
+++ b/net/minecraft/network/protocol/common/ServerboundCustomPayloadPacket.java
@@ -14,7 +_,7 @@
@@ -14,10 +_,8 @@
public record ServerboundCustomPayloadPacket(CustomPacketPayload payload) implements Packet<ServerCommonPacketListener> {
private static final int MAX_PAYLOAD_SIZE = 32767;
public static final StreamCodec<FriendlyByteBuf, ServerboundCustomPayloadPacket> STREAM_CODEC = CustomPacketPayload.<FriendlyByteBuf>codec(
id -> DiscardedPayload.codec(id, 32767),
- Util.make(Lists.newArrayList(new CustomPacketPayload.TypeAndCodec<>(BrandPayload.TYPE, BrandPayload.STREAM_CODEC)), types -> {})
- id -> DiscardedPayload.codec(id, 32767), Util.make(new ArrayList<>(), types -> {
- types.add(new CustomPacketPayload.TypeAndCodec<>(BrandPayload.TYPE, BrandPayload.STREAM_CODEC));
- types.add(new CustomPacketPayload.TypeAndCodec<>(ModListPayload.TYPE, ModListPayload.STREAM_CODEC));
- })
+ id -> DiscardedPayload.codec(id, 32767),
+ java.util.Collections.emptyList() // CraftBukkit - treat all packets the same
)
.map(ServerboundCustomPayloadPacket::new, ServerboundCustomPayloadPacket::payload);
@@ -1,6 +1,6 @@
--- a/net/minecraft/network/protocol/game/ClientboundPlayerInfoUpdatePacket.java
+++ b/net/minecraft/network/protocol/game/ClientboundPlayerInfoUpdatePacket.java
@@ -42,6 +_,17 @@
@@ -40,6 +_,17 @@
this.actions = EnumSet.of(action);
this.entries = List.of(new ClientboundPlayerInfoUpdatePacket.Entry(player));
}
@@ -18,7 +18,7 @@
public static ClientboundPlayerInfoUpdatePacket createPlayerInitializing(final Collection<ServerPlayer> players) {
EnumSet<ClientboundPlayerInfoUpdatePacket.Action> actions = EnumSet.of(
@@ -56,6 +_,46 @@
@@ -54,6 +_,46 @@
);
return new ClientboundPlayerInfoUpdatePacket(actions, players);
}
@@ -65,7 +65,7 @@
private static StreamCodec<RegistryFriendlyByteBuf, ClientboundPlayerInfoUpdatePacket.Entry> entryCodec(
final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> actions
@@ -127,7 +_,15 @@
@@ -125,7 +_,15 @@
INITIALIZE_CHAT(
1,
(entry, input) -> entry.chatSession = input.readNullable(RemoteChatSession.Data.STREAM_CODEC),
@@ -82,7 +82,7 @@
),
UPDATE_GAME_MODE(
2, (entry, input) -> entry.gameMode = GameType.STREAM_CODEC.decode(input), (output, entry) -> GameType.STREAM_CODEC.encode(output, entry.gameMode)
@@ -177,10 +_,15 @@
@@ -174,10 +_,15 @@
RemoteChatSession.@Nullable Data chatSession
) {
private Entry(final ServerPlayer player) {
@@ -99,7 +99,7 @@
player.connection.latency(),
player.gameMode(),
player.getTabListDisplayName(),
@@ -189,6 +_,11 @@
@@ -186,6 +_,11 @@
Optionull.map(player.getChatSession(), RemoteChatSession::asData)
);
}
@@ -1,11 +1,11 @@
--- a/net/minecraft/network/protocol/handshake/ClientIntentionPacket.java
+++ b/net/minecraft/network/protocol/handshake/ClientIntentionPacket.java
@@ -16,7 +_,7 @@
}
private ClientIntentionPacket(final FriendlyByteBuf input) {
- this(input.readVarInt(), input.readUtf(255), input.readUnsignedShort(), ClientIntent.byId(input.readVarInt()));
+ this(input.readVarInt(), input.readUtf(Short.MAX_VALUE), input.readUnsignedShort(), ClientIntent.byId(input.readVarInt())); // Spigot - increase max hostName length
}
private void write(final FriendlyByteBuf output) {
@@ -11,7 +_,7 @@
public static final StreamCodec<FriendlyByteBuf, ClientIntentionPacket> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.VAR_INT,
ClientIntentionPacket::protocolVersion,
- ByteBufCodecs.stringUtf8(1024),
+ ByteBufCodecs.stringUtf8(Short.MAX_VALUE), // Spigot - increase max hostName length
ClientIntentionPacket::hostName,
ByteBufCodecs.UNSIGNED_SHORT,
ClientIntentionPacket::port,
@@ -1,6 +1,6 @@
--- a/net/minecraft/resources/RegistryDataLoader.java
+++ b/net/minecraft/resources/RegistryDataLoader.java
@@ -278,11 +_,21 @@
@@ -281,11 +_,21 @@
final Map<ResourceKey<? extends Registry<?>>, HolderGetter<?>> result = new HashMap<>();
contextRegistries.forEach(e -> result.put(e.key(), e));
newRegistriesAndLoaders.forEach(e -> result.put(e.registryKey(), e.concurrentRegistrationGetter));
@@ -1,14 +1,14 @@
--- a/net/minecraft/server/Bootstrap.java
+++ b/net/minecraft/server/Bootstrap.java
@@ -44,6 +_,7 @@
if (!isBootstrapped) {
@@ -47,6 +_,7 @@
isBootstrapped = true;
Instant start = Instant.now();
SystemReport.setVersionDetails(SharedConstants.getCurrentVersion().name(), SharedConstants.getCurrentVersion().id(), "MC_DEBUG_");
+ io.papermc.paper.plugin.entrypoint.LaunchEntryPointHandler.enterBootstrappers(); // Paper - Entrypoint for bootstrapping
if (BuiltInRegistries.REGISTRY.keySet().isEmpty()) {
throw new IllegalStateException("Unable to load registries");
}
@@ -56,11 +_,81 @@
@@ -59,11 +_,81 @@
EntitySelectorOptions.bootStrap();
DispenseItemBehavior.bootStrap();
CauldronInteractions.bootStrap();
@@ -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");
@@ -92,41 +_,97 @@
@@ -92,43 +_,99 @@
parser.printHelpOn(System.err);
return;
}
@@ -85,7 +85,6 @@
}
- File universePath = new File(options.valueOf(universe));
- Services services = Services.create(MinecraftServicesDiscoveryService.create(Proxy.NO_PROXY), universePath);
+ // Paper start - Detect headless JRE
+ String awtException = io.papermc.paper.util.ServerEnvironment.awtDependencyCheck();
+ if (awtException != null) {
@@ -110,7 +109,10 @@
+ universePath = new File(bukkitConfiguration.getString("settings.world-container", "."));
+ }
+ // Paper end - fix SPIGOT-5824
+ Services services = Services.create(com.destroystokyo.paper.profile.PaperServicesDiscoveryService.create(Proxy.NO_PROXY), universePath, userCacheFile, options); // Paper - pass OptionSet to load paper config files; override authentication service; fix world-container
Services services = Services.create(
- MinecraftServicesDiscoveryService.create(Proxy.NO_PROXY, SharedConstants.getCurrentVersion().id()), universePath
+ com.destroystokyo.paper.profile.PaperServicesDiscoveryService.create(Proxy.NO_PROXY, SharedConstants.getCurrentVersion().id()), universePath, userCacheFile, options // Paper - pass OptionSet to load paper config files; override authentication service; fix world-container
);
NotificationManager notificationManager = new NotificationManager();
ManagementServer jsonRpcServer = JsonRpc.create(settings, notificationManager);
- String levelName = Optional.ofNullable(options.valueOf(worldName)).orElse(settings.getProperties().levelName);
@@ -118,7 +120,7 @@
LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(universePath.toPath());
LevelStorageSource.LevelStorageAccess access = levelStorageSource.validateAndCreateAccess(levelName);
Dynamic<?> levelDataTag;
@@ -154,13 +_,20 @@
@@ -156,13 +_,20 @@
} else {
levelDataTag = null;
}
@@ -140,7 +142,7 @@
WorldStem worldStem;
try {
@@ -169,17 +_,31 @@
@@ -171,17 +_,31 @@
executor -> WorldLoader.load(
worldLoadConfig,
context -> {
@@ -174,7 +176,7 @@
}
},
WorldStem::new,
@@ -195,6 +_,7 @@
@@ -197,6 +_,7 @@
return;
}
@@ -182,7 +184,7 @@
RegistryAccess.Frozen registryHolder = worldStem.registries().compositeAccess();
WorldData data = worldStem.worldDataAndGenSettings().data();
boolean recreateRegionFilesValue = options.has(recreateRegionFiles);
@@ -203,9 +_,12 @@
@@ -205,9 +_,12 @@
}
access.saveDataTag(data);
@@ -195,7 +197,7 @@
thread,
access,
packRepository,
@@ -218,17 +_,35 @@
@@ -220,17 +_,35 @@
notificationManager
);
notificationManager.setServer(server);
@@ -233,7 +235,7 @@
Thread shutdownThread = new Thread("Server Shutdown Thread") {
@Override
public void run() {
@@ -237,12 +_,13 @@
@@ -239,12 +_,13 @@
};
shutdownThread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(LOGGER));
Runtime.getRuntime().addShutdownHook(shutdownThread);
@@ -248,7 +250,7 @@
final DedicatedServerSettings settings,
final WorldLoader.DataLoadContext context,
final Registry<LevelStem> datapackDimensions,
@@ -313,7 +_,11 @@
@@ -315,7 +_,11 @@
final RegistryAccess registryAccess,
final boolean recreateRegionFiles
) {
@@ -261,7 +263,7 @@
try (WorldUpgrader upgrader = new WorldUpgrader(storageSource, fixerUpper, registryAccess, eraseCache, recreateRegionFiles)) {
Component lastStatus = null;
@@ -341,5 +_,6 @@
@@ -343,5 +_,6 @@
}
}
}
@@ -1,6 +1,6 @@
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -188,11 +_,13 @@
@@ -189,11 +_,13 @@
import org.slf4j.Logger;
public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTask> implements CommandSource, ServerInfo, ChunkIOErrorReporter {
@@ -15,16 +15,16 @@
private static final int OVERLOADED_TICKS_THRESHOLD = 20;
private static final long OVERLOADED_WARNING_INTERVAL_NANOS = 10L * TimeUtil.NANOSECONDS_PER_SECOND;
private static final int OVERLOADED_TICKS_WARNING_INTERVAL = 100;
@@ -215,7 +_,7 @@
@@ -216,7 +_,7 @@
protected final PlayerDataStorage playerDataStorage;
private final SavedDataStorage savedDataStorage;
private final List<Runnable> tickables = Lists.newArrayList();
private final List<Runnable> tickables = new ArrayList<>();
- private final GameRules gameRules;
+ // Paper - per-level GameRules
private MetricsRecorder metricsRecorder = InactiveMetricsRecorder.INSTANCE;
private Consumer<ProfileResults> onMetricsRecordingStopped = results -> this.stopRecordingMetrics();
private Consumer<Path> onMetricsRecordingFinished = ignored -> {};
@@ -223,7 +_,7 @@
@@ -224,7 +_,7 @@
private MinecraftServer.@Nullable TimeProfiler debugCommandProfiler;
private boolean debugCommandProfilerDelayStart;
private ServerConnectionListener connection;
@@ -33,8 +33,8 @@
private @Nullable ServerStatus status;
private ServerStatus.@Nullable Favicon statusIcon;
private final RandomSource random = RandomSource.create();
@@ -234,13 +_,14 @@
private Map<ResourceKey<Level>, ServerLevel> levels = Maps.newLinkedHashMap();
@@ -235,13 +_,14 @@
private Map<ResourceKey<Level>, ServerLevel> levels = new LinkedHashMap<>();
private PlayerList playerList;
private volatile boolean running = true;
+ private volatile boolean isRestarting = false; // Paper - flag to signify we're attempting to restart
@@ -49,7 +49,7 @@
private int playerIdleTimeout;
private final long[] tickTimesNanos = new long[100];
private long aggregatedTickTimesNanos = 0L;
@@ -268,7 +_,7 @@
@@ -269,7 +_,7 @@
private @Nullable CommandStorage commandStorage;
private final CustomBossEvents customBossEvents;
private final RandomSequences randomSequences;
@@ -58,7 +58,7 @@
private final ServerFunctionManager functionManager;
private boolean enforceWhitelist;
private boolean usingWhitelist;
@@ -286,13 +_,14 @@
@@ -287,13 +_,14 @@
private final SuppressedExceptionCollector suppressedExceptions = new SuppressedExceptionCollector();
private final DiscontinuousFrame tickFrame;
private final PacketProcessor packetProcessor;
@@ -75,7 +75,7 @@
if (Runtime.getRuntime().availableProcessors() > 4) {
thread.setPriority(8);
}
@@ -303,7 +_,124 @@
@@ -304,7 +_,124 @@
return server;
}
@@ -200,7 +200,7 @@
final Thread serverThread,
final LevelStorageSource.LevelStorageAccess storageSource,
final PackRepository packRepository,
@@ -317,28 +_,28 @@
@@ -318,28 +_,28 @@
final NotificationManager notificationManager
) {
super("Server", propagatesCrashes);
@@ -237,7 +237,7 @@
this.fixerUpper = fixerUpper;
this.functionManager = new ServerFunctionManager(this, this.resources.managers.getFunctionLibrary());
HolderGetter<Block> blockLookup = this.registries.compositeAccess().lookupOrThrow(Registries.BLOCK).filterFeatures(this.worldData.enabledFeatures());
@@ -353,7 +_,14 @@
@@ -354,7 +_,14 @@
this.clockManager = this.getDataStorage().computeIfAbsent(ServerClockManager.TYPE);
this.clockManager.init(this);
this.customBossEvents = this.savedDataStorage.computeIfAbsent(CustomBossEvents.TYPE);
@@ -253,7 +253,7 @@
}
protected abstract boolean initServer() throws IOException;
@@ -386,15 +_,15 @@
@@ -387,15 +_,15 @@
};
}
@@ -274,7 +274,7 @@
if (profiledDuration != null) {
profiledDuration.finish(true);
}
@@ -408,33 +_,150 @@
@@ -409,31 +_,146 @@
}
}
@@ -323,14 +323,12 @@
+ final ResourceKey<Level> dimensionKey = loading.info().dimensionKey();
+ final SavedDataStorage savedDataStorage = new SavedDataStorage(this.storageSource.getDimensionPath(dimensionKey).resolve(LevelResource.DATA.id()), this.getFixerUpper(), this.registryAccess());
+ savedDataStorage.set(WorldGenSettings.TYPE, new WorldGenSettings(worldDataAndGenSettings.genSettings().options(), worldDataAndGenSettings.genSettings().dimensions()));
long seed = worldOptions.seed();
long biomeZoomSeed = BiomeManager.obfuscateSeed(seed);
List<CustomSpawner> overworldCustomSpawners = ImmutableList.of(
- new PhantomSpawner(), new PatrolSpawner(), new CatSpawner(), new VillageSiege(), new WanderingTraderSpawner(this.savedDataStorage)
- );
- LevelStem overworldData = dimensions.getValue(LevelStem.OVERWORLD);
- ServerLevel overworld = new ServerLevel(
- this, this.executor, this.storageSource, levelData, Level.OVERWORLD, overworldData, isDebug, biomeZoomSeed, overworldCustomSpawners, true
- this, this.executor, this.storageSource, levelData, Level.OVERWORLD, overworldData, isDebug, overworldCustomSpawners, true
- );
- this.levels.put(Level.OVERWORLD, overworld);
+ new PhantomSpawner(), new PatrolSpawner(), new CatSpawner(), new VillageSiege(), new WanderingTraderSpawner(savedDataStorage) // Paper - save to world data
@@ -361,7 +359,6 @@
+ dimensionKey,
+ levelStem,
+ worldDataAndGenSettings.data().isDebugWorld(),
+ biomeZoomSeed,
+ overworldCustomSpawners,
+ true,
+ loading.info().stemKey(),
@@ -392,7 +389,6 @@
+ dimensionKey,
+ levelStem,
+ this.worldData.isDebugWorld(),
+ biomeZoomSeed,
+ spawners,
+ true,
+ loading.info().stemKey(),
@@ -439,7 +435,7 @@
}
} catch (Throwable t) {
CrashReport report = CrashReport.forThrowable(t, "Exception initializing level");
@@ -451,25 +_,7 @@
@@ -450,25 +_,7 @@
}
GlobalPos focusPos = this.selectLevelLoadFocusPos();
@@ -452,7 +448,7 @@
- ResourceKey<Level> dimension = ResourceKey.create(Registries.DIMENSION, name.identifier());
- DerivedLevelData derivedLevelData = new DerivedLevelData(this.worldData, levelData);
- level = new ServerLevel(
- this, this.executor, this.storageSource, derivedLevelData, dimension, entry.getValue(), isDebug, biomeZoomSeed, ImmutableList.of(), false
- this, this.executor, this.storageSource, derivedLevelData, dimension, entry.getValue(), isDebug, ImmutableList.of(), false
- );
- this.levels.put(dimension, level);
- } else {
@@ -466,7 +462,7 @@
}
private static void setInitialSpawn(
@@ -485,6 +_,28 @@
@@ -484,6 +_,28 @@
levelData.setSpawn(LevelData.RespawnData.of(level.dimension(), BlockPos.ZERO.above(80), 0.0F, 0.0F));
} else {
ServerChunkCache chunkSource = level.getChunkSource();
@@ -495,7 +491,7 @@
ChunkPos spawnChunk = chunkSource.getGenerator().getOrigin(chunkSource.randomState());
levelLoadListener.start(LevelLoadListener.Stage.PREPARE_GLOBAL_SPAWN, 0);
levelLoadListener.updateFocus(level.dimension(), spawnChunk);
@@ -530,19 +_,22 @@
@@ -529,19 +_,22 @@
}
}
@@ -522,7 +518,7 @@
chunkLoadCounter.track(level, () -> {
TicketStorage savedTickets = level.getDataStorage().get(TicketStorage.TYPE);
if (savedTickets != null) {
@@ -551,17 +_,19 @@
@@ -550,17 +_,19 @@
});
}
@@ -548,7 +544,7 @@
}
protected GlobalPos selectLevelLoadFocusPos() {
@@ -623,19 +_,49 @@
@@ -622,19 +_,49 @@
this.stopServer();
}
@@ -599,7 +595,7 @@
}
LOGGER.info("Saving worlds");
@@ -678,6 +_,20 @@
@@ -677,6 +_,20 @@
} catch (IOException e) {
LOGGER.error("Failed to unlock level {}", this.storageSource.getLevelId(), e);
}
@@ -620,7 +616,7 @@
}
public String getLocalIp() {
@@ -693,6 +_,14 @@
@@ -692,6 +_,14 @@
}
public void halt(final boolean wait) {
@@ -635,7 +631,7 @@
this.running = false;
if (wait) {
try {
@@ -703,6 +_,122 @@
@@ -702,6 +_,122 @@
}
}
@@ -758,7 +754,7 @@
protected void runServer() {
try {
if (!this.initServer()) {
@@ -710,30 +_,73 @@
@@ -709,30 +_,73 @@
}
this.nextTickTimeNanos = Util.getNanos();
@@ -846,7 +842,7 @@
boolean sprinting = thisTickNanos == 0L;
if (this.debugCommandProfilerDelayStart) {
@@ -741,7 +_,7 @@
@@ -740,7 +_,7 @@
this.debugCommandProfiler = new MinecraftServer.TimeProfiler(Util.getNanos(), this.tickCount);
}
@@ -855,7 +851,7 @@
try (Profiler.Scope ignored = Profiler.use(this.createProfiler())) {
this.processPacketsAndTick(sprinting);
@@ -750,7 +_,7 @@
@@ -749,7 +_,7 @@
this.mayHaveDelayedTasks = true;
this.delayedTasksMaxNextTickTimeNanos = Math.max(Util.getNanos() + thisTickNanos, this.nextTickTimeNanos);
this.startMeasuringTaskExecutionTime();
@@ -864,7 +860,7 @@
this.finishMeasuringTaskExecutionTime();
if (sprinting) {
this.tickRateManager.endTickWork();
@@ -784,7 +_,7 @@
@@ -783,7 +_,7 @@
} catch (Throwable t) {
LOGGER.error("Exception stopping the server", t);
} finally {
@@ -873,7 +869,7 @@
}
}
}
@@ -836,7 +_,14 @@
@@ -835,7 +_,14 @@
}
private boolean haveTime() {
@@ -889,7 +885,7 @@
}
public NotificationManager notificationManager() {
@@ -844,11 +_,11 @@
@@ -843,11 +_,11 @@
}
protected void waitUntilNextTick() {
@@ -903,7 +899,7 @@
} finally {
this.waitingForNextTick = false;
}
@@ -866,18 +_,38 @@
@@ -865,18 +_,38 @@
}
@Override
@@ -946,7 +942,7 @@
this.mayHaveDelayedTasks = mayHaveMoreTasks;
return mayHaveMoreTasks;
}
@@ -887,15 +_,16 @@
@@ -886,15 +_,16 @@
return true;
}
@@ -965,7 +961,7 @@
}
@Override
@@ -947,26 +_,44 @@
@@ -946,26 +_,44 @@
}
protected void tickServer(final BooleanSupplier haveTime) {
@@ -1011,7 +1007,7 @@
this.tickCount++;
this.tickRateManager.tick();
this.tickChildren(haveTime);
@@ -976,11 +_,18 @@
@@ -975,11 +_,18 @@
}
this.ticksUntilAutosave--;
@@ -1031,7 +1027,7 @@
profiler.push("tallying");
long tickTime = Util.getNanos() - nano;
int tickIndex = this.tickCount % 100;
@@ -996,16 +_,16 @@
@@ -995,16 +_,16 @@
ProfilerFiller profiler = Profiler.get();
profiler.push("tick");
this.tickFrame.start();
@@ -1052,7 +1048,7 @@
LOGGER.debug("Autosave started");
ProfilerFiller profiler = Profiler.get();
profiler.push("save");
@@ -1047,7 +_,7 @@
@@ -1046,7 +_,7 @@
private ServerStatus buildServerStatus() {
ServerStatus.Players players = this.buildPlayerStatus();
return new ServerStatus(
@@ -1184,14 +1180,14 @@
+ // CraftBukkit start
+ public void addLevel(ServerLevel level) {
+ Map<ResourceKey<Level>, ServerLevel> oldLevels = this.levels;
+ Map<ResourceKey<Level>, ServerLevel> newLevels = Maps.newLinkedHashMap(oldLevels);
+ Map<ResourceKey<Level>, ServerLevel> newLevels = new LinkedHashMap<>(oldLevels);
+ newLevels.put(level.dimension(), level);
+ this.levels = Collections.unmodifiableMap(newLevels);
+ }
+
+ public void removeLevel(ServerLevel level) {
+ Map<ResourceKey<Level>, ServerLevel> oldLevels = this.levels;
+ Map<ResourceKey<Level>, ServerLevel> newLevels = Maps.newLinkedHashMap(oldLevels);
+ Map<ResourceKey<Level>, ServerLevel> newLevels = new LinkedHashMap<>(oldLevels);
+ newLevels.remove(level.dimension());
+ this.levels = Collections.unmodifiableMap(newLevels);
+ }
@@ -1209,7 +1205,7 @@
}
public ServerClockManager clockManager() {
@@ -1243,7 +_,12 @@
@@ -1251,7 +_,12 @@
@Override
public void sendSystemMessage(final Component message) {
@@ -1223,7 +1219,7 @@
}
public KeyPair getKeyPair() {
@@ -1280,11 +_,17 @@
@@ -1288,11 +_,17 @@
}
}
@@ -1246,7 +1242,7 @@
}
}
@@ -1300,6 +_,11 @@
@@ -1308,6 +_,11 @@
public void setDifficultyLocked(final boolean locked) {
this.worldData.setDifficultyLocked(locked);
@@ -1258,7 +1254,7 @@
this.getPlayerList().getPlayers().forEach(this::sendDifficultyUpdate);
}
@@ -1360,10 +_,20 @@
@@ -1368,10 +_,20 @@
@Override
public String getMotd() {
@@ -1280,7 +1276,7 @@
this.motd = motd;
}
@@ -1393,16 +_,20 @@
@@ -1401,16 +_,20 @@
int count = 0;
for (ServerPlayer player : this.getPlayerList().getPlayers()) {
@@ -1304,7 +1300,7 @@
}
public boolean isReady() {
@@ -1469,7 +_,7 @@
@@ -1481,7 +_,7 @@
@Override
public void executeIfPossible(final Runnable command) {
if (this.isStopped()) {
@@ -1313,7 +1309,7 @@
}
super.executeIfPossible(command);
@@ -1504,7 +_,16 @@
@@ -1520,7 +_,16 @@
return this.functionManager;
}
@@ -1330,7 +1326,7 @@
CompletableFuture<Void> result = CompletableFuture.<ImmutableList>supplyAsync(
() -> packsToEnable.stream()
.map(this.packRepository::getPack)
@@ -1516,7 +_,7 @@
@@ -1532,7 +_,7 @@
.thenCompose(
packsToLoad -> {
CloseableResourceManager resources = new MultiPackResourceManager(PackType.SERVER_DATA, packsToLoad);
@@ -1339,7 +1335,7 @@
return ReloadableServerResources.loadResources(
resources,
this.registries,
@@ -1536,17 +_,35 @@
@@ -1552,17 +_,35 @@
}
)
.thenAcceptAsync(newResources -> {
@@ -1377,7 +1373,7 @@
}, this);
if (this.isSameThread()) {
this.managedBlock(result::isDone);
@@ -1561,7 +_,7 @@
@@ -1577,7 +_,7 @@
DataPackConfig dataPackConfig = initialDataConfig.dataPacks();
FeatureFlagSet forcedFeatures = initMode ? FeatureFlagSet.of() : initialDataConfig.enabledFeatures();
FeatureFlagSet allowedFeatures = initMode ? FeatureFlags.REGISTRY.allFlags() : initialDataConfig.enabledFeatures();
@@ -1386,7 +1382,7 @@
if (safeMode) {
return configureRepositoryWithSelection(packRepository, List.of("vanilla"), forcedFeatures, false);
}
@@ -1616,7 +_,7 @@
@@ -1632,7 +_,7 @@
private static WorldDataConfiguration configureRepositoryWithSelection(
final PackRepository packRepository, final Collection<String> selected, final FeatureFlagSet forcedFeatures, final boolean disableInactive
) {
@@ -1395,7 +1391,7 @@
enableForcedFeaturePacks(packRepository, forcedFeatures);
DataPackConfig packConfig = getSelectedPacks(packRepository, disableInactive);
FeatureFlagSet packRequestedFeatures = packRepository.getRequestedFeatureFlags().join(forcedFeatures);
@@ -1648,7 +_,7 @@
@@ -1664,7 +_,7 @@
}
}
@@ -1404,10 +1400,10 @@
}
}
@@ -1665,8 +_,8 @@
@@ -1681,8 +_,8 @@
UserWhiteList whiteList = playerList.getWhiteList();
for (ServerPlayer player : Lists.newArrayList(playerList.getPlayers())) {
for (ServerPlayer player : List.copyOf(playerList.getPlayers())) {
- if (!whiteList.isWhiteListed(player.nameAndId())) {
- player.connection.disconnect(Component.translatable("multiplayer.disconnect.not_whitelisted"));
+ if (!whiteList.isWhiteListed(player.nameAndId()) && !this.getPlayerList().isOp(player.nameAndId())) { // Paper - Fix kicking ops when whitelist is reloaded (MC-171420)
@@ -1415,7 +1411,7 @@
}
}
}
@@ -1688,12 +_,12 @@
@@ -1704,12 +_,12 @@
}
public ServerLevel findRespawnDimension() {
@@ -1430,7 +1426,7 @@
public void setRespawnData(final LevelData.RespawnData respawnData) {
ServerLevelData levelData = this.worldData.overworldData();
LevelData.RespawnData oldRespawnData = levelData.getRespawnData();
@@ -1761,17 +_,20 @@
@@ -1777,17 +_,20 @@
return this.randomSequences;
}
@@ -1456,16 +1452,16 @@
}
public boolean isEnforceWhitelist() {
@@ -1854,7 +_,7 @@
@@ -1870,7 +_,7 @@
private void dumpGameRules(final Path path) throws IOException {
try (Writer output = Files.newBufferedWriter(path)) {
final List<String> entries = Lists.newArrayList();
final List<String> entries = new ArrayList<>();
- final GameRules gameRules = this.getGameRules();
+ final GameRules gameRules = this.overworld().getGameRules(); // Paper - per-level GameRules
gameRules.visitGameRuleTypes(new GameRuleTypeVisitor() {
@Override
public <T> void visit(final GameRule<T> gameRule) {
@@ -1911,6 +_,17 @@
@@ -1927,6 +_,17 @@
}
}
@@ -1483,7 +1479,7 @@
private ProfilerFiller createProfiler() {
if (this.willStartRecordingMetrics) {
this.metricsRecorder = ActiveMetricsRecorder.createStarted(
@@ -2038,16 +_,22 @@
@@ -2054,16 +_,22 @@
}
public void logChatMessage(final Component message, final ChatType.Bound chatType, final @Nullable String tag) {
@@ -1510,7 +1506,7 @@
}
public boolean logIPs() {
@@ -2058,8 +_,9 @@
@@ -2074,8 +_,9 @@
LOGGER.debug("Received custom click action {} with payload {}", id, payload.orElse(null));
}
@@ -1521,7 +1517,7 @@
}
public boolean setAutoSave(final boolean enable) {
@@ -2085,12 +_,14 @@
@@ -2101,12 +_,14 @@
return false;
}
@@ -1539,7 +1535,7 @@
player.connection.send(new ClientboundEntityEventPacket(player, event));
}
} else if (rule == GameRules.LIMITED_CRAFTING || rule == GameRules.IMMEDIATE_RESPAWN) {
@@ -2098,20 +_,21 @@
@@ -2114,20 +_,21 @@
? ClientboundGameEventPacket.LIMITED_CRAFTING
: ClientboundGameEventPacket.IMMEDIATE_RESPAWN;
ClientboundGameEventPacket packet = new ClientboundGameEventPacket(eventType, (Boolean)value ? 1.0F : 0.0F);
@@ -1566,7 +1562,7 @@
}
}
@@ -2124,12 +_,14 @@
@@ -2140,12 +_,14 @@
return this.savedDataStorage;
}
@@ -1583,7 +1579,7 @@
}
public boolean acceptsTransfers() {
@@ -2288,4 +_,53 @@
@@ -2309,4 +_,53 @@
};
}
}
@@ -1,6 +1,6 @@
--- a/net/minecraft/server/bossevents/CustomBossEvent.java
+++ b/net/minecraft/server/bossevents/CustomBossEvent.java
@@ -24,6 +_,16 @@
@@ -23,6 +_,16 @@
private int value;
private int max = 100;
private final Runnable dirtyCallback;
@@ -1,6 +1,6 @@
--- a/net/minecraft/server/commands/PlaceCommand.java
+++ b/net/minecraft/server/commands/PlaceCommand.java
@@ -313,6 +_,7 @@
@@ -312,6 +_,7 @@
throw ERROR_STRUCTURE_FAILED.create();
}
@@ -15,7 +15,7 @@
private static Collection<String> discoverNewPacks(final PackRepository packRepository, final WorldData worldData, final Collection<String> currentPacks) {
- packRepository.reload();
+ packRepository.reload(true); // Paper - will perform a full reload
Collection<String> selected = Lists.newArrayList(currentPacks);
Collection<String> selected = new ArrayList<>(currentPacks);
Collection<String> disabled = worldData.getDataConfiguration().dataPacks().getDisabled();
@@ -36,6 +_,16 @@
@@ -1,10 +1,10 @@
--- a/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/net/minecraft/server/dedicated/DedicatedServer.java
@@ -76,9 +_,9 @@
@@ -79,9 +_,9 @@
private static final Logger LOGGER = LogUtils.getLogger();
private static final int CONVERSION_RETRY_DELAY_MS = 5000;
private static final int CONVERSION_RETRIES = 2;
- private final List<ConsoleInput> consoleInput = Collections.synchronizedList(Lists.newArrayList());
- private final List<ConsoleInput> consoleInput = Collections.synchronizedList(new ArrayList<>());
+ private final java.util.Queue<ConsoleInput> serverCommandQueue = new java.util.concurrent.ConcurrentLinkedQueue<>(); // Paper - Perf: use a proper queue
private @Nullable QueryThreadGs4 queryThreadGs4;
- private final RconConsoleSource rconConsoleSource;
@@ -12,7 +12,7 @@
private @Nullable RconThread rconThread;
public DedicatedServerSettings settings;
private @Nullable MinecraftServerGui gui;
@@ -90,6 +_,7 @@
@@ -93,6 +_,7 @@
private final @Nullable ManagementServer jsonRpcServer;
public DedicatedServer(
@@ -20,7 +20,7 @@
final Thread serverThread,
final LevelStorageSource.LevelStorageAccess levelStorageSource,
final PackRepository packRepository,
@@ -102,6 +_,7 @@
@@ -105,6 +_,7 @@
final NotificationManager notificationManager
) {
super(
@@ -28,7 +28,7 @@
serverThread,
levelStorageSource,
packRepository,
@@ -115,7 +_,8 @@
@@ -118,7 +_,8 @@
notificationManager
);
this.settings = settings;
@@ -38,7 +38,7 @@
this.serverTextFilter = ServerTextFilter.createFromConfig(settings.getProperties());
this.jsonRpcServer = jsonRpcServer;
this.serverLinks = createServerLinks(settings);
@@ -167,6 +_,10 @@
@@ -170,6 +_,10 @@
Thread consoleThread = new Thread("Server console handler") {
@Override
public void run() {
@@ -49,7 +49,7 @@
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8));
String line;
@@ -177,16 +_,41 @@
@@ -180,16 +_,41 @@
} catch (IOException e) {
DedicatedServer.LOGGER.error("Exception handling console input", e);
}
@@ -92,7 +92,7 @@
LOGGER.info("Loading properties");
DedicatedServerProperties properties = this.settings.getProperties();
if (this.isSingleplayer()) {
@@ -197,8 +_,45 @@
@@ -200,8 +_,45 @@
this.setLocalIp(properties.serverIp);
}
@@ -139,7 +139,7 @@
InetAddress localAddress = null;
if (!this.getLocalIp().isEmpty()) {
localAddress = InetAddress.getByName(this.getLocalIp());
@@ -207,47 +_,73 @@
@@ -210,47 +_,73 @@
if (this.getPort() < 0) {
this.setPort(properties.serverPort);
}
@@ -221,7 +221,7 @@
}
if (properties.enableQuery) {
@@ -260,7 +_,7 @@
@@ -263,7 +_,7 @@
this.rconThread = RconThread.create(this);
}
@@ -230,7 +230,7 @@
Thread watchdog = new Thread(new ServerWatchdog(this), "Server Watchdog");
watchdog.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandlerWithName(LOGGER));
watchdog.setDaemon(true);
@@ -277,6 +_,12 @@
@@ -280,6 +_,12 @@
return true;
}
@@ -243,7 +243,7 @@
@Override
public boolean isEnforceWhitelist() {
return this.settings.getProperties().enforceWhitelist.get();
@@ -294,6 +_,7 @@
@@ -297,6 +_,7 @@
@Override
public void setUsingWhitelist(final boolean usingWhitelist) {
@@ -251,7 +251,7 @@
this.settings.update(p -> p.whiteList.update(this.registryAccess(), usingWhitelist));
}
@@ -345,7 +_,7 @@
@@ -348,7 +_,7 @@
@Override
protected void forceDifficulty() {
@@ -260,7 +260,7 @@
}
public int viewDistance() {
@@ -403,11 +_,11 @@
@@ -406,11 +_,11 @@
}
if (this.rconThread != null) {
@@ -274,7 +274,7 @@
}
if (this.jsonRpcServer != null) {
@@ -417,6 +_,9 @@
@@ -420,6 +_,9 @@
LOGGER.error("Interrupted while stopping the management server", e);
}
}
@@ -284,7 +284,7 @@
}
@Override
@@ -426,12 +_,20 @@
@@ -429,12 +_,20 @@
}
public void handleConsoleInput(final String msg, final CommandSourceStack source) {
@@ -308,7 +308,7 @@
this.getCommands().performPrefixedCommand(input.source, input.msg);
}
}
@@ -592,12 +_,15 @@
@@ -600,12 +_,15 @@
@Override
public String getMotd() {
@@ -326,7 +326,7 @@
}
@Override
@@ -623,7 +_,11 @@
@@ -631,7 +_,11 @@
@Override
public boolean enforceSecureProfile() {
DedicatedServerProperties properties = this.getProperties();
@@ -339,7 +339,7 @@
}
@Override
@@ -708,21 +_,60 @@
@@ -721,21 +_,60 @@
@Override
public String getPluginNames() {
@@ -405,7 +405,7 @@
}
@Override
@@ -852,4 +_,15 @@
@@ -871,4 +_,15 @@
public Map<String, String> getCodeOfConducts() {
return this.codeOfConductTexts;
}
@@ -1,28 +1,29 @@
--- a/net/minecraft/server/dedicated/DedicatedServerProperties.java
+++ b/net/minecraft/server/dedicated/DedicatedServerProperties.java
@@ -50,6 +_,7 @@
private static final Logger LOGGER = LogUtils.getLogger();
private static final Pattern SHA1 = Pattern.compile("^[a-fA-F0-9]{40}$");
private static final Splitter COMMA_SPLITTER = Splitter.on(',').trimResults();
private static final Splitter COMMA_SPLITTER_NO_EMPTY = COMMA_SPLITTER.omitEmptyStrings();
+ public final boolean debug = this.get("debug", false); // CraftBukkit
public static final String MANAGEMENT_SERVER_TLS_ENABLED_KEY = "management-server-tls-enabled";
public static final String MANAGEMENT_SERVER_TLS_KEYSTORE_KEY = "management-server-tls-keystore";
public static final String MANAGEMENT_SERVER_TLS_KEYSTORE_PASSWORD_KEY = "management-server-tls-keystore-password";
@@ -108,7 +_,7 @@
@@ -109,7 +_,7 @@
public final boolean broadcastRconToOps = this.get("broadcast-rcon-to-ops", true);
public final boolean broadcastConsoleToOps = this.get("broadcast-console-to-ops", true);
public final int maxWorldSize = this.get("max-world-size", v -> Mth.clamp(v, 1, 29999984), 29999984);
public final int maxWorldSize = this.get("max-world-size", v -> Math.clamp(v.intValue(), 1, 29999984), 29999984);
- public final boolean syncChunkWrites = this.get("sync-chunk-writes", true);
+ public final boolean syncChunkWrites = this.get("sync-chunk-writes", true) && Boolean.getBoolean("Paper.enable-sync-chunk-writes"); // Paper - Hide sync chunk writes behind flag
public final String regionFileComression = this.get("region-file-compression", "deflate");
public final boolean enableJmxMonitoring = this.get("enable-jmx-monitoring", false);
public final Settings<DedicatedServerProperties>.MutableValue<Boolean> enableStatus = this.getMutable("enable-status", true);
@@ -125,13 +_,16 @@
@@ -127,14 +_,17 @@
public final Settings<DedicatedServerProperties>.MutableValue<Boolean> whiteList = this.getMutable("white-list", true);
public final boolean enforceSecureProfile = this.get("enforce-secure-profile", true);
public final boolean logIPs = this.get("log-ips", true);
- public final Settings<DedicatedServerProperties>.MutableValue<Integer> pauseWhenEmptySeconds = this.getMutable("pause-when-empty-seconds", 60);
+ public final Settings<DedicatedServerProperties>.MutableValue<Integer> pauseWhenEmptySeconds = this.getMutable("pause-when-empty-seconds", -1); // Paper - disable tick sleeping by default
public final String allowedConnectionIds = this.get("allowed-connection-ids", "");
private final DedicatedServerProperties.WorldDimensionData worldDimensionData;
public final WorldOptions worldOptions;
public final Settings<DedicatedServerProperties>.MutableValue<Boolean> acceptsTransfers = this.getMutable("accepts-transfers", false);
@@ -37,7 +38,7 @@
String levelSeed = this.get("level-seed", "");
boolean generateStructures = this.get("generate-structures", true);
long seed = WorldOptions.parseSeed(levelSeed).orElse(WorldOptions.randomSeed());
@@ -152,15 +_,23 @@
@@ -155,15 +_,23 @@
this.get("initial-enabled-packs", String.join(",", WorldDataConfiguration.DEFAULT.dataPacks().getEnabled())),
this.get("initial-disabled-packs", String.join(",", WorldDataConfiguration.DEFAULT.dataPacks().getDisabled()))
);
@@ -1,6 +1,6 @@
--- a/net/minecraft/server/level/ChunkMap.java
+++ b/net/minecraft/server/level/ChunkMap.java
@@ -151,6 +_,33 @@
@@ -149,6 +_,33 @@
public int serverViewDistance;
private final WorldGenContext worldGenContext;
@@ -34,7 +34,7 @@
public ChunkMap(
final ServerLevel level,
final LevelStorageSource.LevelStorageAccess levelStorage,
@@ -176,7 +_,13 @@
@@ -174,13 +_,19 @@
this.level = level;
RegistryAccess registryAccess = level.registryAccess();
long levelSeed = level.getSeed();
@@ -48,9 +48,7 @@
+ // CraftBukkit end
this.randomState = RandomState.create(registryAccess.lookupOrThrow(Registries.NOISE), levelSeed, noiseGenerator.generatorSettings().value());
} else {
this.randomState = RandomState.create(
@@ -184,7 +_,7 @@
);
this.randomState = RandomState.create(registryAccess.lookupOrThrow(Registries.NOISE), levelSeed, false, 63, NoiseRouterData.none());
}
- this.chunkGeneratorState = generator.createState(registryAccess.lookupOrThrow(Registries.STRUCTURE_SET), this.randomState, levelSeed);
@@ -58,7 +56,7 @@
this.mainThreadExecutor = mainThreadExecutor;
ConsecutiveExecutor worldgen = new ConsecutiveExecutor(executor, "worldgen");
this.chunkStatusListener = chunkStatusListener;
@@ -211,6 +_,12 @@
@@ -206,6 +_,12 @@
this.chunksToEagerlySave.add(chunkPos.pack());
}
@@ -71,7 +69,7 @@
protected ChunkGenerator generator() {
return this.worldGenContext.generator();
}
@@ -347,9 +_,9 @@
@@ -342,9 +_,9 @@
}
});
sb.append("Updating:").append(System.lineSeparator());
@@ -83,7 +81,7 @@
CrashReport report = CrashReport.forThrowable(exception, "Chunk loading");
CrashReportCategory category = report.addCategory("Chunk loading");
category.setDetail("Details", details);
@@ -385,6 +_,7 @@
@@ -380,6 +_,7 @@
chunk.setTicketLevel(level);
} else {
chunk = new ChunkHolder(ChunkPos.unpack(node), level, this.level, this.lightEngine, this::onLevelChange, this);
@@ -91,7 +89,7 @@
}
this.updatingChunkMap.put(node, chunk);
@@ -412,8 +_,8 @@
@@ -407,8 +_,8 @@
protected void saveAllChunks(final boolean flushStorage) {
if (flushStorage) {
@@ -102,7 +100,7 @@
.stream()
.filter(ChunkHolder::wasAccessibleSinceLastSave)
.peek(ChunkHolder::refreshAccessibility)
@@ -439,7 +_,7 @@
@@ -434,7 +_,7 @@
this.nextChunkSaveTime.clear();
long now = Util.getMillis();
@@ -111,7 +109,7 @@
this.saveChunkIfNeeded(chunk, now);
}
}
@@ -460,6 +_,7 @@
@@ -455,6 +_,7 @@
public boolean hasWork() {
return this.lightEngine.hasLightWork()
|| !this.pendingUnloads.isEmpty()
@@ -119,7 +117,7 @@
|| !this.updatingChunkMap.isEmpty()
|| this.poiManager.hasWork()
|| !this.toDrop.isEmpty()
@@ -518,7 +_,11 @@
@@ -513,7 +_,11 @@
this.scheduleUnload(pos, chunkHolder);
} else {
ChunkAccess chunk = chunkHolder.getLatestChunk();
@@ -132,7 +130,7 @@
if (chunk instanceof LevelChunk levelChunk) {
levelChunk.setLoaded(false);
}
@@ -531,7 +_,9 @@
@@ -526,7 +_,9 @@
this.lightEngine.updateChunkStatus(chunk.getPos());
this.lightEngine.tryScheduleUpdate();
this.nextChunkSaveTime.remove(chunk.getPos().pack());
@@ -143,7 +141,7 @@
}
}, this.unloadQueue::add).whenComplete((ignored, throwable) -> {
if (throwable != null) {
@@ -842,7 +_,7 @@
@@ -835,7 +_,7 @@
}
public int size() {
@@ -152,7 +150,7 @@
}
public net.minecraft.server.level.DistanceManager getDistanceManager() {
@@ -869,10 +_,10 @@
@@ -862,10 +_,10 @@
.addColumn("fluid_ticks")
.build(output);
@@ -167,7 +165,7 @@
Optional<ChunkAccess> chunk = Optional.ofNullable(holder.getLatestChunk());
Optional<LevelChunk> fullChunk = chunk.flatMap(
chunkAccess -> chunkAccess instanceof LevelChunk levelChunk ? Optional.of(levelChunk) : Optional.empty()
@@ -921,14 +_,15 @@
@@ -914,14 +_,15 @@
return this.upgradeChunkTag(
tag,
-1,
@@ -186,7 +184,7 @@
generatorIdentifier.ifPresent(identifier -> contextTag.putString("generator", identifier.toString()));
return contextTag;
}
@@ -940,7 +_,7 @@
@@ -933,7 +_,7 @@
ChunkHolder holder = this.visibleChunkMap.get(spawnCandidateChunks.nextLong());
if (holder != null) {
LevelChunk chunk = holder.getTickingChunk();
@@ -195,7 +193,7 @@
output.add(chunk);
}
}
@@ -960,8 +_,14 @@
@@ -953,8 +_,14 @@
}
public boolean anyPlayerCloseEnoughForSpawning(final ChunkPos pos) {
@@ -211,7 +209,7 @@
}
public boolean anyPlayerCloseEnoughTo(final BlockPos pos, final int maxDistance) {
@@ -977,8 +_,24 @@
@@ -970,8 +_,24 @@
}
private boolean anyPlayerCloseEnoughForSpawningInternal(final ChunkPos pos) {
@@ -237,7 +235,7 @@
return true;
}
}
@@ -995,7 +_,7 @@
@@ -988,7 +_,7 @@
Builder<ServerPlayer> builder = ImmutableList.builder();
for (ServerPlayer player : this.playerMap.getAllPlayers()) {
@@ -246,7 +244,7 @@
builder.add(player);
}
}
@@ -1003,13 +_,13 @@
@@ -996,13 +_,13 @@
return builder.build();
}
@@ -262,7 +260,7 @@
}
private boolean playerIsCloseEnoughTo(final ServerPlayer player, final Vec3 pos, final int maxDistance) {
@@ -1141,9 +_,19 @@
@@ -1134,9 +_,19 @@
}
protected void addEntity(final Entity entity) {
@@ -281,8 +279,8 @@
+ range = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, range); // Spigot
if (range != 0) {
if (this.entityMap.containsKey(entity.getId())) {
throw (IllegalStateException)Util.pauseInIde(new IllegalStateException("Entity is already tracked!"));
@@ -1167,6 +_,7 @@
throw (IllegalStateException)DevUtil.pauseInIde(new IllegalStateException("Entity is already tracked!"));
@@ -1160,6 +_,7 @@
}
protected void removeEntity(final Entity entity) {
@@ -290,7 +288,7 @@
if (entity instanceof ServerPlayer player) {
this.updatePlayerStatus(player, false);
@@ -1323,10 +_,10 @@
@@ -1316,10 +_,10 @@
private final Entity entity;
private final int range;
private SectionPos lastSectionPos;
@@ -303,7 +301,7 @@
this.entity = entity;
this.range = range;
this.lastSectionPos = SectionPos.of(entity);
@@ -1373,6 +_,7 @@
@@ -1366,6 +_,7 @@
}
public void removePlayer(final ServerPlayer player) {
@@ -311,7 +309,7 @@
if (this.seenBy.remove(player.connection)) {
this.serverEntity.removePairing(player);
if (this.seenBy.isEmpty()) {
@@ -1382,23 +_,47 @@
@@ -1375,23 +_,47 @@
}
public void updatePlayer(final ServerPlayer player) {
@@ -362,7 +360,7 @@
}
} else {
this.removePlayer(player);
@@ -1415,6 +_,7 @@
@@ -1408,6 +_,7 @@
for (Entity passenger : this.entity.getIndirectPassengers()) {
int passengerRange = passenger.getType().clientTrackingRange() * 16;
@@ -1,19 +1,19 @@
--- a/net/minecraft/server/level/DistanceManager.java
+++ b/net/minecraft/server/level/DistanceManager.java
@@ -72,6 +_,12 @@
@@ -84,6 +_,12 @@
return false;
}
if (!this.chunksToUpdateFutures.isEmpty()) {
+ // CraftBukkit start - SPIGOT-7780: Call chunk unload events before updateHighestAllowedStatus
+ for (final ChunkHolder chunksToUpdateFuture : this.chunksToUpdateFutures) {
+ chunksToUpdateFuture.callEventIfUnloading(scheduler);
+ }
+ // CraftBukkit end - SPIGOT-7780: Call chunk unload events before updateHighestAllowedStatus
+ // CraftBukkit start - SPIGOT-7780: Call chunk unload events before updateHighestAllowedStatus
+ for (final ChunkHolder chunksToUpdateFuture : this.chunksToUpdateFutures) {
+ chunksToUpdateFuture.callEventIfUnloading(scheduler);
+ }
+ // CraftBukkit end - SPIGOT-7780: Call chunk unload events before updateHighestAllowedStatus
+
for (ChunkHolder chunksToUpdateFuture : this.chunksToUpdateFutures) {
chunksToUpdateFuture.updateHighestAllowedStatus(scheduler);
}
@@ -119,8 +_,10 @@
for (ChunkHolder chunksToUpdateFuture : this.chunksToUpdateFutures) {
chunksToUpdateFuture.updateHighestAllowedStatus(scheduler);
}
@@ -130,8 +_,10 @@
ChunkPos chunk = pos.chunk();
long chunkPos = chunk.pack();
ObjectSet<ServerPlayer> chunkPlayers = this.playersPerChunk.get(chunkPos);
@@ -1,6 +1,6 @@
--- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -65,6 +_,7 @@
@@ -66,6 +_,7 @@
private final SavedDataStorage savedDataStorage;
private final TicketStorage ticketStorage;
public boolean spawnEnemies = true;
@@ -8,7 +8,7 @@
private static final int CACHE_SIZE = 4;
private final long[] lastChunkPos = new long[4];
private final @Nullable ChunkStatus[] lastChunkStatus = new ChunkStatus[4];
@@ -73,6 +_,13 @@
@@ -74,6 +_,13 @@
private final Set<ChunkHolder> chunkHoldersToBroadcast = new ReferenceOpenHashSet<>();
@VisibleForDebug
private NaturalSpawner.@Nullable SpawnState lastSpawnState;
@@ -22,7 +22,7 @@
public ServerChunkCache(
final ServerLevel level,
@@ -85,6 +_,7 @@
@@ -86,6 +_,7 @@
final int simulationDistance,
final boolean syncWrites,
final ChunkStatusUpdateListener chunkStatusListener
@@ -30,7 +30,7 @@
) {
this.level = level;
this.mainThreadProcessor = new ServerChunkCache.MainThreadExecutor(level);
@@ -97,7 +_,7 @@
@@ -98,7 +_,7 @@
LOGGER.error("Failed to create dimension data storage directory", e);
}
@@ -39,7 +39,7 @@
this.ticketStorage = this.savedDataStorage.computeIfAbsent(TicketStorage.TYPE);
this.chunkMap = new ChunkMap(
level,
@@ -119,6 +_,64 @@
@@ -120,6 +_,64 @@
this.clearCache();
}
@@ -104,7 +104,7 @@
@Override
public ThreadedLevelLightEngine getLightEngine() {
return this.lightEngine;
@@ -153,7 +_,7 @@
@@ -154,7 +_,7 @@
for (int i = 0; i < 4; i++) {
if (pos == this.lastChunkPos[i] && targetStatus == this.lastChunkStatus[i]) {
ChunkAccess chunkAccess = this.lastChunk[i];
@@ -113,7 +113,7 @@
return chunkAccess;
}
}
@@ -232,7 +_,15 @@
@@ -233,7 +_,15 @@
long key = pos.pack();
int targetTicketLevel = ChunkLevel.byStatus(targetStatus);
ChunkHolder chunkHolder = this.getVisibleChunkIfPresent(key);
@@ -130,7 +130,7 @@
this.addTicket(new Ticket(TicketType.UNKNOWN, targetTicketLevel), pos);
if (this.chunkAbsent(chunkHolder, targetTicketLevel)) {
ProfilerFiller profiler = Profiler.get();
@@ -252,7 +_,7 @@
@@ -253,7 +_,7 @@
}
private boolean chunkAbsent(final @Nullable ChunkHolder chunkHolder, final int targetTicketLevel) {
@@ -139,7 +139,7 @@
}
@Override
@@ -306,17 +_,39 @@
@@ -307,17 +_,39 @@
@Override
public void close() throws IOException {
@@ -181,7 +181,7 @@
this.ticketStorage.purgeStaleTickets(this.chunkMap);
}
@@ -337,7 +_,7 @@
@@ -338,7 +_,7 @@
if (!this.level.isDebug()) {
ProfilerFiller profiler = Profiler.get();
profiler.push("pollingChunks");
@@ -190,7 +190,7 @@
profiler.push("tickingChunks");
this.tickChunks(profiler);
profiler.pop();
@@ -367,12 +_,20 @@
@@ -368,12 +_,20 @@
int chunkCount = this.distanceManager.getNaturalSpawnChunkCount();
NaturalSpawner.SpawnState spawnCookie = NaturalSpawner.createState(chunkCount, this.level, this::getFullChunk, new LocalMobCapCalculator(this.chunkMap));
this.lastSpawnState = spawnCookie;
@@ -215,7 +215,7 @@
} else {
spawningCategories = List.of();
}
@@ -557,7 +_,13 @@
@@ -558,7 +_,13 @@
@Override
public void setSpawnSettings(final boolean spawnEnemies) {
@@ -229,7 +229,7 @@
}
public String getChunkDebugData(final ChunkPos pos) {
@@ -624,12 +_,18 @@
@@ -625,12 +_,18 @@
@Override
protected boolean pollTask() {
@@ -1,6 +1,6 @@
--- a/net/minecraft/server/level/ServerEntity.java
+++ b/net/minecraft/server/level/ServerEntity.java
@@ -72,10 +_,11 @@
@@ -71,10 +_,11 @@
private Vec3 lastSentMovement;
private int tickCount;
private int teleportDelay;
@@ -13,7 +13,7 @@
public ServerEntity(
final ServerLevel level,
@@ -83,7 +_,9 @@
@@ -82,7 +_,9 @@
final UpdateInterval updateInterval,
final boolean trackDelta,
final ServerEntity.Synchronizer synchronizer
@@ -23,7 +23,7 @@
this.level = level;
this.synchronizer = synchronizer;
this.entity = entity;
@@ -104,16 +_,22 @@
@@ -103,16 +_,22 @@
List<Entity> passengers = this.entity.getPassengers();
if (!passengers.equals(this.lastPassengers)) {
this.synchronizer.sendToTrackingPlayers(new ClientboundSetPassengersPacket(this.entity));
@@ -50,7 +50,7 @@
data.tickCarriedBy(player, itemStack, frame);
Packet<?> packet = data.getUpdatePacket(id, player);
if (packet != null) {
@@ -158,7 +_,13 @@
@@ -157,7 +_,13 @@
if (this.entity instanceof AbstractArrow) {
packet = this.createMovePacket(position, yRotn, xRotn, true, true);
} else {
@@ -65,7 +65,7 @@
boolean shouldSendPosition = positionChanged || this.tickCount % 60 == 0;
packet = this.createMovePacket(position, yRotn, xRotn, shouldSendPosition, shouldSendRotation);
}
@@ -213,6 +_,25 @@
@@ -212,6 +_,25 @@
this.tickCount++;
if (this.entity.syncVelocity) {
@@ -91,7 +91,7 @@
this.entity.syncVelocity = false;
this.synchronizer.sendToTrackingPlayersAndSelf(new ClientboundSetEntityMotionPacket(this.entity));
}
@@ -309,7 +_,10 @@
@@ -308,7 +_,10 @@
public void sendPairingData(final ServerPlayer player, final Consumer<Packet<ClientGamePacketListener>> broadcast) {
this.entity.updateDataBeforeSync();
if (this.entity.isRemoved()) {
@@ -103,7 +103,7 @@
}
Packet<ClientGamePacketListener> packet = this.entity.getAddEntityPacket(this);
@@ -321,6 +_,11 @@
@@ -320,6 +_,11 @@
if (this.entity instanceof LivingEntity livingEntity) {
Collection<AttributeInstance> attributes = livingEntity.getAttributes().getSyncableAttributes();
if (!attributes.isEmpty()) {
@@ -115,7 +115,7 @@
broadcast.accept(new ClientboundUpdateAttributesPacket(this.entity.getId(), attributes));
}
}
@@ -336,8 +_,9 @@
@@ -335,8 +_,9 @@
}
if (!slots.isEmpty()) {
@@ -126,7 +126,7 @@
}
if (!this.entity.getPassengers().isEmpty()) {
@@ -384,6 +_,11 @@
@@ -383,6 +_,11 @@
if (this.entity instanceof LivingEntity livingEntity) {
Set<AttributeInstance> attributes = livingEntity.getAttributes().getAttributesToSync();
if (!attributes.isEmpty()) {
@@ -1,7 +1,7 @@
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -196,7 +_,7 @@
private final List<ServerPlayer> players = Lists.newArrayList();
private final List<ServerPlayer> players = new ArrayList<>();
private final ServerChunkCache chunkSource;
private final MinecraftServer server;
- public final ServerLevelData serverLevelData;
@@ -9,7 +9,7 @@
private final EntityTickList entityTickList = new EntityTickList();
private final ServerWaypointManager waypointManager;
private EnvironmentAttributeSystem environmentAttributes;
@@ -224,24 +_,201 @@
@@ -224,23 +_,200 @@
private final boolean tickTime;
private final LevelDebugSynchronizers debugSynchronizers = new LevelDebugSynchronizers(this);
@@ -151,7 +151,6 @@
final ResourceKey<Level> dimension,
final LevelStem levelStem,
final boolean isDebug,
final long biomeZoomSeed,
final List<CustomSpawner> customSpawners,
final boolean tickTime
+ // Paper start - add parameters
@@ -163,7 +162,7 @@
+ io.papermc.paper.world.PaperWorldLoader.LoadedWorldData loadedWorldData
+ // Paper end - add parameters
) {
- super(levelData, dimension, server.registryAccess(), levelStem.type(), false, isDebug, biomeZoomSeed, server.getMaxChainedNeighborUpdates());
- super(levelData, dimension, server.registryAccess(), levelStem.type(), false, isDebug, server.getMaxChainedNeighborUpdates());
+ // CraftBukkit start
+ final io.papermc.paper.world.saveddata.PaperLevelOverrides levelData = loadedWorldData.levelOverrides();
+ savedDataStorage.set(io.papermc.paper.world.saveddata.PaperLevelOverrides.TYPE, levelData);
@@ -171,7 +170,7 @@
+ savedDataStorage.set(io.papermc.paper.world.saveddata.PaperWorldPDC.TYPE, loadedWorldData.pdc() == null ? io.papermc.paper.world.saveddata.PaperWorldPDC.TYPE.constructor().get() : loadedWorldData.pdc());
+ final GameRules gameRules = new GameRules(server.getWorldData().enabledFeatures(), savedDataStorage.computeIfAbsent(net.minecraft.world.level.gamerules.GameRuleMap.TYPE));
+ this.gameRules = gameRules;
+ super(levelData, dimension, server.registryAccess(), levelStem.type(), false, isDebug, biomeZoomSeed, server.getMaxChainedNeighborUpdates(), loadedWorldData.bukkitName(), gen, biomeProvider, env, spigotConfig -> server.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(server.storageSource.getDimensionPath(dimension), dimension.identifier(), spigotConfig, server.registryAccess(), gameRules))); // Paper - create paper world configs
+ super(levelData, dimension, server.registryAccess(), levelStem.type(), false, isDebug, server.getMaxChainedNeighborUpdates(), loadedWorldData.bukkitName(), gen, biomeProvider, env, spigotConfig -> server.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(server.storageSource.getDimensionPath(dimension), dimension.identifier(), spigotConfig, server.registryAccess(), gameRules))); // Paper - create paper world configs
+ this.weatherData = savedDataStorage.computeIfAbsent(WeatherData.TYPE);
+ this.weatherData.setLevel(this);
+ this.typeKey = typeKey;
@@ -213,7 +212,7 @@
boolean syncWrites = server.forceSynchronousWrites();
DataFixer fixerUpper = server.getFixerUpper();
EntityPersistentStorage<Entity> entityStorage = new EntityStorage(
@@ -263,15 +_,16 @@
@@ -262,15 +_,16 @@
server.getStructureTemplateManager(),
executor,
generator,
@@ -233,7 +232,7 @@
}
this.raids = this.getDataStorage().computeIfAbsent(Raids.TYPE);
@@ -279,10 +_,10 @@
@@ -278,10 +_,10 @@
levelData.setGameType(server.getDefaultGameType());
}
@@ -245,14 +244,16 @@
this.chunkSource.chunkScanner(),
this.registryAccess(),
server.getStructureTemplateManager(),
@@ -302,11 +_,19 @@
@@ -301,13 +_,21 @@
this.sleepStatus = new SleepStatus();
this.gameEventDispatcher = new GameEventDispatcher(this);
- this.waypointManager = new ServerWaypointManager();
+ this.waypointManager = new ServerWaypointManager(this); // Paper - optimize ServerWaypointManager with locator bar disabled
this.environmentAttributes = EnvironmentAttributeSystem.builder().addDefaultLayers(this).build();
this.uncachedBiomeResolver = this.getChunkSource().getGenerator().getBiomeSource().createUncachedResolver(this.getChunkSource().randomState());
this.uncachedBiomeResolver = new BiomeManager(
this.getChunkSource().getGenerator().getBiomeSource().createUncachedResolver(this.getChunkSource().randomState()), BiomeManager.obfuscateSeed(seed)
);
this.updateSkyBrightness();
- }
+ this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit
@@ -267,7 +268,7 @@
@Override
public int getNextEntityId() {
@@ -340,7 +_,7 @@
@@ -337,7 +_,7 @@
@Override
public ServerClockManager clockManager() {
@@ -276,7 +277,7 @@
}
@Override
@@ -373,11 +_,25 @@
@@ -370,11 +_,25 @@
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();
@@ -304,7 +305,7 @@
if (this.getGameRules().get(GameRules.ADVANCE_WEATHER) && this.isRaining()) {
this.resetWeatherCycle();
}
@@ -392,9 +_,9 @@
@@ -389,9 +_,9 @@
if (!this.isDebug() && runs) {
long tick = this.getGameTime();
profiler.push("blockTicks");
@@ -316,7 +317,7 @@
profiler.pop();
}
@@ -412,7 +_,7 @@
@@ -409,7 +_,7 @@
this.handlingTick = false;
profiler.pop();
@@ -325,7 +326,7 @@
if (isActive) {
this.resetEmptyTime();
}
@@ -456,9 +_,12 @@
@@ -453,9 +_,12 @@
}
}
);
@@ -338,7 +339,7 @@
}
profiler.push("entityManagement");
@@ -487,7 +_,7 @@
@@ -484,7 +_,7 @@
long time = this.levelData.getGameTime() + 1L;
this.serverLevelData.setGameTime(time);
Profiler.get().push("scheduledFunctions");
@@ -347,7 +348,7 @@
Profiler.get().pop();
}
}
@@ -510,11 +_,13 @@
@@ -507,11 +_,13 @@
ProfilerFiller profiler = Profiler.get();
profiler.push("iceandsnow");
@@ -361,7 +362,7 @@
profiler.popPush("tickBlocks");
if (tickSpeed > 0) {
@@ -555,12 +_,12 @@
@@ -552,12 +_,12 @@
int minZ = chunkPos.getMinBlockZ();
ProfilerFiller profiler = Profiler.get();
profiler.push("thunder");
@@ -376,7 +377,7 @@
&& !this.getBlockState(pos.below()).is(BlockTags.LIGHTNING_RODS);
if (isTrap) {
SkeletonHorse horse = EntityTypes.SKELETON_HORSE.create(this, EntitySpawnReason.EVENT);
@@ -568,7 +_,7 @@
@@ -565,7 +_,7 @@
horse.setTrap(true);
horse.setAge(0);
horse.setPos(pos.getX(), pos.getY(), pos.getZ());
@@ -385,7 +386,7 @@
}
}
@@ -576,7 +_,7 @@
@@ -573,7 +_,7 @@
if (bolt != null) {
bolt.snapTo(Vec3.atBottomCenterOf(pos));
bolt.setVisualOnly(isTrap);
@@ -394,7 +395,7 @@
}
}
}
@@ -590,7 +_,7 @@
@@ -587,7 +_,7 @@
BlockPos belowPos = topPos.below();
Biome biome = this.getBiome(topPos).value();
if (biome.shouldFreeze(this, belowPos)) {
@@ -403,7 +404,7 @@
}
if (this.isRaining()) {
@@ -602,10 +_,10 @@
@@ -599,10 +_,10 @@
if (currentLayers < Math.min(maxHeight, 8)) {
BlockState newState = state.setValue(SnowLayerBlock.LAYERS, currentLayers + 1);
Block.pushEntitiesUp(state, newState, this, topPos);
@@ -416,7 +417,7 @@
}
}
@@ -630,6 +_,13 @@
@@ -627,6 +_,13 @@
}
protected BlockPos findLightningTargetAround(final BlockPos pos) {
@@ -430,7 +431,7 @@
BlockPos center = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, pos);
Optional<BlockPos> lightningRodTarget = this.findLightningRod(center);
if (lightningRodTarget.isPresent()) {
@@ -637,11 +_,12 @@
@@ -634,11 +_,12 @@
}
AABB search = AABB.encapsulatingFullBlocks(center, center.atY(this.getMaxY() + 1)).inflate(3.0);
@@ -444,7 +445,7 @@
if (center.getY() == this.getMinY() - 1) {
center = center.above(2);
}
@@ -758,8 +_,8 @@
@@ -755,8 +_,8 @@
weatherData.setThunderTime(thunderTime);
weatherData.setRainTime(rainTime);
weatherData.setClearWeatherTime(clearWeatherTime);
@@ -455,15 +456,15 @@
}
this.oThunderLevel = this.thunderLevel;
@@ -780,6 +_,7 @@
this.rainLevel = Mth.clamp(this.rainLevel, 0.0F, 1.0F);
@@ -777,6 +_,7 @@
this.rainLevel = Math.clamp(this.rainLevel, 0.0F, 1.0F);
}
+ /* CraftBukkit start
if (this.oRainLevel != this.rainLevel) {
this.server
.getPlayerList()
@@ -802,15 +_,48 @@
@@ -799,15 +_,48 @@
this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.RAIN_LEVEL_CHANGE, this.rainLevel));
this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.THUNDER_LEVEL_CHANGE, this.thunderLevel));
}
@@ -516,7 +517,7 @@
}
public void resetEmptyTime() {
@@ -832,17 +_,44 @@
@@ -829,17 +_,44 @@
}
}
@@ -561,7 +562,7 @@
}
private void tickPassenger(final Entity vehicle, final Entity entity) {
@@ -854,6 +_,7 @@
@@ -851,6 +_,7 @@
profiler.incrementCounter("tickPassenger");
entity.commonTick();
entity.rideTick();
@@ -569,7 +570,7 @@
profiler.pop();
for (Entity passenger : entity.getPassengers()) {
@@ -884,6 +_,7 @@
@@ -881,6 +_,7 @@
public void save(final @Nullable ProgressListener progressListener, final boolean flush, final boolean noSave) {
ServerChunkCache chunkSource = this.getChunkSource();
if (!noSave) {
@@ -577,7 +578,7 @@
if (progressListener != null) {
progressListener.progressStartNoAbort(Component.translatable("menu.savingLevel"));
}
@@ -904,6 +_,7 @@
@@ -901,6 +_,7 @@
private void saveLevelData(final boolean sync) {
SavedDataStorage savedDataStorage = this.getChunkSource().getDataStorage();
@@ -585,7 +586,7 @@
if (sync) {
savedDataStorage.saveAndJoin();
} else {
@@ -966,18 +_,40 @@
@@ -963,18 +_,40 @@
@Override
public boolean addFreshEntity(final Entity entity) {
@@ -629,7 +630,7 @@
}
}
@@ -1000,42 +_,121 @@
@@ -997,42 +_,121 @@
this.entityManager.addNewEntity(player);
}
@@ -690,7 +691,7 @@
+ // Paper start - this area looks like it can load chunks, change the behavior
+ // chests for example can apply physics to the world
+ // so instead we just change the active container and call the event
+ for (org.bukkit.entity.HumanEntity human : Lists.newArrayList(((net.minecraft.world.Container) blockEntity).getViewers())) {
+ for (org.bukkit.entity.HumanEntity human : new ArrayList<>(((net.minecraft.world.Container) blockEntity).getViewers())) {
+ ((org.bukkit.craftbukkit.entity.CraftHumanEntity) human).getHandle().closeUnloadedInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.UNLOADED); // Paper - Inventory close reason
+ }
+ // Paper end - this area looks like it can load chunks, change the behavior
@@ -736,7 +737,7 @@
+ // If a plugin is using this method to send destroy packets for a client-side only entity id, no block progress occurred on the server.
+ // Hence, do not call the event.
+ if (entity != null) {
+ float progressFloat = Mth.clamp(progress, 0, 10) / 10.0f;
+ float progressFloat = Math.clamp(progress, 0, 10) / 10.0f;
+ org.bukkit.craftbukkit.block.CraftBlock bukkitBlock = org.bukkit.craftbukkit.block.CraftBlock.at(this, blockPos);
+ new io.papermc.paper.event.block.BlockBreakProgressUpdateEvent(bukkitBlock, progressFloat, entity.getBukkitEntity())
+ .callEvent();
@@ -756,7 +757,7 @@
player.connection.send(new ClientboundBlockDestructionPacket(id, blockPos, progress));
}
}
@@ -1123,7 +_,7 @@
@@ -1120,7 +_,7 @@
pos.getX(),
pos.getY(),
pos.getZ(),
@@ -765,7 +766,7 @@
this.dimension(),
new ClientboundLevelEventPacket(type, pos, data, false)
);
@@ -1135,6 +_,11 @@
@@ -1132,6 +_,11 @@
@Override
public void gameEvent(final Holder<GameEvent> gameEvent, final Vec3 position, final GameEvent.Context context) {
@@ -777,7 +778,7 @@
this.gameEventDispatcher.post(gameEvent, position, context);
}
@@ -1147,17 +_,28 @@
@@ -1144,17 +_,28 @@
this.getChunkSource().blockChanged(pos);
this.pathTypesByPosCache.invalidate(pos);
@@ -806,7 +807,7 @@
try {
this.isUpdatingNavigations = true;
@@ -1169,15 +_,23 @@
@@ -1166,15 +_,23 @@
this.isUpdatingNavigations = false;
}
}
@@ -830,9 +831,9 @@
this.neighborUpdater.updateNeighborsAtExceptFromFacing(pos, sourceBlock, null, orientation);
}
@@ -1231,6 +_,44 @@
@@ -1228,6 +_,44 @@
final WeightedList<ExplosionParticleInfo> blockParticles,
final Holder<SoundEvent> explosionSound
final @Nullable Holder<SoundEvent> explosionSound
) {
+ // CraftBukkit start
+ this.explode0(source, damageSource, damageCalculator, x, y, z, r, fire, interactionType, smallExplosionParticles, largeExplosionParticles, blockParticles, explosionSound);
@@ -875,7 +876,7 @@
Explosion.BlockInteraction blockInteraction = switch (interactionType) {
case NONE -> Explosion.BlockInteraction.KEEP;
case BLOCK -> this.getDestroyType(GameRules.BLOCK_EXPLOSION_DROP_DECAY);
@@ -1239,10 +_,17 @@
@@ -1236,10 +_,17 @@
: Explosion.BlockInteraction.KEEP;
case TNT -> this.getDestroyType(GameRules.TNT_EXPLOSION_DROP_DECAY);
case TRIGGER -> Explosion.BlockInteraction.TRIGGER_BLOCK;
@@ -890,11 +891,11 @@
+ return explosion;
+ }
+ // CraftBukkit end
ParticleOptions explosionParticle = explosion.isSmall() ? smallExplosionParticles : largeExplosionParticles;
Optional<ParticleOptions> explosionParticle = Optional.ofNullable(explosion.isSmall() ? smallExplosionParticles : largeExplosionParticles);
Optional<Holder<SoundEvent>> effectiveExplosionSound = source != null && source.isSilent() ? Optional.empty() : Optional.ofNullable(explosionSound);
for (ServerPlayer player : this.players) {
@@ -1256,6 +_,8 @@
);
@@ -1250,6 +_,8 @@
.send(new ClientboundExplodePacket(center, r, blockCount, playerKnockback, explosionParticle, effectiveExplosionSound, blockParticles));
}
}
+
@@ -902,7 +903,7 @@
}
private Explosion.BlockInteraction getDestroyType(final GameRule<Boolean> gameRule) {
@@ -1333,73 +_,167 @@
@@ -1327,73 +_,167 @@
final double zDist,
final double speed
) {
@@ -1136,7 +1137,7 @@
particle,
overrideLimiter,
alwaysShow,
@@ -1431,7 +_,27 @@
@@ -1425,7 +_,27 @@
final double speed,
final ClientboundLevelParticlesPacket.RandomizationType randomizationType
) {
@@ -1165,7 +1166,7 @@
}
public <T extends ParticleOptions> int sendParticles(
@@ -1450,6 +_,68 @@
@@ -1444,6 +_,68 @@
final double zSpeed,
final ClientboundLevelParticlesPacket.RandomizationType randomizationType
) {
@@ -1234,7 +1235,7 @@
ClientboundLevelParticlesPacket packet = new ClientboundLevelParticlesPacket(
particle,
overrideLimiter,
@@ -1468,8 +_,9 @@
@@ -1462,8 +_,9 @@
);
int result = 0;
@@ -1246,7 +1247,7 @@
if (this.sendParticles(player, overrideLimiter, x, y, z, packet)) {
result++;
}
@@ -1613,10 +_,36 @@
@@ -1607,10 +_,36 @@
@Override
public @Nullable MapItemSavedData getMapData(final MapId id) {
@@ -1284,7 +1285,7 @@
this.getServer().getDataStorage().set(MapItemSavedData.type(id), data);
}
@@ -1626,7 +_,20 @@
@@ -1620,7 +_,20 @@
@Override
public void setRespawnData(final LevelData.RespawnData respawnData) {
@@ -1306,7 +1307,7 @@
}
@Override
@@ -1663,6 +_,11 @@
@@ -1657,6 +_,11 @@
this.debugSynchronizers.dropPoi(immutable);
}));
newType.ifPresent(poiType -> this.getServer().execute(() -> {
@@ -1318,7 +1319,7 @@
PoiRecord record = this.getPoiManager().add(immutable, (Holder<PoiType>)poiType);
if (record != null) {
this.debugSynchronizers.registerPoi(record);
@@ -1811,12 +_,12 @@
@@ -1805,12 +_,12 @@
}
public boolean isFlat() {
@@ -1333,7 +1334,7 @@
}
public @Nullable EnderDragonFight getDragonFight() {
@@ -1824,7 +_,7 @@
@@ -1818,7 +_,7 @@
}
public WeatherData getWeatherData() {
@@ -1342,7 +1343,7 @@
}
@Override
@@ -1871,6 +_,7 @@
@@ -1865,6 +_,7 @@
@Override
public LevelEntityGetter<Entity> getEntities() {
@@ -1350,7 +1351,7 @@
return this.entityManager.getEntityGetter();
}
@@ -1963,8 +_,30 @@
@@ -1961,8 +_,30 @@
}
public GameRules getGameRules() {
@@ -1383,7 +1384,7 @@
@Override
public CrashReportCategory fillReportDetails(final CrashReport report) {
@@ -2022,6 +_,7 @@
@@ -2020,6 +_,7 @@
if (entity instanceof WaypointTransmitter waypoint && waypoint.isTransmittingWaypoint()) {
ServerLevel.this.getWaypointManager().trackWaypoint(waypoint);
}
@@ -1391,7 +1392,7 @@
}
@Override
@@ -2035,17 +_,24 @@
@@ -2033,17 +_,24 @@
@Override
public void onTickingStart(final Entity entity) {
@@ -1417,16 +1418,16 @@
if (entity instanceof ServerPlayer player) {
ServerLevel.this.players.add(player);
if (player.isReceivingWaypoints()) {
@@ -2060,7 +_,7 @@
@@ -2058,7 +_,7 @@
}
if (entity instanceof Mob mob) {
- if (ServerLevel.this.isUpdatingNavigations) {
+ if (false && ServerLevel.this.isUpdatingNavigations) { // Paper - Remove unnecessary onTrackingStart during navigation warning
String message = "onTrackingStart called during navigation iteration";
Util.logAndPauseIfInIde(
DevUtil.logAndPauseIfInIde(
"onTrackingStart called during navigation iteration", new IllegalStateException("onTrackingStart called during navigation iteration")
@@ -2077,10 +_,51 @@
@@ -2075,10 +_,51 @@
}
entity.updateDynamicGameEventListener(DynamicGameEventListener::add);
@@ -1470,7 +1471,7 @@
+ merchant.getTrader().closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.UNLOADED);
+ }
+ // Paper end - Fix merchant inventory not closing on entity removal
+ for (org.bukkit.entity.HumanEntity h : Lists.newArrayList(((org.bukkit.inventory.InventoryHolder) entity.getBukkitEntity()).getInventory().getViewers())) {
+ for (org.bukkit.entity.HumanEntity h : new ArrayList<>(((org.bukkit.inventory.InventoryHolder) entity.getBukkitEntity()).getInventory().getViewers())) {
+ h.closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.UNLOADED); // Paper - Inventory close reason
+ }
+ }
@@ -1478,16 +1479,16 @@
ServerLevel.this.getChunkSource().removeEntity(entity);
if (entity instanceof ServerPlayer player) {
ServerLevel.this.players.remove(player);
@@ -2089,7 +_,7 @@
@@ -2087,7 +_,7 @@
}
if (entity instanceof Mob mob) {
- if (ServerLevel.this.isUpdatingNavigations) {
+ if (false && ServerLevel.this.isUpdatingNavigations) { // Paper - Remove unnecessary onTrackingStart during navigation warning
String message = "onTrackingStart called during navigation iteration";
Util.logAndPauseIfInIde(
DevUtil.logAndPauseIfInIde(
"onTrackingStart called during navigation iteration", new IllegalStateException("onTrackingStart called during navigation iteration")
@@ -2107,6 +_,15 @@
@@ -2105,6 +_,15 @@
entity.updateDynamicGameEventListener(DynamicGameEventListener::remove);
ServerLevel.this.debugSynchronizers.dropEntity(entity);
@@ -1503,7 +1504,7 @@
}
@Override
@@ -2114,4 +_,28 @@
@@ -2112,4 +_,28 @@
entity.updateDynamicGameEventListener(DynamicGameEventListener::move);
}
}
@@ -1,6 +1,6 @@
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -260,7 +_,8 @@
@@ -259,7 +_,8 @@
private int levitationStartTime;
private boolean disconnected;
private int requestedViewDistance = 2;
@@ -10,7 +10,7 @@
private @Nullable Vec3 startingToFallPosition;
private @Nullable Vec3 enteredNetherPosition;
private @Nullable Vec3 enteredLavaOnVehiclePosition;
@@ -308,6 +_,13 @@
@@ -307,6 +_,13 @@
}
}
@@ -24,7 +24,7 @@
@Override
public void sendSlotChange(final AbstractContainerMenu container, final int slotIndex, final ItemStack itemStack) {
ServerPlayer.this.connection.send(new ClientboundContainerSetSlotPacket(container.containerId, container.incrementStateId(), slotIndex, itemStack));
@@ -343,6 +_,32 @@
@@ -342,6 +_,32 @@
}
}
@@ -57,7 +57,7 @@
@Override
public void dataChanged(final AbstractContainerMenu container, final int id, final int value) {
}
@@ -369,10 +_,43 @@
@@ -368,10 +_,43 @@
public void sendSystemMessage(final Component message) {
ServerPlayer.this.sendSystemMessage(message);
}
@@ -101,7 +101,7 @@
public ServerPlayer(final MinecraftServer server, final ServerLevel level, final GameProfile gameProfile, final ClientInformation clientInformation) {
super(level, gameProfile);
@@ -383,8 +_,14 @@
@@ -382,8 +_,14 @@
this.recipeBook = new ServerRecipeBook((id, output) -> server.getRecipeManager().listDisplaysForRecipe(id, output));
this.stats = server.getPlayerList().getPlayerStats(this);
this.advancements = server.getPlayerList().getPlayerAdvancements(this);
@@ -117,7 +117,7 @@
}
@Override
@@ -403,6 +_,7 @@
@@ -402,6 +_,7 @@
this.seenCredits = input.getBooleanOr("seenCredits", false);
input.read("recipeBook", ServerRecipeBook.Packed.CODEC)
.ifPresent(p -> this.recipeBook.loadUntrusted(p, id -> this.server.getRecipeManager().byKey(id).isPresent()));
@@ -125,7 +125,7 @@
if (this.isSleeping()) {
this.stopSleeping();
}
@@ -410,13 +_,27 @@
@@ -409,13 +_,27 @@
this.respawnConfig = input.read("respawn", ServerPlayer.RespawnConfig.CODEC).orElse(null);
this.spawnExtraParticlesOnFall = input.getBooleanOr("spawn_extra_particles_on_fall", false);
this.raidOmenPosition = input.read("raid_omen_position", BlockPos.CODEC).orElse(null);
@@ -155,7 +155,7 @@
@Override
protected void addAdditionalSaveData(final ValueOutput output) {
super.addAdditionalSaveData(output);
@@ -440,12 +_,24 @@
@@ -439,12 +_,24 @@
if (!this.getShoulderEntityRight().isEmpty()) {
output.store("ShoulderEntityRight", CompoundTag.CODEC, this.getShoulderEntityRight());
}
@@ -181,7 +181,7 @@
ValueOutput vehicleWrapper = playerOutput.child("RootVehicle");
vehicleWrapper.store("Attach", UUIDUtil.CODEC, vehicle.getUUID());
rootVehicle.save(vehicleWrapper.child("Entity"));
@@ -457,7 +_,7 @@
@@ -456,7 +_,7 @@
if (!rootTag.isEmpty()) {
ServerLevel serverLevel = this.level();
Entity vehicle = EntityType.loadEntityRecursive(
@@ -190,7 +190,7 @@
);
if (vehicle != null) {
UUID attachTo = rootTag.get().read("Attach", UUIDUtil.CODEC).orElse(null);
@@ -474,10 +_,10 @@
@@ -473,10 +_,10 @@
if (!this.isPassenger()) {
LOGGER.warn("Couldn't reattach entity to player");
@@ -203,7 +203,7 @@
}
}
}
@@ -489,6 +_,7 @@
@@ -488,6 +_,7 @@
ValueOutput.ValueOutputList pearlsOutput = playerOutput.childrenList("ender_pearls");
for (ThrownEnderpearl enderPearl : this.enderPearls) {
@@ -211,7 +211,7 @@
if (enderPearl.isRemoved()) {
LOGGER.warn("Trying to save removed ender pearl, skipping");
} else {
@@ -587,6 +_,11 @@
@@ -586,6 +_,11 @@
@Override
public void tick() {
@@ -223,7 +223,7 @@
this.connection.tickClientLoadTimeout();
this.gameMode.tick();
this.wardenSpawnTracker.tick();
@@ -594,9 +_,18 @@
@@ -593,9 +_,18 @@
this.damageCooldownTime--;
}
@@ -245,7 +245,7 @@
this.containerMenu = this.inventoryMenu;
}
@@ -658,10 +_,10 @@
@@ -657,10 +_,10 @@
public void doTick() {
try {
@@ -258,7 +258,7 @@
this.containerMenu = this.inventoryMenu;
}
@@ -691,7 +_,7 @@
@@ -690,7 +_,7 @@
if (this.getHealth() != this.lastSentHealth
|| this.lastSentFood != this.foodData.getFoodLevel()
|| this.foodData.getSaturationLevel() == 0.0F != this.lastFoodSaturationZero) {
@@ -267,7 +267,7 @@
this.lastSentHealth = this.getHealth();
this.lastSentFood = this.foodData.getFoodLevel();
this.lastFoodSaturationZero = this.foodData.getSaturationLevel() == 0.0F;
@@ -722,6 +_,12 @@
@@ -721,6 +_,12 @@
this.updateScoreForCriteria(ObjectiveCriteria.EXPERIENCE, Mth.ceil(this.lastRecordedExperience));
}
@@ -280,7 +280,7 @@
if (this.experienceLevel != this.lastRecordedLevel) {
this.lastRecordedLevel = this.experienceLevel;
this.updateScoreForCriteria(ObjectiveCriteria.LEVEL, Mth.ceil(this.lastRecordedLevel));
@@ -735,6 +_,20 @@
@@ -734,6 +_,20 @@
if (this.tickCount % 20 == 0) {
CriteriaTriggers.LOCATION.trigger(this);
}
@@ -301,7 +301,7 @@
} catch (Throwable t) {
CrashReport report = CrashReport.forThrowable(t, "Ticking player");
CrashReportCategory category = report.addCategory("Player being ticked");
@@ -759,7 +_,7 @@
@@ -758,7 +_,7 @@
if (this.level().getDifficulty() == Difficulty.PEACEFUL && this.level().getGameRules().get(GameRules.NATURAL_HEALTH_REGENERATION)) {
if (this.tickCount % 20 == 0) {
if (this.getHealth() < this.getMaxHealth()) {
@@ -310,7 +310,7 @@
}
float saturation = this.foodData.getSaturationLevel();
@@ -779,6 +_,7 @@
@@ -778,6 +_,7 @@
this.playShoulderEntityAmbientSound(this.getShoulderEntityLeft());
this.playShoulderEntityAmbientSound(this.getShoulderEntityRight());
if (this.fallDistance > 0.5 || this.isInWater() || this.getAbilities().flying || this.isSleeping() || this.isInPowderSnow) {
@@ -318,7 +318,7 @@
this.removeEntitiesOnShoulder();
}
}
@@ -823,32 +_,61 @@
@@ -822,32 +_,61 @@
@Override
public void removeEntitiesOnShoulder() {
if (this.timeEntitySatOnShoulder + 20L < this.level().getGameTime()) {
@@ -387,7 +387,7 @@
}
@Override
@@ -888,15 +_,37 @@
@@ -887,15 +_,37 @@
}
private void updateScoreForCriteria(final ObjectiveCriteria criteria, final int value) {
@@ -434,7 +434,7 @@
this.connection
.send(
new ClientboundPlayerCombatKillPacket(this.getId(), deathMessage),
@@ -913,6 +_,65 @@
@@ -912,6 +_,65 @@
}
)
);
@@ -500,7 +500,7 @@
Team team = this.getTeam();
if (team == null || team.getDeathMessageVisibility() == Team.Visibility.ALWAYS) {
this.server.getPlayerList().broadcastSystemMessage(deathMessage, false);
@@ -922,7 +_,7 @@
@@ -921,7 +_,7 @@
this.server.getPlayerList().broadcastSystemToAllExceptTeam(this, deathMessage);
}
} else {
@@ -509,7 +509,7 @@
}
this.removeEntitiesOnShoulder();
@@ -930,11 +_,35 @@
@@ -929,11 +_,35 @@
this.tellNeutralMobsThatIDied();
}
@@ -548,7 +548,7 @@
LivingEntity killer = this.getKillCredit();
if (killer != null) {
this.awardStat(Stats.ENTITY_KILLED_BY.get(killer.getType()));
@@ -968,10 +_,10 @@
@@ -967,10 +_,10 @@
if (victim != this) {
super.awardKillScore(victim, killingBlow);
Scoreboard scoreboard = this.level().getScoreboard();
@@ -561,7 +561,7 @@
} else {
this.awardStat(Stats.MOB_KILLS);
}
@@ -984,11 +_,11 @@
@@ -983,11 +_,11 @@
private void handleTeamKill(final ScoreHolder source, final ScoreHolder target, final Map<TeamColor, ObjectiveCriteria> criteriaByTeam) {
Scoreboard scoreboard = this.level().getScoreboard();
@@ -575,7 +575,7 @@
}
}
}
@@ -1000,9 +_,20 @@
@@ -999,9 +_,20 @@
}
Entity entity = source.getEntity();
@@ -599,7 +599,7 @@
}
@Override
@@ -1014,24 +_,87 @@
@@ -1013,24 +_,87 @@
return this.level().isPvpAllowed();
}
@@ -694,7 +694,7 @@
}
public boolean isReceivingWaypoints() {
@@ -1054,8 +_,21 @@
@@ -1053,8 +_,21 @@
public void sendPostEffects() {
this.postEffectsDirty = false;
@@ -718,7 +718,7 @@
public boolean addPostEffect(final Identifier postEffect) {
if (this.postEffects.contains(postEffect)) {
@@ -1103,15 +_,17 @@
@@ -1102,15 +_,17 @@
if (block instanceof RespawnAnchorBlock && (forced || blockState.getValue(RespawnAnchorBlock.CHARGE) > 0) && RespawnAnchorBlock.canSetSpawn(level, pos)
)
{
@@ -739,7 +739,7 @@
} else {
if (!forced) {
return Optional.empty();
@@ -1121,7 +_,7 @@
@@ -1120,7 +_,7 @@
BlockState topState = level.getBlockState(pos.above());
boolean freeTop = topState.getBlock().isPossibleToRespawnInThis(topState);
return freeBottom && freeTop
@@ -748,7 +748,7 @@
: Optional.empty();
}
}
@@ -1137,7 +_,8 @@
@@ -1136,7 +_,8 @@
}
@Override
@@ -758,7 +758,7 @@
if (this.isRemoved()) {
return null;
}
@@ -1149,12 +_,36 @@
@@ -1148,12 +_,36 @@
ServerLevel newLevel = transition.newLevel();
ServerLevel oldLevel = this.level();
ResourceKey<Level> lastDimension = oldLevel.dimension();
@@ -796,7 +796,7 @@
this.connection.resetPosition();
transition.postTeleportTransition().onTransition(this);
return this;
@@ -1162,22 +_,24 @@
@@ -1161,22 +_,24 @@
this.isChangingDimension = true;
LevelData levelData = newLevel.getLevelData();
@@ -823,7 +823,7 @@
this.connection.resetPosition();
newLevel.addDuringTeleport(this);
profiler.pop();
@@ -1193,6 +_,15 @@
@@ -1192,6 +_,15 @@
this.lastSentHealth = -1.0F;
this.lastSentFood = -1;
this.teleportSpectators(transition, oldLevel);
@@ -839,7 +839,7 @@
return this;
}
@@ -1205,14 +_,29 @@
@@ -1204,14 +_,29 @@
public void triggerDimensionChangeTriggers(final ServerLevel oldLevel) {
ResourceKey<Level> oldKey = oldLevel.dimension();
ResourceKey<Level> newKey = this.level().dimension();
@@ -872,7 +872,7 @@
}
@Override
@@ -1226,11 +_,10 @@
@@ -1225,11 +_,10 @@
this.containerMenu.broadcastChanges();
}
@@ -888,7 +888,7 @@
if (!this.isSleeping() && this.isAlive()) {
boolean canSleep = rule.canSleep(this.level());
boolean canSetSpawn = rule.canSetSpawn(this.level());
@@ -1244,7 +_,7 @@
@@ -1243,7 +_,7 @@
if (canSetSpawn) {
this.setRespawnPosition(
@@ -897,7 +897,7 @@
);
}
@@ -1267,7 +_,36 @@
@@ -1266,7 +_,36 @@
}
}
@@ -935,7 +935,7 @@
this.awardStat(bedBlock.getSleptInBedStatType());
if (canSetSpawn) {
CriteriaTriggers.SLEPT_IN_BED.trigger(this);
@@ -1279,8 +_,6 @@
@@ -1278,8 +_,6 @@
this.level().updateSleepingPlayerList();
return result;
@@ -944,7 +944,7 @@
}
}
@@ -1316,19 +_,30 @@
@@ -1315,19 +_,30 @@
@Override
public void stopSleepInBed(final boolean forcefulWakeUp, final boolean updateLevelList) {
@@ -977,7 +977,7 @@
}
@Override
@@ -1377,8 +_,9 @@
@@ -1376,8 +_,9 @@
this.connection.send(new ClientboundShowDialogPacket(dialog));
}
@@ -988,7 +988,7 @@
}
@Override
@@ -1387,12 +_,39 @@
@@ -1386,12 +_,39 @@
return OptionalInt.empty();
}
@@ -1029,7 +1029,7 @@
if (menu == null) {
if (this.isSpectator()) {
this.sendOverlayMessage(Component.translatable("container.spectatorCantOpen").withStyle(ChatFormatting.RED));
@@ -1400,9 +_,14 @@
@@ -1399,9 +_,14 @@
return OptionalInt.empty();
} else {
@@ -1046,7 +1046,7 @@
return OptionalInt.of(this.containerCounter);
}
}
@@ -1421,27 +_,49 @@
@@ -1420,27 +_,49 @@
@Override
public void openHorseInventory(final AbstractHorse horse, final Container container) {
@@ -1101,7 +1101,7 @@
this.initMenu(this.containerMenu);
}
@@ -1463,10 +_,30 @@
@@ -1462,10 +_,30 @@
@Override
public void closeContainer() {
@@ -1132,7 +1132,7 @@
@Override
public void doCloseContainer() {
this.containerMenu.removed(this);
@@ -1489,19 +_,19 @@
@@ -1488,19 +_,19 @@
int distance = Math.round((float)Math.sqrt(dx * dx + dy * dy + dz * dz) * 100.0F);
if (distance > 0) {
this.awardStat(Stats.SWIM_ONE_CM, distance);
@@ -1155,7 +1155,7 @@
}
} else if (this.onClimbable()) {
if (dy > 0.0) {
@@ -1512,13 +_,13 @@
@@ -1511,13 +_,13 @@
if (horizontalDistance > 0) {
if (this.isSprinting()) {
this.awardStat(Stats.SPRINT_ONE_CM, horizontalDistance);
@@ -1172,7 +1172,7 @@
}
}
} else if (this.isFallFlying()) {
@@ -1562,13 +_,13 @@
@@ -1561,13 +_,13 @@
@Override
public void awardStat(final Stat<?> stat, final int count) {
this.stats.increment(this, stat, count);
@@ -1188,7 +1188,7 @@
}
@Override
@@ -1597,9 +_,9 @@
@@ -1596,9 +_,9 @@
super.jumpFromGround();
this.awardStat(Stats.JUMP);
if (this.isSprinting()) {
@@ -1200,7 +1200,7 @@
}
}
@@ -1614,6 +_,13 @@
@@ -1613,6 +_,13 @@
public void disconnect() {
this.disconnected = true;
this.ejectPassengers();
@@ -1214,7 +1214,7 @@
if (this.isSleeping()) {
this.stopSleepInBed(true, false);
}
@@ -1625,6 +_,7 @@
@@ -1624,6 +_,7 @@
public void resetSentInfo() {
this.lastSentHealth = -1.0E8F;
@@ -1222,7 +1222,7 @@
}
@Override
@@ -1654,18 +_,18 @@
@@ -1653,18 +_,18 @@
this.onUpdateAbilities();
this.getAttributes().assignBaseValues(oldPlayer.getAttributes());
if (restoreAll) {
@@ -1244,7 +1244,7 @@
if (this.level().getGameRules().get(GameRules.KEEP_INVENTORY) || oldPlayer.isSpectator()) {
this.transferInventoryXpAndScore(oldPlayer);
}
@@ -1677,7 +_,7 @@
@@ -1676,7 +_,7 @@
this.lastSentExp = -1;
this.lastSentHealth = -1.0F;
this.lastSentFood = -1;
@@ -1253,7 +1253,7 @@
this.seenCredits = oldPlayer.seenCredits;
this.enteredNetherPosition = oldPlayer.enteredNetherPosition;
this.currentExplosionImpactPos = oldPlayer.currentExplosionImpactPos;
@@ -1731,9 +_,22 @@
@@ -1730,9 +_,22 @@
CriteriaTriggers.EFFECTS_CHANGED.trigger(this, null);
}
@@ -1277,7 +1277,7 @@
}
@Override
@@ -1751,6 +_,7 @@
@@ -1750,6 +_,7 @@
final float newYRot,
final float newXRot,
final boolean resetCamera
@@ -1285,7 +1285,7 @@
) {
if (this.isSleeping()) {
this.stopSleepInBed(true, true);
@@ -1760,7 +_,7 @@
@@ -1759,7 +_,7 @@
this.setCamera(this);
}
@@ -1294,7 +1294,7 @@
if (success) {
this.setYHeadRot(relatives.contains(Relative.Y_ROT) ? this.getYHeadRot() + newYRot : newYRot);
this.connection.resetFlyingTicks();
@@ -1799,11 +_,16 @@
@@ -1798,11 +_,16 @@
}
public boolean setGameMode(final GameType mode) {
@@ -1315,7 +1315,7 @@
this.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.CHANGE_GAME_MODE, mode.getId()));
if (mode == GameType.SPECTATOR) {
this.removeEntitiesOnShoulder();
@@ -1819,7 +_,7 @@
@@ -1818,7 +_,7 @@
this.onUpdateAbilities();
this.updateEffectVisibility();
@@ -1324,7 +1324,7 @@
}
@Override
@@ -1877,8 +_,13 @@
@@ -1876,8 +_,13 @@
}
public void sendChatMessage(final OutgoingChatMessage message, final boolean filtered, final ChatType.Bound chatType) {
@@ -1339,7 +1339,7 @@
}
}
@@ -1889,7 +_,42 @@
@@ -1888,7 +_,42 @@
}
public void updateOptions(final ClientInformation information) {
@@ -1382,7 +1382,7 @@
this.requestedViewDistance = information.viewDistance();
this.chatVisibility = information.chatVisibility();
this.canChatColor = information.chatColors();
@@ -1974,8 +_,23 @@
@@ -1978,8 +_,23 @@
Entity oldCamera = this.getCamera();
this.camera = newCamera == null ? this : newCamera;
if (oldCamera != this.camera) {
@@ -1407,7 +1407,7 @@
}
if (newCamera != null) {
@@ -1999,11 +_,11 @@
@@ -2003,11 +_,11 @@
}
public @Nullable Component getTabListDisplayName() {
@@ -1421,7 +1421,7 @@
}
public boolean isChangingDimension() {
@@ -2027,11 +_,60 @@
@@ -2031,11 +_,60 @@
}
public void setRespawnPosition(final ServerPlayer.@Nullable RespawnConfig respawnConfig, final boolean showMessage) {
@@ -1484,7 +1484,7 @@
}
public SectionPos getLastSectionPos() {
@@ -2051,16 +_,23 @@
@@ -2055,16 +_,23 @@
}
@Override
@@ -1513,7 +1513,7 @@
return entity;
}
@@ -2113,7 +_,7 @@
@@ -2117,7 +_,7 @@
super.updateUsingItem(useItem);
}
@@ -1522,7 +1522,7 @@
Inventory inventory = this.getInventory();
ItemStack removed = inventory.removeFromSelected(all);
this.containerMenu
@@ -2123,8 +_,9 @@
@@ -2127,8 +_,9 @@
this.stopUsingItem();
}
@@ -1533,7 +1533,7 @@
}
@Override
@@ -2186,9 +_,9 @@
@@ -2190,9 +_,9 @@
}
@Override
@@ -1545,7 +1545,7 @@
if (oldVehicle instanceof LivingEntity livingEntity) {
for (MobEffectInstance effect : livingEntity.getActiveEffects()) {
this.connection.send(new ClientboundRemoveMobEffectPacket(oldVehicle.getId(), effect.getEffect()));
@@ -2310,7 +_,7 @@
@@ -2313,7 +_,7 @@
}
public static long placeEnderPearlTicket(final ServerLevel level, final ChunkPos chunk) {
@@ -1554,7 +1554,7 @@
return TicketType.ENDER_PEARL.timeout();
}
@@ -2347,9 +_,11 @@
@@ -2350,9 +_,11 @@
}
}
@@ -1569,7 +1569,7 @@
}
private static float calculateLookAtYaw(final Vec3 position, final BlockPos lookAtBlockPos) {
@@ -2369,4 +_,135 @@
@@ -2372,4 +_,135 @@
);
public static final ServerPlayer.SavedPosition EMPTY = new ServerPlayer.SavedPosition(Optional.empty(), Optional.empty(), Optional.empty());
}
@@ -1641,7 +1641,7 @@
+ this.pluginRainPosition -= 0.01F;
+ }
+
+ this.pluginRainPosition = Mth.clamp(this.pluginRainPosition, 0.0F, 1.0F);
+ this.pluginRainPosition = Math.clamp(this.pluginRainPosition, 0.0F, 1.0F);
+ }
+
+ public void resetPlayerWeather() {
@@ -91,7 +91,7 @@
if (blockState.isAir()) {
this.level.destroyBlockProgress(this.player.getId(), this.destroyPos, -1);
this.lastSentState = -1;
@@ -165,6 +_,7 @@
@@ -167,6 +_,7 @@
final BlockPos pos, final ServerboundPlayerActionPacket.Action action, final Direction direction, final int maxY, final int sequence
) {
if (!this.player.isWithinBlockInteractionRange(pos, 1.0)) {
@@ -99,7 +99,7 @@
if (this.isDestroyingBlock && action == ServerboundPlayerActionPacket.Action.ABORT_DESTROY_BLOCK) {
this.abortDestroyBlock(pos, sequence);
}
@@ -182,16 +_,35 @@
@@ -184,16 +_,35 @@
}
if (!this.level.mayInteract(this.player, pos)) {
@@ -137,7 +137,7 @@
if (this.player.blockActionRestricted(this.level, pos, this.gameModeForPlayer)) {
this.player.connection.send(new ClientboundBlockUpdatePacket(pos, this.level.getBlockState(pos)));
this.debugLogging(pos, false, sequence, "block action restricted");
@@ -201,7 +_,7 @@
@@ -203,7 +_,7 @@
this.destroyProgressStart = this.gameTicks;
float progress = 1.0F;
BlockState blockState = this.level.getBlockState(pos);
@@ -146,7 +146,7 @@
EnchantmentHelper.onHitBlock(
this.level,
this.player.getMainHandItem(),
@@ -216,6 +_,23 @@
@@ -218,6 +_,23 @@
progress = blockState.getDestroyProgress(this.player, this.player.level(), pos);
}
@@ -170,7 +170,7 @@
if (!blockState.isAir() && progress >= 1.0F) {
this.destroyAndAck(pos, sequence, "insta mine");
} else {
@@ -265,14 +_,22 @@
@@ -267,14 +_,22 @@
private void abortDestroyBlock(final BlockPos pos, final int sequence) {
this.isDestroyingBlock = false;
@@ -195,7 +195,7 @@
}
public void destroyAndAck(final BlockPos pos, final int sequence, final String exitId) {
@@ -286,21 +_,70 @@
@@ -288,21 +_,70 @@
public boolean destroyBlock(final BlockPos pos) {
BlockState state = this.level.getBlockState(pos);
@@ -270,7 +270,7 @@
BlockState adjustedState = block.playerWillDestroy(this.level, pos, state, this.player);
boolean changed = this.level.removeBlock(pos, false);
if (SharedConstants.DEBUG_BLOCK_BREAK) {
@@ -311,17 +_,38 @@
@@ -313,17 +_,38 @@
block.destroy(this.level, pos, adjustedState);
}
@@ -316,7 +316,7 @@
return true;
}
@@ -337,6 +_,7 @@
@@ -339,6 +_,7 @@
int oldCount = itemStack.getCount();
int oldDamage = itemStack.getDamageValue();
@@ -324,7 +324,7 @@
InteractionResult result = itemStack.use(level, player, hand);
ItemStack resultStack;
if (result instanceof InteractionResult.Success success) {
@@ -365,25 +_,70 @@
@@ -367,25 +_,70 @@
}
if (!player.isUsingItem()) {
@@ -398,7 +398,7 @@
return InteractionResult.CONSUME;
}
@@ -424,7 +_,7 @@
@@ -426,7 +_,7 @@
}
}
@@ -407,7 +407,7 @@
UseOnContext context = new UseOnContext(player, hand, hitResult);
InteractionResult result;
if (player.hasInfiniteMaterials()) {
@@ -447,6 +_,11 @@
@@ -449,6 +_,11 @@
return result;
} else {
@@ -1,6 +1,6 @@
--- a/net/minecraft/server/level/WorldGenRegion.java
+++ b/net/minecraft/server/level/WorldGenRegion.java
@@ -164,6 +_,28 @@
@@ -158,6 +_,28 @@
return distance < this.generatingStep.directDependencies().size();
}
@@ -29,7 +29,7 @@
@Override
public BlockState getBlockState(final BlockPos pos) {
int chunkX = SectionPos.blockToSectionCoord(pos.getX());
@@ -295,11 +_,14 @@
@@ -284,11 +_,14 @@
return Math.abs(this.centerChunkX - chunkX) <= this.writeRadius && Math.abs(this.centerChunkZ - chunkZ) <= this.writeRadius;
}
@@ -41,10 +41,10 @@
int chunkZ = SectionPos.blockToSectionCoord(pos.getZ());
+ // Paper start - Buffer OOB setBlock calls
+ if (!hasSetFarWarned) {
Util.logAndPauseIfInIde(
DevUtil.logAndPauseIfInIde(
"Detected setBlock in a far chunk ["
+ chunkX
@@ -311,6 +_,12 @@
@@ -300,6 +_,12 @@
+ this.generatingStep.targetStatus()
+ (this.currentlyGenerating == null ? "" : ", currently generating: " + this.currentlyGenerating.get())
);
@@ -57,7 +57,7 @@
return false;
} else if (this.center.isUpgrading()) {
LevelHeightAccessor levelHeightAccessor = this.center.getHeightAccessorForGeneration();
@@ -328,7 +_,7 @@
@@ -317,7 +_,7 @@
ChunkAccess chunk = this.getChunk(pos);
BlockState oldState = chunk.setBlockState(pos, blockState, updateFlags);
@@ -66,7 +66,7 @@
this.level.updatePOIOnBlockStateChange(pos, oldState, blockState);
}
@@ -341,6 +_,17 @@
@@ -330,6 +_,17 @@
chunk.removeBlockEntity(pos);
}
} else {
@@ -84,7 +84,7 @@
CompoundTag tag = new CompoundTag();
tag.putInt("x", pos.getX());
tag.putInt("y", pos.getY());
@@ -368,6 +_,13 @@
@@ -357,6 +_,13 @@
@Override
public boolean addFreshEntity(final Entity entity) {
@@ -4,10 +4,10 @@
import com.mojang.authlib.GameProfile;
import net.minecraft.server.level.ClientInformation;
-public record CommonListenerCookie(GameProfile gameProfile, int latency, ClientInformation clientInformation, boolean transferred) {
+public record CommonListenerCookie(GameProfile gameProfile, int latency, ClientInformation clientInformation, boolean transferred, @org.jspecify.annotations.Nullable String clientBrand, java.util.Set<String> channels) { // Paper
-public record CommonListenerCookie(GameProfile gameProfile, int latency, ClientInformation clientInformation, boolean transferred, ClientDebugInfo debugInfo) {
+public record CommonListenerCookie(GameProfile gameProfile, int latency, ClientInformation clientInformation, boolean transferred, ClientDebugInfo debugInfo, @org.jspecify.annotations.Nullable String clientBrand, java.util.Set<String> channels) { // Paper
public static CommonListenerCookie createInitial(final GameProfile gameProfile, final boolean transferred) {
- return new CommonListenerCookie(gameProfile, 0, ClientInformation.createDefault(), transferred);
+ return new CommonListenerCookie(gameProfile, 0, ClientInformation.createDefault(), transferred, null, new java.util.HashSet<>()); // Paper
- return new CommonListenerCookie(gameProfile, 0, ClientInformation.createDefault(), transferred, new ClientDebugInfo());
+ return new CommonListenerCookie(gameProfile, 0, ClientInformation.createDefault(), transferred, new ClientDebugInfo(), null, new java.util.HashSet<>()); // Paper
}
}
@@ -1,14 +1,14 @@
--- a/net/minecraft/server/network/LegacyQueryHandler.java
+++ b/net/minecraft/server/network/LegacyQueryHandler.java
@@ -14,6 +_,7 @@
public class LegacyQueryHandler extends ChannelInboundHandlerAdapter {
@@ -20,6 +_,7 @@
private static final Logger LOGGER = LogUtils.getLogger();
private final ServerInfo server;
private final Connection connection;
+ private ByteBuf buf; // Paper
public LegacyQueryHandler(final ServerInfo server) {
public LegacyQueryHandler(final ServerInfo server, final Connection connection) {
this.server = server;
@@ -22,6 +_,17 @@
@@ -29,6 +_,17 @@
@Override
public void channelRead(final ChannelHandlerContext ctx, final Object msg) {
ByteBuf in = (ByteBuf)msg;
@@ -26,15 +26,27 @@
in.markReaderIndex();
boolean connectNormally = true;
@@ -33,9 +_,19 @@
@@ -40,14 +_,27 @@
SocketAddress socket = ctx.channel().remoteAddress();
int length = in.readableBytes();
+ String body = null; // Paper
if (length == 0) {
- LOGGER.debug("Ping: (<1.3.x) from {}", socket);
- String body = createVersion0Response(this.server);
- if (this.server.acceptsConnection(this.connection, UNKNOWN_SERVER_DETAILS)) {
- String body = createVersion0Response(this.server);
- sendFlushAndClose(ctx, createLegacyDisconnectPacket(ctx.alloc(), body));
- } else {
- ctx.close();
- }
+ LOGGER.debug("Ping: (<1.3.x) from {}", net.minecraft.server.MinecraftServer.getServer().logIPs() ? socket : "<ip address withheld>"); // Paper - Respect logIPs option
+ if (!this.server.acceptsConnection(this.connection, UNKNOWN_SERVER_DETAILS)) {
+ ctx.close();
+ in.release();
+ connectNormally = false;
+ return;
+ }
+
+ // Paper start - Call PaperServerListPingEvent and use results
+ com.destroystokyo.paper.event.server.PaperServerListPingEvent event = com.destroystokyo.paper.network.PaperLegacyStatusClient.processRequest(net.minecraft.server.MinecraftServer.getServer(), socket, 39, null);
+ if (event == null) {
@@ -45,35 +57,52 @@
+ }
+ body = String.format(Locale.ROOT, "%s§%d§%d", com.destroystokyo.paper.network.PaperLegacyStatusClient.getUnformattedMotd(event), event.getNumPlayers(), event.getMaxPlayers());
+ // Paper end - Call PaperServerListPingEvent and use results
sendFlushAndClose(ctx, createLegacyDisconnectPacket(ctx.alloc(), body));
+ sendFlushAndClose(ctx, createLegacyDisconnectPacket(ctx.alloc(), body));
} else {
if (in.readUnsignedByte() != 1) {
@@ -43,16 +_,39 @@
}
return;
@@ -55,23 +_,47 @@
ServerConnectionDetails connectionDetails;
if (in.isReadable()) {
- if (!readCustomPayloadPacket(in)) {
- connectionDetails = readCustomPayloadPacket(in);
- if (connectionDetails == null) {
- return;
- }
-
- LOGGER.debug("Ping: (1.6) from {}, host: {}", socket, connectionDetails);
- } else {
- LOGGER.debug("Ping: (1.4-1.5.x) from {}", socket);
- connectionDetails = UNKNOWN_SERVER_DETAILS;
- }
-
- if (this.server.acceptsConnection(this.connection, connectionDetails)) {
- String body = createVersion1Response(this.server);
- sendFlushAndClose(ctx, createLegacyDisconnectPacket(ctx.alloc(), body));
- } else {
- ctx.close();
- }
+ // Paper start - Replace below
+ if (in.readUnsignedByte() != LegacyProtocolUtils.CUSTOM_PAYLOAD_PACKET_ID) {
+ body = this.readLegacy1_6(ctx, in);
+ if (body == null) {
+ return;
+ }
}
-
- LOGGER.debug("Ping: (1.6) from {}", socket);
+ }
+ // Paper end - Replace below
} else {
- LOGGER.debug("Ping: (1.4-1.5.x) from {}", socket);
- }
-
- String body = createVersion1Response(this.server);
+ } else {
+ LOGGER.debug("Ping: (1.4-1.5.x) from {}", net.minecraft.server.MinecraftServer.getServer().logIPs() ? socket : "<ip address withheld>"); // Paper - Respect logIPs option
+ }
+
+ // Paper start - Call PaperServerListPingEvent and use results
+ if (body == null) {
+ if (!this.server.acceptsConnection(this.connection, UNKNOWN_SERVER_DETAILS)) {
+ ctx.close();
+ in.release();
+ connectNormally = false;
+ return;
+ }
+
+ com.destroystokyo.paper.event.server.PaperServerListPingEvent event = com.destroystokyo.paper.network.PaperLegacyStatusClient.processRequest(net.minecraft.server.MinecraftServer.getServer(), socket, 127, null); // Paper
+ if (event == null) {
+ ctx.close();
@@ -93,10 +122,11 @@
+ );
+ }
+ // Paper end - Call PaperServerListPingEvent and use results
sendFlushAndClose(ctx, createLegacyDisconnectPacket(ctx.alloc(), body));
+ sendFlushAndClose(ctx, createLegacyDisconnectPacket(ctx.alloc(), body));
}
@@ -110,6 +_,96 @@
in.release();
@@ -128,6 +_,101 @@
server.getMaxPlayers()
);
}
@@ -161,6 +191,11 @@
+
+ LOGGER.debug("Ping: (1.6) from {}", net.minecraft.server.MinecraftServer.getServer().logIPs() ? ctx.channel().remoteAddress() : "<ip address withheld>"); // Paper - Respect logIPs option
+
+ if (!this.server.acceptsConnection(this.connection, ServerConnectionDetails.fromIntentPacket(host, port))) {
+ ctx.close();
+ return null;
+ }
+
+ net.minecraft.server.MinecraftServer server = net.minecraft.server.MinecraftServer.getServer();
+ java.net.InetSocketAddress virtualHost = com.destroystokyo.paper.network.PaperNetworkClient.prepareVirtualHost(host, port);
+ com.destroystokyo.paper.event.server.PaperServerListPingEvent event = com.destroystokyo.paper.network.PaperLegacyStatusClient.processRequest(server, ctx.channel().remoteAddress(), protocolVersion, virtualHost);
@@ -1,6 +1,6 @@
--- a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
@@ -37,7 +_,7 @@
@@ -42,7 +_,7 @@
private static final Component TIMEOUT_DISCONNECTION_MESSAGE = Component.translatable("disconnect.timeout");
static final Component DISCONNECT_UNEXPECTED_QUERY = Component.translatable("multiplayer.disconnect.unexpected_query_response");
protected final MinecraftServer server;
@@ -9,10 +9,10 @@
private final boolean transferred;
private long keepAliveTime;
private boolean keepAlivePending;
@@ -46,6 +_,16 @@
private boolean closed = false;
@@ -52,6 +_,16 @@
private int latency;
private volatile boolean suspendFlushingOnServerThread = false;
private final ClientDebugInfo debugInfo;
+ // CraftBukkit start
+ public final org.bukkit.craftbukkit.CraftServer cserver;
+ // CraftBukkit end
@@ -26,10 +26,10 @@
public ServerCommonPacketListenerImpl(final MinecraftServer server, final Connection connection, final CommonListenerCookie cookie) {
this.server = server;
@@ -53,7 +_,18 @@
this.keepAliveTime = Util.getMillis();
@@ -60,7 +_,18 @@
this.latency = cookie.latency();
this.transferred = cookie.transferred();
this.debugInfo = cookie.debugInfo();
+ // Paper start
+ this.clientBrand = cookie.clientBrand();
+ this.cserver = server.server;
@@ -45,7 +45,7 @@
private void close() {
if (!this.closed) {
@@ -83,7 +_,7 @@
@@ -91,7 +_,7 @@
this.latency = (this.latency * 3 + time) / 4;
this.keepAlivePending = false;
} else if (!this.isSingleplayerOwner()) {
@@ -54,7 +54,7 @@
}
}
@@ -91,14 +_,88 @@
@@ -99,24 +_,88 @@
public void handlePong(final ServerboundPongPacket serverboundPongPacket) {
}
@@ -65,6 +65,16 @@
+
@Override
public void handleCustomPayload(final ServerboundCustomPayloadPacket packet) {
- PacketUtils.ensureRunningOnSameThread(packet, this, this.server.packetProcessor());
- switch (packet.payload()) {
- case BrandPayload(String brand):
- this.debugInfo.setBrand(brand);
- break;
- case ModListPayload(Map<Identifier, PropertyMap> entries):
- this.debugInfo.appendModInfo(entries);
- break;
- default:
- }
+ // Paper start
+ if (!(packet.payload() instanceof final net.minecraft.network.protocol.common.custom.DiscardedPayload discardedPayload)) {
+ return;
@@ -143,7 +153,7 @@
}
@Override
@@ -106,21 +_,46 @@
@@ -124,21 +_,46 @@
PacketUtils.ensureRunningOnSameThread(packet, this, this.server.packetProcessor());
if (packet.action() == ServerboundResourcePackPacket.Action.DECLINED && this.server.isResourcePackRequired()) {
LOGGER.info("Disconnecting {} due to resource pack {} rejection", this.playerProfile().name(), packet.id());
@@ -195,7 +205,7 @@
} else if (this.checkIfClosed(now)) {
this.keepAlivePending = true;
this.keepAliveTime = now;
@@ -135,7 +_,7 @@
@@ -153,7 +_,7 @@
private boolean checkIfClosed(final long now) {
if (this.closed) {
if (now - this.closedListenerTime >= 15000L) {
@@ -204,7 +214,7 @@
}
return false;
@@ -158,6 +_,13 @@
@@ -176,6 +_,13 @@
}
public void send(final Packet<?> packet, final @Nullable ChannelFutureListener listener) {
@@ -218,7 +228,7 @@
if (packet.isTerminal()) {
this.close();
}
@@ -174,15 +_,108 @@
@@ -192,14 +_,108 @@
}
}
@@ -304,7 +314,6 @@
+ private void disconnect0(final DisconnectionDetails details) {
this.connection.send(new ClientboundDisconnectPacket(details.reason()), PacketSendListener.thenRun(() -> this.connection.disconnect(details)));
this.connection.setReadOnly();
- this.server.executeBlocking(this.connection::handleDisconnection);
- }
+ this.connection.handleConnectionDisconnectOnNextTick = true; // Paper - Force kill connection ticking. Let this close the connection
+ }
@@ -330,11 +339,11 @@
protected boolean isSingleplayerOwner() {
return this.server.isSingleplayerOwner(new NameAndId(this.playerProfile()));
@@ -200,6 +_,6 @@
@@ -221,6 +_,6 @@
}
protected CommonListenerCookie createCookie(final ClientInformation clientInformation) {
- return new CommonListenerCookie(this.playerProfile(), this.latency, clientInformation, this.transferred);
+ return new CommonListenerCookie(this.playerProfile(), this.latency, clientInformation, this.transferred, this.clientBrand, this.pluginMessagerChannels); // Paper
- return new CommonListenerCookie(this.playerProfile(), this.latency, clientInformation, this.transferred, this.debugInfo);
+ return new CommonListenerCookie(this.playerProfile(), this.latency, clientInformation, this.transferred, this.debugInfo, this.clientBrand, this.pluginMessagerChannels); // Paper
}
}
@@ -26,10 +26,10 @@
.childHandler(
new ChannelInitializer<Channel>() {
@Override
@@ -82,22 +_,63 @@
Connection connection = rateLimitPacketsPerSecond > 0
? new RateKickingConnection(rateLimitPacketsPerSecond)
: new Connection(PacketFlow.SERVERBOUND);
@@ -84,22 +_,63 @@
}
Connection.configureSerialization(pipeline, PacketFlow.SERVERBOUND, false, null);
+ // Paper start - Add support for Proxy Protocol
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.proxyProtocol) {
+ channel.pipeline().addAfter(HandlerNames.TIMEOUT, "haproxy-decoder", new io.netty.handler.codec.haproxy.HAProxyMessageDecoder());
@@ -91,7 +91,7 @@
public SocketAddress startMemoryChannel() {
ChannelFuture newChannel;
synchronized (this.channels) {
@@ -138,9 +_,26 @@
@@ -140,9 +_,26 @@
public void stop() {
this.running = false;
@@ -119,7 +119,7 @@
} catch (InterruptedException ignored) {
LOGGER.error("Interrupted whilst closing channel");
}
@@ -169,12 +_,25 @@
@@ -171,12 +_,25 @@
public void tick() {
synchronized (this.connections) {
this.addPendingConnections();
@@ -145,7 +145,7 @@
try {
connection.tick();
} catch (Exception e) {
@@ -188,6 +_,7 @@
@@ -190,6 +_,7 @@
connection.setReadOnly();
}
} else {
@@ -153,7 +153,7 @@
iterator.remove();
connection.handleDisconnection();
}
@@ -201,6 +_,28 @@
@@ -203,6 +_,28 @@
}
}
}
@@ -656,7 +656,7 @@
+ // CraftBukkit end
int slot = packet.slot();
if (Inventory.isHotbarSlot(slot) || slot == 40) {
List<String> contents = Lists.newArrayList();
List<String> contents = new ArrayList<>();
@@ -1012,10 +_,14 @@
}
@@ -735,7 +735,7 @@
if (this.player.isSleeping()) {
if (movedDist > 1.0) {
this.teleport(this.player.getX(), this.player.getY(), this.player.getZ(), targetYRot, targetXRot);
@@ -1149,28 +_,79 @@
@@ -1149,32 +_,89 @@
if (level.tickRateManager().runsNormally()) {
this.receivedMovePacketCount++;
int deltaPackets = this.receivedMovePacketCount - this.knownMovePacketCount;
@@ -821,9 +821,6 @@
}
boolean playerStandsOnSomething = this.player.verticalCollisionBelow;
@@ -1179,6 +_,12 @@
}
this.player.move(MoverType.PLAYER, new Vec3(xDist, yDist, zDist));
+ this.player.onGround = isOnGround; // CraftBukkit - SPIGOT-5810, SPIGOT-5835, SPIGOT-6828: reset by this.player.move
+ // Paper start - prevent position desync
@@ -831,9 +828,9 @@
+ return; // ... thanks Mojang for letting move calls teleport across dimensions.
+ }
+ // Paper end - prevent position desync
double oyDist = yDist;
xDist = targetX - this.player.getX();
yDist = targetY - this.player.getY();
if (isOnGround && !playerStandsOnSomething) {
this.forceSendPlayerSupportBlocks();
}
@@ -1188,21 +_,88 @@
zDist = targetZ - this.player.getZ();
@@ -2529,7 +2526,7 @@
}
@Override
@@ -2250,27 +_,32 @@
@@ -2250,16 +_,19 @@
private void resetPlayerChatState(final RemoteChatSession chatSession) {
this.chatSession = chatSession;
@@ -2550,12 +2547,7 @@
}
);
}
@Override
public void handleCustomPayload(final ServerboundCustomPayloadPacket packet) {
+ super.handleCustomPayload(packet); // Paper
}
@@ -2273,6 +_,7 @@
@Override
public void handleClientTickEnd(final ServerboundClientTickEndPacket packet) {
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level());
@@ -2563,7 +2555,7 @@
if (!this.receivedMovementThisTick) {
this.player.setKnownMovement(Vec3.ZERO);
}
@@ -2308,12 +_,23 @@
@@ -2310,12 +_,23 @@
}
public void tickClientLoadTimeout() {
@@ -2589,7 +2581,7 @@
this.clientLoadedTimeoutTimer = 0;
}
@@ -2325,4 +_,80 @@
@@ -2327,4 +_,80 @@
this.waitingForRespawn = false;
this.clientLoadedTimeoutTimer = 60;
}
@@ -1,9 +1,9 @@
--- a/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java
@@ -14,9 +_,20 @@
@@ -16,9 +_,20 @@
public class ServerHandshakePacketListenerImpl implements ServerHandshakePacketListener {
private static final Component IGNORE_STATUS_REASON = Component.translatable("disconnect.ignoring_status_request");
private static final Component CONNECTION_REJECTED_REASON = Component.translatable("multiplayer.disconnect.rejected");
+ // Spigot start
+ private static final com.google.gson.Gson gson = new com.google.gson.Gson();
+ static final java.util.regex.Pattern HOST_PATTERN = java.util.regex.Pattern.compile("[0-9a-f\\.:]{0,45}");
@@ -21,17 +21,17 @@
public ServerHandshakePacketListenerImpl(final MinecraftServer server, final Connection connection) {
this.server = server;
this.connection = connection;
@@ -24,6 +_,7 @@
@@ -26,6 +_,7 @@
@Override
public void handleIntention(final ClientIntentionPacket packet) {
+ this.connection.hostname = packet.hostName() + ":" + packet.port(); // CraftBukkit - set hostname
switch (packet.intention()) {
case LOGIN:
this.beginLogin(packet, false);
@@ -50,22 +_,119 @@
default:
throw new UnsupportedOperationException("Invalid intention " + packet.intention());
ServerConnectionDetails details = ServerConnectionDetails.fromIntentPacket(packet);
if (!this.server.acceptsConnection(this.connection, details)) {
this.connection.disconnect(CONNECTION_REJECTED_REASON);
@@ -57,22 +_,119 @@
throw new UnsupportedOperationException("Invalid intention " + packet.intention());
}
}
+
+ // Paper start - NetworkClient implementation
@@ -1,32 +1,36 @@
--- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
@@ -45,9 +_,16 @@
@@ -49,9 +_,16 @@
import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
-public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, TickablePacketListener {
+// CraftBukkit start
+import org.bukkit.craftbukkit.entity.CraftPlayer;
+import org.bukkit.craftbukkit.util.Waitable;
+import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
+import org.bukkit.event.player.PlayerPreLoginEvent;
+
public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, TickablePacketListener {
+public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, TickablePacketListener, net.minecraft.network.PacketProcessor.Handler { // CraftBukkit
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.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 static final String PROTOCOL_ERROR_MESSAGE = "Protocol error";
private final byte[] challenge;
private final MinecraftServer server;
@@ -55,10 +_,14 @@
@@ -60,12 +_,16 @@
private final ServerActivityMonitor serverActivityMonitor;
public volatile ServerLoginPacketListenerImpl.State state = ServerLoginPacketListenerImpl.State.HELLO;
private int tick;
- private @Nullable String requestedUsername;
- private @Nullable GameProfile resolvedUserProfile;
+ public @Nullable String requestedUsername; // Paper - public
public @Nullable GameProfile authenticatedProfile;
+ public @Nullable GameProfile resolvedUserProfile; // Paper - public
private final String serverId = "";
- private final boolean transferred;
+ public final boolean transferred; // Paper
+ public final boolean transferred; // Paper - public
private final OptionalInt requestedCompressionThreshold;
private @Nullable SecretKey pendingSecretKey;
+ public boolean iKnowThisMayNotBeTheBestIdeaButPleaseDisableUsernameValidation = false; // Paper - username validation overriding
+ private int velocityLoginMessageId = -1; // Paper - Add Velocity IP Forwarding Support
+ public java.util.@Nullable UUID requestedUuid; // Paper
@@ -34,10 +38,10 @@
public ServerLoginPacketListenerImpl(final MinecraftServer minecraftserver, final Connection connection, final boolean transferred) {
this.server = minecraftserver;
@@ -66,10 +_,19 @@
this.serverActivityMonitor = this.server.getServerActivityMonitor();
this.challenge = Ints.toByteArray(RandomSource.create().nextInt());
this.transferred = transferred;
@@ -77,10 +_,19 @@
this.requestedCompressionThreshold = !connection.isMemoryConnection() && compressionThreshold >= 0
? OptionalInt.of(compressionThreshold)
: OptionalInt.empty();
+ this.paperLoginConnection = new io.papermc.paper.connection.PaperPlayerLoginConnection(this); // Paper
}
@@ -52,10 +56,10 @@
+ // Paper end - login cookie API
+
if (this.state == ServerLoginPacketListenerImpl.State.VERIFYING) {
this.verifyLoginAndFinishConnectionSetup(Objects.requireNonNull(this.authenticatedProfile));
this.verifyLoginAndFinishConnectionSetup(Objects.requireNonNull(this.resolvedUserProfile));
}
@@ -79,16 +_,39 @@
this.finishLoginAndWaitForClient(this.authenticatedProfile);
@@ -90,16 +_,39 @@
this.finishLoginAndWaitForClient(this.resolvedUserProfile);
}
+ // Paper start - login cookie API
@@ -85,7 +89,7 @@
+ 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.send(new ClientboundLoginDisconnectPacket(reason), net.minecraft.network.PacketSendListener.thenRun(() -> this.connection.disconnect(reason)));
+ this.connection.handleConnectionDisconnectOnNextTick = true;
+ } catch (Exception var3) {
+ LOGGER.error("Error whilst disconnecting player", (Throwable)var3);
@@ -95,7 +99,7 @@
public void disconnect(final Component component) {
try {
LOGGER.info("Disconnecting {}: {}", this.getUserName(), component.getString());
@@ -116,7 +_,14 @@
@@ -127,7 +_,14 @@
@Override
public void handleHello(final ServerboundHelloPacket packet) {
Validate.validState(this.state == ServerLoginPacketListenerImpl.State.HELLO, "Unexpected hello packet");
@@ -111,11 +115,11 @@
this.requestedUsername = packet.name();
GameProfile singleplayerProfile = this.server.getSingleplayerProfile();
if (singleplayerProfile != null && this.requestedUsername.equalsIgnoreCase(singleplayerProfile.name())) {
@@ -126,7 +_,30 @@
@@ -137,7 +_,30 @@
this.state = ServerLoginPacketListenerImpl.State.KEY;
this.connection.send(new ClientboundHelloPacket("", this.server.getKeyPair().getPublic().getEncoded(), this.challenge, true));
this.connection.send(new ClientboundKeyPacket("", this.server.getKeyPair().getPublic().getEncoded(), this.challenge, true));
} else {
- this.startClientVerification(UUIDUtil.createOfflineProfile(this.requestedUsername));
- this.startOfflineClientVerification(UUIDUtil.createOfflineProfile(this.requestedUsername));
+ // Paper start - Add Velocity IP Forwarding Support
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.velocity.enabled) {
+ this.velocityLoginMessageId = java.util.concurrent.ThreadLocalRandom.current().nextInt();
@@ -133,7 +137,7 @@
+
+ gameprofile = ServerLoginPacketListenerImpl.this.callPlayerPreLoginEvents(gameprofile); // Paper - Add more fields to AsyncPlayerPreLoginEvent
+ ServerLoginPacketListenerImpl.LOGGER.info("UUID of player {} is {}", gameprofile.name(), gameprofile.id());
+ ServerLoginPacketListenerImpl.this.startClientVerification(gameprofile);
+ ServerLoginPacketListenerImpl.this.startOfflineClientVerification(gameprofile);
+ } catch (Exception ex) {
+ ServerLoginPacketListenerImpl.this.disconnect("Failed to verify username!");
+ ServerLoginPacketListenerImpl.this.server.server.getLogger().log(java.util.logging.Level.WARNING, "Exception verifying " + ServerLoginPacketListenerImpl.this.requestedUsername, ex);
@@ -143,7 +147,7 @@
}
}
}
@@ -138,19 +_,23 @@
@@ -149,15 +_,15 @@
private void verifyLoginAndFinishConnectionSetup(final GameProfile profile) {
PlayerList playerList = this.server.getPlayerList();
@@ -153,21 +157,8 @@
- this.disconnect(error);
+ this.disconnectAsync(error); // Paper
} else {
if (this.server.getCompressionThreshold() >= 0 && !this.connection.isMemoryConnection()) {
- this.connection
- .send(
- new ClientboundLoginCompressionPacket(this.server.getCompressionThreshold()),
- PacketSendListener.thenRun(() -> this.connection.setupCompression(this.server.getCompressionThreshold(), true))
- );
+ // Paper start - Keep compression packet write and setup in the same Netty task
+ final int compressionThreshold = this.server.getCompressionThreshold();
+ this.connection.sendPacketAndScheduleNettyTask(
+ new ClientboundLoginCompressionPacket(compressionThreshold), true,
+ () -> {
+ this.connection.setupCompression(compressionThreshold, true);
+ }
+ );
+ // Paper end - Keep compression packet write and setup in the same Netty task
if (this.requestedCompressionThreshold.isPresent()) {
this.connection.send(new ClientboundSetupCompressionPacket(this.requestedCompressionThreshold.getAsInt()));
}
- boolean waitForDisconnection = playerList.disconnectAllPlayersWithProfile(profile.id());
@@ -175,15 +166,15 @@
if (waitForDisconnection) {
this.state = ServerLoginPacketListenerImpl.State.WAITING_FOR_DUPE_DISCONNECT;
} else {
@@ -162,6 +_,7 @@
private void finishLoginAndWaitForClient(final GameProfile gameProfile) {
@@ -170,6 +_,7 @@
byte optionsToEnable = this.loginFinishedPacketEnableOptions();
this.state = ServerLoginPacketListenerImpl.State.PROTOCOL_SWITCHING;
this.connection.send(new ClientboundLoginFinishedPacket(gameProfile, this.server.getConnection().getSessionId()));
this.connection.send(new ClientboundLoginFinishedPacket(gameProfile, this.server.getConnection().getSessionId(), optionsToEnable));
+ this.server.services().paper().filledProfileCache().add(gameProfile); // Paper - update profile cache
}
@Override
@@ -185,7 +_,8 @@
private @ConnectionOptionsPacket.Flags byte loginFinishedPacketEnableOptions() {
@@ -196,7 +_,8 @@
throw new IllegalStateException("Protocol error", e);
}
@@ -193,7 +184,7 @@
@Override
public void run() {
String name = Objects.requireNonNull(ServerLoginPacketListenerImpl.this.requestedUsername, "Player name not initialized");
@@ -197,12 +_,18 @@
@@ -208,12 +_,18 @@
.hasJoinedServer(name, digest, this.getAddress());
if (result != null) {
GameProfile profile = result.profile();
@@ -205,20 +196,20 @@
+ // CraftBukkit end
ServerLoginPacketListenerImpl.LOGGER.info("UUID of player {} is {}", profile.name(), profile.id());
ServerLoginPacketListenerImpl.this.serverActivityMonitor.reportLoginActivity();
ServerLoginPacketListenerImpl.this.startClientVerification(profile);
ServerLoginPacketListenerImpl.this.applyConnectionEncryption(profile, secretKey);
} else if (ServerLoginPacketListenerImpl.this.server.isSingleplayer()) {
ServerLoginPacketListenerImpl.LOGGER.warn("Failed to verify username but will let them in anyway!");
- ServerLoginPacketListenerImpl.this.startClientVerification(UUIDUtil.createOfflineProfile(name));
+ ServerLoginPacketListenerImpl.this.startClientVerification(ServerLoginPacketListenerImpl.this.createOfflineProfile(name)); // Spigot
- ServerLoginPacketListenerImpl.this.applyConnectionEncryption(UUIDUtil.createOfflineProfile(name), secretKey);
+ ServerLoginPacketListenerImpl.this.applyConnectionEncryption(ServerLoginPacketListenerImpl.this.createOfflineProfile(name), secretKey); // Spigot
} else {
ServerLoginPacketListenerImpl.this.disconnect(Component.translatable("multiplayer.disconnect.unverified_username"));
ServerLoginPacketListenerImpl.LOGGER.error("Username '{}' tried to join with an invalid session", name);
@@ -210,11 +_,16 @@
@@ -221,11 +_,16 @@
} catch (AuthenticationUnavailableException ignored) {
if (ServerLoginPacketListenerImpl.this.server.isSingleplayer()) {
ServerLoginPacketListenerImpl.LOGGER.warn("Authentication servers are down but will let them in anyway!");
- ServerLoginPacketListenerImpl.this.startClientVerification(UUIDUtil.createOfflineProfile(name));
+ ServerLoginPacketListenerImpl.this.startClientVerification(ServerLoginPacketListenerImpl.this.createOfflineProfile(name)); // Spigot
- ServerLoginPacketListenerImpl.this.applyConnectionEncryption(UUIDUtil.createOfflineProfile(name), secretKey);
+ ServerLoginPacketListenerImpl.this.applyConnectionEncryption(ServerLoginPacketListenerImpl.this.createOfflineProfile(name), secretKey); // Spigot
} else {
- ServerLoginPacketListenerImpl.this.disconnect(Component.translatable("multiplayer.disconnect.authservers_down"));
+ ServerLoginPacketListenerImpl.this.disconnect(io.papermc.paper.adventure.PaperAdventure.asVanilla(io.papermc.paper.configuration.GlobalConfiguration.get().messages.kick.authenticationServersDown)); // Paper - Configurable kick message
@@ -232,7 +223,7 @@
}
}
@@ -224,18 +_,123 @@
@@ -235,10 +_,60 @@
? inetSocketAddress.getAddress()
: null;
}
@@ -295,6 +286,10 @@
+ }
+ // CraftBukkit end
private void applyConnectionEncryption(final GameProfile authenticatedGameProfile, final SecretKey secretKey) {
this.resolvedUserProfile = authenticatedGameProfile;
@@ -279,12 +_,71 @@
@Override
public void handleCustomQueryPacket(final ServerboundCustomQueryAnswerPacket packet) {
+ // Paper start - Add Velocity IP Forwarding Support
@@ -333,19 +328,19 @@
+ }
+ this.connection.address = new java.net.InetSocketAddress(com.destroystokyo.paper.proxy.VelocityProxy.readAddress(buf), port);
+
+ this.authenticatedProfile = com.destroystokyo.paper.proxy.VelocityProxy.createProfile(buf);
+ this.resolvedUserProfile = com.destroystokyo.paper.proxy.VelocityProxy.createProfile(buf);
+
+ //TODO Update handling for lazy sessions, might not even have to do anything?
+
+ // Proceed with login
+ authenticatorPool.execute(() -> {
+ try {
+ final GameProfile gameprofile = this.callPlayerPreLoginEvents(this.authenticatedProfile);
+ final GameProfile gameprofile = this.callPlayerPreLoginEvents(this.resolvedUserProfile);
+ ServerLoginPacketListenerImpl.LOGGER.info("UUID of player {} is {}", gameprofile.name(), gameprofile.id());
+ ServerLoginPacketListenerImpl.this.startClientVerification(gameprofile);
+ ServerLoginPacketListenerImpl.this.startOfflineClientVerification(gameprofile);
+ } catch (Exception ex) {
+ disconnect("Failed to verify username!");
+ server.server.getLogger().log(java.util.logging.Level.WARNING, "Exception verifying " + this.authenticatedProfile.name(), ex);
+ server.server.getLogger().log(java.util.logging.Level.WARNING, "Exception verifying " + this.resolvedUserProfile.name(), ex);
+ }
+ });
+ return;
@@ -356,11 +351,32 @@
@Override
public void handleLoginAcknowledgement(final ServerboundLoginAcknowledgedPacket packet) {
- this.applyConnectionOptions(this.loginFinishedPacketEnableOptions());
+ // Paper start - apply connection options on the network thread before rescheduling
+ if (!this.server.packetProcessor().isSameThread()) {
+ this.applyConnectionOptions(this.loginFinishedPacketEnableOptions());
+ }
+ // Paper end - apply connection options on the network thread before rescheduling
+ net.minecraft.network.protocol.PacketUtils.ensureRunningOnSameThread(packet, this, this.server.packetProcessor()); // CraftBukkit
Validate.validState(this.state == ServerLoginPacketListenerImpl.State.PROTOCOL_SWITCHING, "Unexpected login acknowledgement packet");
this.connection.setupOutboundProtocol(ConfigurationProtocols.CLIENTBOUND);
CommonListenerCookie cookie = CommonListenerCookie.createInitial(Objects.requireNonNull(this.authenticatedProfile), this.transferred);
@@ -252,8 +_,31 @@
CommonListenerCookie cookie = CommonListenerCookie.createInitial(Objects.requireNonNull(this.resolvedUserProfile), this.transferred);
@@ -294,6 +_,14 @@
this.state = ServerLoginPacketListenerImpl.State.ACCEPTED;
}
+ // CraftBukkit start
+ @Override
+ public void onPacketError(final net.minecraft.network.protocol.Packet<?> packet, final Exception e) {
+ LOGGER.error("Failed to handle packet {}, suppressing error", packet, e);
+ this.disconnect(Connection.PACKET_HANDLER_ERROR_DISCONNECT_REASON);
+ }
+ // CraftBukkit end
+
@Override
public void fillListenerSpecificCrashDetails(final CrashReport report, final CrashReportCategory connectionDetails) {
connectionDetails.setDetail("Login phase", () -> this.state.toString());
@@ -301,8 +_,31 @@
@Override
public void handleCookieResponse(final ServerboundCookieResponsePacket packet) {
@@ -1,6 +1,6 @@
--- a/net/minecraft/server/players/CachedUserNameToIdResolver.java
+++ b/net/minecraft/server/players/CachedUserNameToIdResolver.java
@@ -46,6 +_,10 @@
@@ -47,6 +_,10 @@
private final Gson gson = new GsonBuilder().create();
private final File file;
private final AtomicLong operationCount = new AtomicLong();
@@ -11,7 +11,7 @@
public CachedUserNameToIdResolver(final GameProfileRepository profileRepository, final File file) {
this.profileRepository = profileRepository;
@@ -54,10 +_,12 @@
@@ -55,10 +_,12 @@
}
private void safeAdd(final CachedUserNameToIdResolver.GameProfileInfo profileInfo) {
@@ -24,7 +24,7 @@
}
private Optional<NameAndId> lookupGameProfile(final GameProfileRepository profileRepository, final String name) {
@@ -65,12 +_,14 @@
@@ -66,12 +_,14 @@
return this.createUnknownProfile(name);
}
@@ -41,7 +41,7 @@
}
@Override
@@ -90,7 +_,7 @@
@@ -91,7 +_,7 @@
Date expirationDate = c.getTime();
CachedUserNameToIdResolver.GameProfileInfo profileInfo = new CachedUserNameToIdResolver.GameProfileInfo(profile, expirationDate);
this.safeAdd(profileInfo);
@@ -50,7 +50,7 @@
return profileInfo;
}
@@ -98,9 +_,24 @@
@@ -99,9 +_,24 @@
return this.operationCount.incrementAndGet();
}
@@ -75,7 +75,7 @@
CachedUserNameToIdResolver.GameProfileInfo profileInfo = this.profilesByName.get(userName);
boolean needsSave = false;
if (profileInfo != null && new Date().getTime() >= profileInfo.expirationDate.getTime()) {
@@ -114,8 +_,13 @@
@@ -115,8 +_,13 @@
if (profileInfo != null) {
profileInfo.setLastAccess(this.getNextOperation());
result = Optional.of(profileInfo.nameAndId());
@@ -90,7 +90,7 @@
if (profile.isPresent()) {
result = Optional.of(this.addInternal(profile.get()).nameAndId());
needsSave = false;
@@ -124,15 +_,17 @@
@@ -125,15 +_,17 @@
}
}
@@ -110,7 +110,7 @@
CachedUserNameToIdResolver.GameProfileInfo profileInfo = this.profilesByUUID.get(id);
if (profileInfo == null) {
return Optional.empty();
@@ -140,6 +_,7 @@
@@ -141,6 +_,7 @@
profileInfo.setLastAccess(this.getNextOperation());
return Optional.of(profileInfo.nameAndId());
@@ -118,7 +118,7 @@
}
private static DateFormat createDateFormat() {
@@ -158,6 +_,11 @@
@@ -159,6 +_,11 @@
DateFormat dateFormat = createDateFormat();
entryList.forEach(element -> readGameProfile(element, dateFormat).ifPresent(result::add));
} catch (FileNotFoundException var7) {
@@ -130,7 +130,7 @@
} catch (IOException | JsonParseException e) {
LOGGER.warn("Failed to load profile cache {}", this.file, e);
}
@@ -167,23 +_,51 @@
@@ -168,23 +_,51 @@
@Override
public void save() {
Loaded 100 of 322 files, more files were not shown because too many files have changed in this diff. Show more