mirror of
https://github.com/PaperMC/Paper.git
synced 2026-11-04 17:00:09 -05:00
Compare commits
52
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6409d5ce9e | ||
|
|
2de25adfc9 | ||
|
|
182b2f8a2a | ||
|
|
a8c05a35c2 | ||
|
|
945aea9961 | ||
|
|
4aef3f0a67 | ||
|
|
60073714ba | ||
|
|
4f184db3c9 | ||
|
|
57d84f68a3 | ||
|
|
49daadd25d | ||
|
|
b2b1e177cb | ||
|
|
90aee8e007 | ||
|
|
d74f6bc77a | ||
|
|
dd203a2716 | ||
|
|
dcf5577aae | ||
|
|
c39822ae08 | ||
|
|
4af86ff3e0 | ||
|
|
1a8417b12b | ||
|
|
65461225ef | ||
|
|
7b31cc2b6b | ||
|
|
aa6f09b098 | ||
|
|
944ce44a4a | ||
|
|
6fe7eb5aae | ||
|
|
948acae8c2 | ||
|
|
ef4e85d6db | ||
|
|
3611a815e6 | ||
|
|
1d14197790 | ||
|
|
6bac3c95bf | ||
|
|
9b1dd57135 | ||
|
|
4be1a310c2 | ||
|
|
66d3bbed38 | ||
|
|
d0be5e20fd | ||
|
|
dd0810c4af | ||
|
|
04ee8eac9b | ||
|
|
c58cba39fb | ||
|
|
4731020c9d | ||
|
|
dbd8747173 | ||
|
|
d4895653fa | ||
|
|
4397b319a7 | ||
|
|
298a8bfd32 | ||
|
|
bcf5a3fffe | ||
|
|
84a73d8984 | ||
|
|
f46fbbe20b | ||
|
|
ad45c84240 | ||
|
|
e4114b0e0e | ||
|
|
d56b548552 | ||
|
|
8e32dcbfe7 | ||
|
|
6968eb065e | ||
|
|
d55864cc09 | ||
|
|
c77b8418bf | ||
|
|
f4074a073e | ||
|
|
05eb2e4543 |
No files matched your search
@@ -0,0 +1,181 @@
|
||||
package io.papermc.paper.registry.keys;
|
||||
|
||||
import static net.kyori.adventure.key.Key.key;
|
||||
|
||||
import io.papermc.paper.annotation.GeneratedClass;
|
||||
import io.papermc.paper.entity.poi.PoiType;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import io.papermc.paper.registry.TypedKey;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* Vanilla keys for {@link RegistryKey#POINT_OF_INTEREST_TYPE}.
|
||||
*
|
||||
* @apiNote The fields provided here are a direct representation of
|
||||
* what is available from the vanilla game source. They may be
|
||||
* changed (including removals) on any Minecraft version
|
||||
* bump, so cross-version compatibility is not provided on the
|
||||
* same level as it is on most of the other API.
|
||||
*/
|
||||
@SuppressWarnings({
|
||||
"unused",
|
||||
"SpellCheckingInspection"
|
||||
})
|
||||
@NullMarked
|
||||
@GeneratedClass
|
||||
public final class PoiTypeKeys {
|
||||
/**
|
||||
* {@code minecraft:armorer}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TypedKey<PoiType> ARMORER = create(key("armorer"));
|
||||
|
||||
/**
|
||||
* {@code minecraft:bee_nest}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TypedKey<PoiType> BEE_NEST = create(key("bee_nest"));
|
||||
|
||||
/**
|
||||
* {@code minecraft:beehive}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TypedKey<PoiType> BEEHIVE = create(key("beehive"));
|
||||
|
||||
/**
|
||||
* {@code minecraft:butcher}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TypedKey<PoiType> BUTCHER = create(key("butcher"));
|
||||
|
||||
/**
|
||||
* {@code minecraft:cartographer}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TypedKey<PoiType> CARTOGRAPHER = create(key("cartographer"));
|
||||
|
||||
/**
|
||||
* {@code minecraft:cleric}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TypedKey<PoiType> CLERIC = create(key("cleric"));
|
||||
|
||||
/**
|
||||
* {@code minecraft:farmer}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TypedKey<PoiType> FARMER = create(key("farmer"));
|
||||
|
||||
/**
|
||||
* {@code minecraft:fisherman}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TypedKey<PoiType> FISHERMAN = create(key("fisherman"));
|
||||
|
||||
/**
|
||||
* {@code minecraft:fletcher}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TypedKey<PoiType> FLETCHER = create(key("fletcher"));
|
||||
|
||||
/**
|
||||
* {@code minecraft:home}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TypedKey<PoiType> HOME = create(key("home"));
|
||||
|
||||
/**
|
||||
* {@code minecraft:leatherworker}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TypedKey<PoiType> LEATHERWORKER = create(key("leatherworker"));
|
||||
|
||||
/**
|
||||
* {@code minecraft:librarian}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TypedKey<PoiType> LIBRARIAN = create(key("librarian"));
|
||||
|
||||
/**
|
||||
* {@code minecraft:lightning_rod}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TypedKey<PoiType> LIGHTNING_ROD = create(key("lightning_rod"));
|
||||
|
||||
/**
|
||||
* {@code minecraft:lodestone}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TypedKey<PoiType> LODESTONE = create(key("lodestone"));
|
||||
|
||||
/**
|
||||
* {@code minecraft:mason}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TypedKey<PoiType> MASON = create(key("mason"));
|
||||
|
||||
/**
|
||||
* {@code minecraft:meeting}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TypedKey<PoiType> MEETING = create(key("meeting"));
|
||||
|
||||
/**
|
||||
* {@code minecraft:nether_portal}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TypedKey<PoiType> NETHER_PORTAL = create(key("nether_portal"));
|
||||
|
||||
/**
|
||||
* {@code minecraft:shepherd}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TypedKey<PoiType> SHEPHERD = create(key("shepherd"));
|
||||
|
||||
/**
|
||||
* {@code minecraft:test_instance}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TypedKey<PoiType> TEST_INSTANCE = create(key("test_instance"));
|
||||
|
||||
/**
|
||||
* {@code minecraft:toolsmith}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TypedKey<PoiType> TOOLSMITH = create(key("toolsmith"));
|
||||
|
||||
/**
|
||||
* {@code minecraft:weaponsmith}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TypedKey<PoiType> WEAPONSMITH = create(key("weaponsmith"));
|
||||
|
||||
private PoiTypeKeys() {
|
||||
}
|
||||
|
||||
private static TypedKey<PoiType> create(final Key key) {
|
||||
return TypedKey.create(RegistryKey.POINT_OF_INTEREST_TYPE, key);
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
package io.papermc.paper.registry.keys;
|
||||
|
||||
import static net.kyori.adventure.key.Key.key;
|
||||
|
||||
import io.papermc.paper.annotation.GeneratedClass;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import io.papermc.paper.registry.TypedKey;
|
||||
import io.papermc.paper.world.Timeline;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* Vanilla keys for {@link RegistryKey#TIMELINE}.
|
||||
*
|
||||
* @apiNote The fields provided here are a direct representation of
|
||||
* what is available from the vanilla game source. They may be
|
||||
* changed (including removals) on any Minecraft version
|
||||
* bump, so cross-version compatibility is not provided on the
|
||||
* same level as it is on most of the other API.
|
||||
*/
|
||||
@SuppressWarnings({
|
||||
"unused",
|
||||
"SpellCheckingInspection"
|
||||
})
|
||||
@NullMarked
|
||||
@GeneratedClass
|
||||
public final class TimelineKeys {
|
||||
/**
|
||||
* {@code minecraft:day}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TypedKey<Timeline> DAY = create(key("day"));
|
||||
|
||||
/**
|
||||
* {@code minecraft:early_game}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TypedKey<Timeline> EARLY_GAME = create(key("early_game"));
|
||||
|
||||
/**
|
||||
* {@code minecraft:moon}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TypedKey<Timeline> MOON = create(key("moon"));
|
||||
|
||||
/**
|
||||
* {@code minecraft:villager_schedule}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TypedKey<Timeline> VILLAGER_SCHEDULE = create(key("villager_schedule"));
|
||||
|
||||
private TimelineKeys() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a typed key for {@link Timeline} in the registry {@code minecraft:timeline}.
|
||||
*
|
||||
* @param key the value's key in the registry
|
||||
* @return a new typed key
|
||||
*/
|
||||
public static TypedKey<Timeline> create(final Key key) {
|
||||
return TypedKey.create(RegistryKey.TIMELINE, key);
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package io.papermc.paper.registry.keys;
|
||||
|
||||
import static net.kyori.adventure.key.Key.key;
|
||||
|
||||
import io.papermc.paper.annotation.GeneratedClass;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import io.papermc.paper.registry.TypedKey;
|
||||
import io.papermc.paper.world.WorldClock;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* Vanilla keys for {@link RegistryKey#WORLD_CLOCK}.
|
||||
*
|
||||
* @apiNote The fields provided here are a direct representation of
|
||||
* what is available from the vanilla game source. They may be
|
||||
* changed (including removals) on any Minecraft version
|
||||
* bump, so cross-version compatibility is not provided on the
|
||||
* same level as it is on most of the other API.
|
||||
*/
|
||||
@SuppressWarnings({
|
||||
"unused",
|
||||
"SpellCheckingInspection"
|
||||
})
|
||||
@NullMarked
|
||||
@GeneratedClass
|
||||
public final class WorldClockKeys {
|
||||
/**
|
||||
* {@code minecraft:overworld}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TypedKey<WorldClock> OVERWORLD = create(key("overworld"));
|
||||
|
||||
/**
|
||||
* {@code minecraft:the_end}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TypedKey<WorldClock> THE_END = create(key("the_end"));
|
||||
|
||||
private WorldClockKeys() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a typed key for {@link WorldClock} in the registry {@code minecraft:world_clock}.
|
||||
*
|
||||
* @param key the value's key in the registry
|
||||
* @return a new typed key
|
||||
*/
|
||||
public static TypedKey<WorldClock> create(final Key key) {
|
||||
return TypedKey.create(RegistryKey.WORLD_CLOCK, key);
|
||||
}
|
||||
}
|
||||
+13
-20
@@ -3,14 +3,14 @@ package io.papermc.paper.registry.keys.tags;
|
||||
import static net.kyori.adventure.key.Key.key;
|
||||
|
||||
import io.papermc.paper.annotation.GeneratedClass;
|
||||
import io.papermc.paper.entity.poi.PoiType;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import io.papermc.paper.registry.tag.TagKey;
|
||||
import io.papermc.paper.world.Timeline;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* Vanilla tag keys for {@link RegistryKey#TIMELINE}.
|
||||
* Vanilla tag keys for {@link RegistryKey#POINT_OF_INTEREST_TYPE}.
|
||||
*
|
||||
* @apiNote The fields provided here are a direct representation of
|
||||
* what is available from the vanilla game source. They may be
|
||||
@@ -24,45 +24,38 @@ import org.jspecify.annotations.NullMarked;
|
||||
})
|
||||
@NullMarked
|
||||
@GeneratedClass
|
||||
public final class TimelineTagKeys {
|
||||
public final class PoiTypeTagKeys {
|
||||
/**
|
||||
* {@code #minecraft:in_end}
|
||||
* {@code #minecraft:acquirable_job_site}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TagKey<Timeline> IN_END = create(key("in_end"));
|
||||
public static final TagKey<PoiType> ACQUIRABLE_JOB_SITE = create(key("acquirable_job_site"));
|
||||
|
||||
/**
|
||||
* {@code #minecraft:in_nether}
|
||||
* {@code #minecraft:bee_home}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TagKey<Timeline> IN_NETHER = create(key("in_nether"));
|
||||
public static final TagKey<PoiType> BEE_HOME = create(key("bee_home"));
|
||||
|
||||
/**
|
||||
* {@code #minecraft:in_overworld}
|
||||
* {@code #minecraft:village}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TagKey<Timeline> IN_OVERWORLD = create(key("in_overworld"));
|
||||
public static final TagKey<PoiType> VILLAGE = create(key("village"));
|
||||
|
||||
/**
|
||||
* {@code #minecraft:universal}
|
||||
*
|
||||
* @apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
*/
|
||||
public static final TagKey<Timeline> UNIVERSAL = create(key("universal"));
|
||||
|
||||
private TimelineTagKeys() {
|
||||
private PoiTypeTagKeys() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a tag key for {@link Timeline} in the registry {@code minecraft:timeline}.
|
||||
* Creates a tag key for {@link PoiType} in the registry {@code minecraft:point_of_interest_type}.
|
||||
*
|
||||
* @param key the tag key's key
|
||||
* @return a new tag key
|
||||
*/
|
||||
public static TagKey<Timeline> create(final Key key) {
|
||||
return TagKey.create(RegistryKey.TIMELINE, key);
|
||||
public static TagKey<PoiType> create(final Key key) {
|
||||
return TagKey.create(RegistryKey.POINT_OF_INTEREST_TYPE, key);
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,7 @@ public final class NamespacedTag implements com.destroystokyo.paper.Namespaced {
|
||||
Preconditions.checkArgument(key != null, "Key cannot be null");
|
||||
|
||||
this.namespace = plugin.getName().toLowerCase(Locale.ROOT);
|
||||
this.key = key.toLowerCase().toLowerCase(Locale.ROOT);
|
||||
this.key = key.toLowerCase(Locale.ROOT);
|
||||
|
||||
// Check validity after normalization
|
||||
Preconditions.checkArgument(VALID_NAMESPACE.matcher(this.namespace).matches(), "Invalid namespace. Must be [a-z0-9._-]: %s", this.namespace);
|
||||
|
||||
@@ -257,16 +257,16 @@ public interface PlayerProfile extends org.bukkit.profile.PlayerProfile, PlayerH
|
||||
|
||||
@Override
|
||||
default void applySkinToPlayerHeadContents(final PlayerHeadObjectContents.Builder builder) {
|
||||
if (this.getProperties().isEmpty() && (this.getName() != null) != (this.getId() != null)) {
|
||||
if (this.getProperties().isEmpty()) {
|
||||
if (this.getId() != null) {
|
||||
builder.id(this.getId());
|
||||
} else {
|
||||
} else if (this.getName() != null) {
|
||||
builder.name(this.getName());
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
builder.id(this.getId())
|
||||
.name(this.getName())
|
||||
.profileProperties(this.getProperties().stream().map(prop -> property(prop.getName(), prop.getValue(), prop.getSignature())).toList());
|
||||
}
|
||||
builder.id(this.getId())
|
||||
.name(this.getName())
|
||||
.profileProperties(this.getProperties().stream().map(prop -> property(prop.getName(), prop.getValue(), prop.getSignature())).toList());
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package io.papermc.paper;
|
||||
import com.destroystokyo.paper.SkinParts;
|
||||
import io.papermc.paper.command.brigadier.CommandSourceStack;
|
||||
import io.papermc.paper.datacomponent.item.ResolvableProfile;
|
||||
import io.papermc.paper.entity.poi.PoiType;
|
||||
import io.papermc.paper.world.damagesource.CombatEntry;
|
||||
import io.papermc.paper.world.damagesource.FallLocationType;
|
||||
import java.util.Set;
|
||||
@@ -52,6 +53,14 @@ public interface InternalAPIBridge {
|
||||
*/
|
||||
DamageEffect getDamageEffect(String key);
|
||||
|
||||
/**
|
||||
* Creates an occupancy instance for the passed enum entry name.
|
||||
*
|
||||
* @param enumNameEntry the enum entry
|
||||
* @return the occupancy
|
||||
*/
|
||||
PoiType.Occupancy createOccupancy(String enumNameEntry);
|
||||
|
||||
/**
|
||||
* Constructs the legacy custom biome instance for the biome enum.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package io.papermc.paper.entity.poi;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* Holds the result of searching for a point of interest.
|
||||
*
|
||||
* @see World#locateAllPoiInRange(Location, Predicate, int)
|
||||
* @see World#locateAllPoiInRange(Location, Predicate, int, PoiType.Occupancy)
|
||||
*/
|
||||
@NullMarked
|
||||
public interface PoiSearchResult {
|
||||
|
||||
/**
|
||||
* Returns the {@link PoiType}.
|
||||
*
|
||||
* @return the {@link PoiType}
|
||||
*/
|
||||
PoiType poiType();
|
||||
|
||||
/**
|
||||
* Return the location of the {@link PoiType}.
|
||||
*
|
||||
* @return the location the {@link PoiType} was found at
|
||||
*/
|
||||
Location location();
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package io.papermc.paper.entity.poi;
|
||||
|
||||
import io.papermc.paper.InternalAPIBridge;
|
||||
import org.bukkit.Keyed;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* Represents a point of interest (POI).
|
||||
* <p>
|
||||
* Many PoiTypes act as a marker vs storing occupants examples of this are most
|
||||
* explicitly {@link PoiTypes#NETHER_PORTAL}. This state can be checked using the method
|
||||
* {@link #hasOccupants()} which will return {@code true} if the PoiType can have occupants.
|
||||
*
|
||||
* @see PoiTypes
|
||||
*/
|
||||
@NullMarked
|
||||
public interface PoiType extends Keyed {
|
||||
|
||||
/**
|
||||
* Determines whether the provided BlockState is relevant to this
|
||||
* point of interest.
|
||||
*
|
||||
* @param data the BlockData to check
|
||||
* @return {@code true} if the BlockState is relevant, otherwise {@code false}
|
||||
*/
|
||||
boolean is(BlockData data);
|
||||
|
||||
/**
|
||||
* Determines whether this PoiType can actually hold occupants.
|
||||
*
|
||||
* @return {@code true} if this PoiType can hold occupants
|
||||
*/
|
||||
boolean hasOccupants();
|
||||
|
||||
/**
|
||||
* Determines the type of occupancy the point of interest has.
|
||||
* <p>
|
||||
* A PoiType occupancy is defined by how much "space" a point of interest
|
||||
* has.
|
||||
* <p>
|
||||
* With context to a Villager workstation if a villager is currently using
|
||||
* a workstation as a source of its profession the occupancy would be
|
||||
* {@link Occupancy#IS_OCCUPIED}, however, if no villager was attached to
|
||||
* a workstation its occupancy would be {@link Occupancy#HAS_SPACE}.
|
||||
*/
|
||||
interface Occupancy {
|
||||
|
||||
/**
|
||||
* The poi has space.
|
||||
*/
|
||||
Occupancy HAS_SPACE = occupancy("HAS_SPACE");
|
||||
/**
|
||||
* The poi is occupied.
|
||||
*/
|
||||
Occupancy IS_OCCUPIED = occupancy("IS_OCCUPIED");
|
||||
/**
|
||||
* The poi is either occupied or has space.
|
||||
*/
|
||||
Occupancy ANY = occupancy("ANY");
|
||||
|
||||
private static Occupancy occupancy(final String enumEntryName) {
|
||||
return InternalAPIBridge.get().createOccupancy(enumEntryName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package io.papermc.paper.entity.poi;
|
||||
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.key.KeyPattern;
|
||||
import org.bukkit.Registry;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* All the built-in point of interest types.
|
||||
*/
|
||||
@NullMarked
|
||||
public final class PoiTypes {
|
||||
|
||||
// Start generate - PoiTypes
|
||||
public static final PoiType ARMORER = get("armorer");
|
||||
|
||||
public static final PoiType BEE_NEST = get("bee_nest");
|
||||
|
||||
public static final PoiType BEEHIVE = get("beehive");
|
||||
|
||||
public static final PoiType BUTCHER = get("butcher");
|
||||
|
||||
public static final PoiType CARTOGRAPHER = get("cartographer");
|
||||
|
||||
public static final PoiType CLERIC = get("cleric");
|
||||
|
||||
public static final PoiType FARMER = get("farmer");
|
||||
|
||||
public static final PoiType FISHERMAN = get("fisherman");
|
||||
|
||||
public static final PoiType FLETCHER = get("fletcher");
|
||||
|
||||
public static final PoiType HOME = get("home");
|
||||
|
||||
public static final PoiType LEATHERWORKER = get("leatherworker");
|
||||
|
||||
public static final PoiType LIBRARIAN = get("librarian");
|
||||
|
||||
public static final PoiType LIGHTNING_ROD = get("lightning_rod");
|
||||
|
||||
public static final PoiType LODESTONE = get("lodestone");
|
||||
|
||||
public static final PoiType MASON = get("mason");
|
||||
|
||||
public static final PoiType MEETING = get("meeting");
|
||||
|
||||
public static final PoiType NETHER_PORTAL = get("nether_portal");
|
||||
|
||||
public static final PoiType SHEPHERD = get("shepherd");
|
||||
|
||||
public static final PoiType TEST_INSTANCE = get("test_instance");
|
||||
|
||||
public static final PoiType TOOLSMITH = get("toolsmith");
|
||||
|
||||
public static final PoiType WEAPONSMITH = get("weaponsmith");
|
||||
// End generate - PoiTypes
|
||||
|
||||
private static PoiType get(@KeyPattern.Value final String key) {
|
||||
return Registry.POINT_OF_INTEREST_TYPE.getOrThrow(Key.key(Key.MINECRAFT_NAMESPACE, key));
|
||||
}
|
||||
|
||||
private PoiTypes() {
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,8 @@ package io.papermc.paper.registry;
|
||||
|
||||
import io.papermc.paper.datacomponent.DataComponentType;
|
||||
import io.papermc.paper.dialog.Dialog;
|
||||
import io.papermc.paper.entity.poi.PoiType;
|
||||
import io.papermc.paper.registry.tag.TagKey;
|
||||
import io.papermc.paper.world.Timeline;
|
||||
import io.papermc.paper.world.WorldClock;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.key.KeyPattern;
|
||||
import net.kyori.adventure.key.Keyed;
|
||||
@@ -94,6 +93,11 @@ public sealed interface RegistryKey<T> extends Keyed permits RegistryKeyImpl {
|
||||
* @see io.papermc.paper.registry.keys.VillagerProfessionKeys
|
||||
*/
|
||||
RegistryKey<Villager.Profession> VILLAGER_PROFESSION = create("villager_profession");
|
||||
/**
|
||||
* Built-in registry for poi types.
|
||||
* @see io.papermc.paper.registry.keys.PoiTypeKeys
|
||||
*/
|
||||
RegistryKey<PoiType> POINT_OF_INTEREST_TYPE = create("point_of_interest_type");
|
||||
/**
|
||||
* Built-in registry for villager types.
|
||||
* @see io.papermc.paper.registry.keys.VillagerTypeKeys
|
||||
@@ -253,16 +257,6 @@ public sealed interface RegistryKey<T> extends Keyed permits RegistryKeyImpl {
|
||||
* @see io.papermc.paper.registry.keys.DialogKeys
|
||||
*/
|
||||
RegistryKey<Dialog> DIALOG = create("dialog");
|
||||
/**
|
||||
* Data-driven registry for world clocks.
|
||||
* @see io.papermc.paper.registry.keys.WorldClockKeys
|
||||
*/
|
||||
RegistryKey<WorldClock> WORLD_CLOCK = create("world_clock");
|
||||
/**
|
||||
* Data-driven registry for timelines.
|
||||
* @see io.papermc.paper.registry.keys.TimelineKeys
|
||||
*/
|
||||
RegistryKey<Timeline> TIMELINE = create("timeline");
|
||||
|
||||
|
||||
/* ******************* *
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
package io.papermc.paper.registry.data;
|
||||
|
||||
import io.papermc.paper.registry.RegistryBuilder;
|
||||
import io.papermc.paper.registry.TypedKey;
|
||||
import io.papermc.paper.world.Timeline;
|
||||
import io.papermc.paper.world.WorldClock;
|
||||
import java.util.Map;
|
||||
import java.util.OptionalInt;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.Range;
|
||||
|
||||
/**
|
||||
* A data-centric version-specific registry entry for the {@link Timeline} type.
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@ApiStatus.NonExtendable
|
||||
public interface TimelineRegistryEntry {
|
||||
|
||||
/**
|
||||
* Gets the clock that drives this timeline.
|
||||
*
|
||||
* @return the clock
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
TypedKey<WorldClock> clock();
|
||||
|
||||
/**
|
||||
* Gets the timeline period, if any.
|
||||
*
|
||||
* @return the timeline period
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
OptionalInt periodTicks();
|
||||
|
||||
/**
|
||||
* Gets the time markers defined by this timeline.
|
||||
*
|
||||
* @return the time markers
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
Map<Key, TimeMarker> timeMarkers();
|
||||
|
||||
/**
|
||||
* A mutable builder for the {@link TimelineRegistryEntry} plugins may change in applicable registry events.
|
||||
* <p>
|
||||
* The following values are required for each builder:
|
||||
* <ul>
|
||||
* <li>{@link #clock(TypedKey)}</li>
|
||||
* </ul>
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@ApiStatus.NonExtendable
|
||||
interface Builder extends TimelineRegistryEntry, RegistryBuilder<Timeline> {
|
||||
|
||||
/**
|
||||
* Sets the clock that drives this timeline.
|
||||
*
|
||||
* @param clock the clock
|
||||
* @return this builder
|
||||
*/
|
||||
@Contract(value = "_ -> this", mutates = "this")
|
||||
Builder clock(TypedKey<WorldClock> clock);
|
||||
|
||||
/**
|
||||
* Sets the timeline period.
|
||||
*
|
||||
* @param periodTicks the period in ticks
|
||||
* @return this builder
|
||||
*/
|
||||
@Contract(value = "_ -> this", mutates = "this")
|
||||
Builder periodTicks(@Range(from = 1, to = Integer.MAX_VALUE) int periodTicks);
|
||||
|
||||
/**
|
||||
* Clears the timeline period.
|
||||
*
|
||||
* @return this builder
|
||||
*/
|
||||
@Contract(value = "-> this", mutates = "this")
|
||||
Builder clearPeriodTicks();
|
||||
|
||||
/**
|
||||
* Adds or replaces a time marker on this timeline.
|
||||
*
|
||||
* @param key the time marker key
|
||||
* @param ticks the marker tick
|
||||
* @param showInCommands whether the marker should be suggested in commands
|
||||
* @return this builder
|
||||
*/
|
||||
@Contract(value = "_, _, _ -> this", mutates = "this")
|
||||
Builder addTimeMarker(Key key, @Range(from = 0, to = Integer.MAX_VALUE) int ticks, boolean showInCommands);
|
||||
}
|
||||
|
||||
/**
|
||||
* A time marker defined by a timeline.
|
||||
*
|
||||
* @param ticks the tick this marker occurs on
|
||||
* @param showInCommands whether the marker should be suggested in commands
|
||||
*/
|
||||
record TimeMarker(@Range(from = 0, to = Integer.MAX_VALUE) int ticks, boolean showInCommands) {
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package io.papermc.paper.registry.data;
|
||||
|
||||
import io.papermc.paper.registry.RegistryBuilder;
|
||||
import io.papermc.paper.world.WorldClock;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
* A data-centric version-specific registry entry for the {@link WorldClock} type.
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@ApiStatus.NonExtendable
|
||||
public interface WorldClockRegistryEntry {
|
||||
|
||||
/**
|
||||
* A mutable builder for the {@link WorldClockRegistryEntry} plugins may change in applicable registry events.
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@ApiStatus.NonExtendable
|
||||
interface Builder extends WorldClockRegistryEntry, RegistryBuilder<WorldClock> {
|
||||
}
|
||||
}
|
||||
@@ -14,13 +14,9 @@ import io.papermc.paper.registry.data.InstrumentRegistryEntry;
|
||||
import io.papermc.paper.registry.data.JukeboxSongRegistryEntry;
|
||||
import io.papermc.paper.registry.data.PaintingVariantRegistryEntry;
|
||||
import io.papermc.paper.registry.data.PigVariantRegistryEntry;
|
||||
import io.papermc.paper.registry.data.TimelineRegistryEntry;
|
||||
import io.papermc.paper.registry.data.WolfVariantRegistryEntry;
|
||||
import io.papermc.paper.registry.data.WorldClockRegistryEntry;
|
||||
import io.papermc.paper.registry.data.ZombieNautilusVariantRegistryEntry;
|
||||
import io.papermc.paper.registry.data.dialog.DialogRegistryEntry;
|
||||
import io.papermc.paper.world.Timeline;
|
||||
import io.papermc.paper.world.WorldClock;
|
||||
import org.bukkit.Art;
|
||||
import org.bukkit.GameEvent;
|
||||
import org.bukkit.JukeboxSong;
|
||||
@@ -60,8 +56,6 @@ public final class RegistryEvents {
|
||||
public static final RegistryEventProvider<Pig.Variant, PigVariantRegistryEntry.Builder> PIG_VARIANT = create(RegistryKey.PIG_VARIANT);
|
||||
public static final RegistryEventProvider<ZombieNautilus.Variant, ZombieNautilusVariantRegistryEntry.Builder> ZOMBIE_NAUTILUS_VARIANT = create(RegistryKey.ZOMBIE_NAUTILUS_VARIANT);
|
||||
public static final RegistryEventProvider<Dialog, DialogRegistryEntry.Builder> DIALOG = create(RegistryKey.DIALOG);
|
||||
public static final RegistryEventProvider<WorldClock, WorldClockRegistryEntry.Builder> WORLD_CLOCK = create(RegistryKey.WORLD_CLOCK);
|
||||
public static final RegistryEventProvider<Timeline, TimelineRegistryEntry.Builder> TIMELINE = create(RegistryKey.TIMELINE);
|
||||
// End generate - RegistryEvents
|
||||
|
||||
private RegistryEvents() {
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
package io.papermc.paper.world;
|
||||
|
||||
import org.bukkit.Keyed;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
* Represents a timeline that can apply world attributes or define time markers for a {@link WorldClock}.
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
public interface Timeline extends Keyed {
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package io.papermc.paper.world;
|
||||
|
||||
import org.bukkit.Keyed;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
* Represents a world clock.
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
public interface WorldClock extends Keyed {
|
||||
}
|
||||
@@ -859,9 +859,13 @@ public final class Bukkit {
|
||||
/**
|
||||
* Gets the world with the given name.
|
||||
*
|
||||
* <p>This method is considered obsolete and is a candidate for future deprecation.
|
||||
* Prefer using {@link #getWorld(NamespacedKey)}.</p>
|
||||
*
|
||||
* @param name the name of the world to retrieve
|
||||
* @return a world with the given name, or null if none exists
|
||||
*/
|
||||
@ApiStatus.Obsolete
|
||||
@Nullable
|
||||
public static World getWorld(@NotNull String name) {
|
||||
return server.getWorld(name);
|
||||
@@ -877,7 +881,7 @@ public final class Bukkit {
|
||||
public static World getWorld(@NotNull UUID uid) {
|
||||
return server.getWorld(uid);
|
||||
}
|
||||
// Paper start
|
||||
|
||||
/**
|
||||
* Gets the world from the given NamespacedKey
|
||||
*
|
||||
@@ -899,7 +903,6 @@ public final class Bukkit {
|
||||
public static World getWorld(@NotNull net.kyori.adventure.key.Key worldKey) {
|
||||
return server.getWorld(worldKey);
|
||||
}
|
||||
// Paper end
|
||||
|
||||
/**
|
||||
* Create a new virtual {@link WorldBorder}.
|
||||
|
||||
@@ -5,6 +5,7 @@ import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import io.papermc.paper.world.MoonPhase;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
@@ -526,12 +527,12 @@ public interface RegionAccessor extends Keyed, io.papermc.paper.world.flag.Featu
|
||||
@NotNull
|
||||
public <T extends Entity> T addEntity(@NotNull T entity);
|
||||
|
||||
// Paper start
|
||||
/**
|
||||
* @return the current moon phase at the current time in the world
|
||||
* @apiNote the returned value may be inaccurate in custom biome using environmental attribute override
|
||||
*/
|
||||
@NotNull
|
||||
io.papermc.paper.world.MoonPhase getMoonPhase();
|
||||
MoonPhase getMoonPhase();
|
||||
|
||||
/**
|
||||
* Get the world's key
|
||||
@@ -559,5 +560,4 @@ public interface RegionAccessor extends Keyed, io.papermc.paper.world.flag.Featu
|
||||
* @return collides or not
|
||||
*/
|
||||
boolean hasCollisionsIn(@NotNull org.bukkit.util.BoundingBox boundingBox);
|
||||
// Paper end
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import io.papermc.paper.datacomponent.DataComponentType;
|
||||
import io.papermc.paper.entity.poi.PoiType;
|
||||
import io.papermc.paper.registry.RegistryAccess;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import io.papermc.paper.registry.TypedKey;
|
||||
@@ -280,6 +281,12 @@ public interface Registry<T extends Keyed> extends Iterable<T> {
|
||||
* @see Villager.Profession
|
||||
*/
|
||||
Registry<Villager.Profession> VILLAGER_PROFESSION = registryFor(RegistryKey.VILLAGER_PROFESSION);
|
||||
/**
|
||||
* Point of interest types.
|
||||
*
|
||||
* @see PoiType
|
||||
*/
|
||||
Registry<PoiType> POINT_OF_INTEREST_TYPE = registryFor(RegistryKey.POINT_OF_INTEREST_TYPE);
|
||||
/**
|
||||
* Villager type.
|
||||
*
|
||||
|
||||
@@ -16,7 +16,6 @@ import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Logger;
|
||||
import io.papermc.paper.configuration.ServerConfiguration;
|
||||
import io.papermc.paper.world.WorldClock;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
||||
@@ -166,85 +165,6 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
|
||||
@NotNull
|
||||
public Collection<? extends Player> getOnlinePlayers();
|
||||
|
||||
/**
|
||||
* Gets the relative in-game time of the specified clock.
|
||||
* <p>
|
||||
* The relative time is analogous to hours * 1000
|
||||
*
|
||||
* @param clock the clock
|
||||
* @return The current relative time
|
||||
* @see #getFullTime(WorldClock) Returns an absolute time of the clock
|
||||
*/
|
||||
public long getTime(@NotNull WorldClock clock);
|
||||
|
||||
/**
|
||||
* Sets the relative in-game time of the specified clock.
|
||||
* <p>
|
||||
* The relative time is analogous to hours * 1000
|
||||
* <p>
|
||||
* Note that setting the relative time below the current relative time
|
||||
* will actually move the clock forward a day. If you require to rewind
|
||||
* time, please see {@link #setFullTime(WorldClock, long)}
|
||||
*
|
||||
* @param clock the clock
|
||||
* @param time The new relative time to set the clock to (in hours*1000)
|
||||
* @see #setFullTime(WorldClock, long) Sets the absolute time of a specific clock
|
||||
*/
|
||||
public void setTime(@NotNull WorldClock clock, long time);
|
||||
|
||||
/**
|
||||
* Gets the full in-game time of the specified clock.
|
||||
*
|
||||
* @param clock the clock
|
||||
* @return The current absolute time
|
||||
* @see #getTime(WorldClock) Returns a relative time of the clock
|
||||
*/
|
||||
public long getFullTime(@NotNull WorldClock clock);
|
||||
|
||||
/**
|
||||
* Sets the in-game time of the specified clock.
|
||||
* <p>
|
||||
* Note that this sets the full time of the clock, which may cause adverse
|
||||
* effects such as breaking redstone clocks and any scheduled events
|
||||
*
|
||||
* @param clock the clock
|
||||
* @param time The new absolute time to set this clock to
|
||||
* @see #setTime(WorldClock, long) Sets the relative time of a specific clock
|
||||
*/
|
||||
public void setFullTime(@NotNull WorldClock clock, long time);
|
||||
|
||||
/**
|
||||
* Gets whether the specified clock is paused.
|
||||
*
|
||||
* @param clock the clock
|
||||
* @return whether the clock is paused
|
||||
*/
|
||||
public boolean isClockPaused(@NotNull WorldClock clock);
|
||||
|
||||
/**
|
||||
* Sets whether the specified clock is paused.
|
||||
*
|
||||
* @param clock the clock
|
||||
* @param paused whether the clock is paused
|
||||
*/
|
||||
public void setClockPaused(@NotNull WorldClock clock, boolean paused);
|
||||
|
||||
/**
|
||||
* Gets the rate of the specified clock.
|
||||
*
|
||||
* @param clock the clock
|
||||
* @return the clock rate
|
||||
*/
|
||||
public float getClockRate(@NotNull WorldClock clock);
|
||||
|
||||
/**
|
||||
* Sets the rate of the specified clock.
|
||||
*
|
||||
* @param clock the clock
|
||||
* @param rate the clock rate
|
||||
*/
|
||||
public void setClockRate(@NotNull WorldClock clock, float rate);
|
||||
|
||||
/**
|
||||
* Get the maximum amount of players which can login to this server.
|
||||
*
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package org.bukkit;
|
||||
|
||||
import io.papermc.paper.entity.poi.PoiSearchResult;
|
||||
import io.papermc.paper.entity.poi.PoiType;
|
||||
import io.papermc.paper.raytracing.PositionedRayTraceConfigurationBuilder;
|
||||
import io.papermc.paper.world.Timeline;
|
||||
import io.papermc.paper.world.WorldClock;
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
@@ -45,6 +45,7 @@ import org.bukkit.util.BoundingBox;
|
||||
import org.bukkit.util.RayTraceResult;
|
||||
import org.bukkit.util.StructureSearchResult;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.checkerframework.checker.index.qual.Positive;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -2073,10 +2074,8 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
|
||||
*
|
||||
* @param time The new relative time to set the in-game time to (in
|
||||
* hours*1000)
|
||||
* @see Server#setTime(WorldClock, long) Sets the relative time of a specific clock
|
||||
* @deprecated use {@link Server#setTime(WorldClock, long)} with {@link #getClock()} instead
|
||||
* @see #setFullTime(long) Sets the absolute time of this world
|
||||
*/
|
||||
@Deprecated(since = "26.1")
|
||||
public void setTime(long time);
|
||||
|
||||
/**
|
||||
@@ -2094,31 +2093,13 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
|
||||
* effects such as breaking redstone clocks and any scheduled events
|
||||
*
|
||||
* @param time The new absolute time to set this world to
|
||||
* @see Server#setFullTime(WorldClock, long) Sets the absolute time of a specific clock
|
||||
* @deprecated use {@link Server#setFullTime(WorldClock, long)} with {@link #getClock()} instead
|
||||
* @see #setTime(long) Sets the relative time of this world
|
||||
* @deprecated all overworlds share the same world clock by default now
|
||||
* @throws IllegalArgumentException if this world does not have a world clock (e.g. the nether)
|
||||
*/
|
||||
@Deprecated(since = "26.1")
|
||||
@Deprecated // TODO world clock API with links to it
|
||||
public void setFullTime(long time);
|
||||
|
||||
/**
|
||||
* Gets the clock of this world.
|
||||
* <p>
|
||||
* This may be a world-specific override of the dimension type's clock.
|
||||
*
|
||||
* @return the clock, or null if this world does not have one
|
||||
*/
|
||||
@Nullable
|
||||
public WorldClock getClock();
|
||||
|
||||
/**
|
||||
* Gets the timelines applied by this world's dimension type.
|
||||
*
|
||||
* @return the timelines
|
||||
*/
|
||||
@NotNull
|
||||
public List<Timeline> getTimelines();
|
||||
|
||||
// Paper start
|
||||
|
||||
/**
|
||||
@@ -2791,10 +2772,11 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
|
||||
/**
|
||||
* Gets if this world is natural.
|
||||
* <p>
|
||||
* When false, the moon is not visible and eyeblossoms do not open/close
|
||||
* When true, eyeblossoms cycle open/close, nether portals can spawn
|
||||
* zombified piglins and creaking heart works
|
||||
*
|
||||
* @return true if world is natural
|
||||
* @deprecated replaced by the gameplay/eyeblossom_open and gameplay/creaking_active environment attributes
|
||||
* @deprecated replaced by the gameplay/nether_portal_spawns_piglin, gameplay/eyeblossom_open and gameplay/creaking_active environmental attributes
|
||||
*/
|
||||
@Deprecated(since = "1.21.11")
|
||||
public boolean isNatural();
|
||||
@@ -2806,7 +2788,7 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
|
||||
* not always be the case.
|
||||
*
|
||||
* @return true if beds work in this world
|
||||
* @deprecated Due to 1.21.11 beds changes, a boolean no longer
|
||||
* @deprecated due to 1.21.11 beds changes, a boolean no longer
|
||||
* represents if they work. There is no replacement API yet
|
||||
*/
|
||||
@ApiStatus.Obsolete(since = "1.21.11")
|
||||
@@ -2831,6 +2813,7 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
|
||||
* transforming to zombified piglins.
|
||||
*
|
||||
* @return true if piglins will not transform to zombified piglins
|
||||
* @apiNote the returned value may be inaccurate in custom biome using environmental attribute override
|
||||
*/
|
||||
public boolean isPiglinSafe();
|
||||
|
||||
@@ -2838,6 +2821,7 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
|
||||
* Gets if this world allows players to charge and use respawn anchors.
|
||||
*
|
||||
* @return true if players can charge and use respawn anchors
|
||||
* @apiNote the returned value may be inaccurate in custom biome using environmental attribute override
|
||||
*/
|
||||
public boolean isRespawnAnchorWorks();
|
||||
|
||||
@@ -2846,6 +2830,7 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
|
||||
* raid.
|
||||
*
|
||||
* @return true if raids will be triggered
|
||||
* @apiNote the returned value may be inaccurate in custom biome using environmental attribute override
|
||||
*/
|
||||
public boolean hasRaids();
|
||||
|
||||
@@ -2859,7 +2844,7 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
|
||||
* </ul>
|
||||
*
|
||||
* @return true if this world has the above mechanics
|
||||
* @deprecated as of 1.21.11, ultra warm is replaced by the WATER_EVAPORATES, FAST_LAVA, and DEFAULT_DRIPSTONE_PARTICLE environment attributes.
|
||||
* @deprecated replaced by the gameplay/water_evaporates and gameplay/fast_lava environmental attributes
|
||||
*/
|
||||
@Deprecated(since = "1.21.11")
|
||||
public boolean isUltraWarm();
|
||||
@@ -4317,6 +4302,69 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
|
||||
return java.util.Optional.ofNullable(this.locateNearestBiome(origin, radius, step, step, biome)).map(BiomeSearchResult::getLocation).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the nearest point of interest closest to the given location
|
||||
* without any occupancy restriction.
|
||||
*
|
||||
* @param origin where to start looking for a new point of interest at
|
||||
* @param poiType the poi type to find
|
||||
* @param radius the radius
|
||||
* @return a location at the nearest PoiType or {@code null} if no poi was found
|
||||
*/
|
||||
@Nullable
|
||||
default Location locateNearestPoi(@NotNull Location origin, @NotNull PoiType poiType, @Positive int radius) {
|
||||
return this.locateNearestPoi(origin, poiType, radius, PoiType.Occupancy.ANY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the nearest point of interest closest to the given location.
|
||||
* <p>
|
||||
* {@link PoiType} that return {@code false} for {@link PoiType#hasOccupants()}
|
||||
* may not behave as expected for given occupancies other than
|
||||
* {@link PoiType.Occupancy#ANY}.
|
||||
*
|
||||
* @param origin where to start looking for a new point of interest at
|
||||
* @param poiType the poi type to find
|
||||
* @param radius the radius
|
||||
* @param occupancy the current required occupancy of the point of interest
|
||||
* @return a location at the nearest PoiType or {@code null} if no poi was found
|
||||
*/
|
||||
@Nullable
|
||||
Location locateNearestPoi(@NotNull Location origin, @NotNull PoiType poiType, @Positive int radius, @NotNull PoiType.Occupancy occupancy);
|
||||
|
||||
/**
|
||||
* Finds all valid {@link PoiType} in the provided radius and returns them
|
||||
* in a list format without any occupancy restriction.
|
||||
*
|
||||
* @param origin the center point of the radius
|
||||
* @param poiTypePredicate the predicate to test whether a PoiType
|
||||
* can be collected into the result
|
||||
* @param radius the radius
|
||||
* @return a list of search results containing all found Poi's in the range
|
||||
*/
|
||||
@NotNull
|
||||
default List<PoiSearchResult> locateAllPoiInRange(@NotNull Location origin, @NotNull Predicate<PoiType> poiTypePredicate, @Positive int radius) {
|
||||
return this.locateAllPoiInRange(origin, poiTypePredicate, radius, PoiType.Occupancy.ANY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all valid {@link PoiType} in the provided radius and returns them
|
||||
* in a list format.
|
||||
* <p>
|
||||
* {@link PoiType} that return false for {@link PoiType#hasOccupants()}
|
||||
* may not behave as expected for given occupancies other than
|
||||
* {@link PoiType.Occupancy#ANY}.
|
||||
*
|
||||
* @param origin the center point of the radius
|
||||
* @param poiTypePredicate the predicate to test whether a PoiType
|
||||
* can be collected into the result
|
||||
* @param radius the radius
|
||||
* @param occupancy the current required occupancy of the point of interest
|
||||
* @return a list of search results containing all found Poi's in the range
|
||||
*/
|
||||
@NotNull
|
||||
List<PoiSearchResult> locateAllPoiInRange(@NotNull Location origin, @NotNull Predicate<PoiType> poiTypePredicate, @Positive int radius, @NotNull PoiType.Occupancy occupancy);
|
||||
|
||||
/**
|
||||
* Gets the coordinate scaling of this world.
|
||||
*
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
package org.bukkit;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import io.papermc.paper.registry.TypedKey;
|
||||
import io.papermc.paper.world.Timeline;
|
||||
import io.papermc.paper.world.WorldClock;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import io.papermc.paper.math.Position;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.generator.BiomeProvider;
|
||||
import org.bukkit.generator.ChunkGenerator;
|
||||
@@ -32,8 +26,10 @@ public class WorldCreator {
|
||||
private String generatorSettings = "";
|
||||
private boolean hardcore = false;
|
||||
private boolean bonusChest = false;
|
||||
private TypedKey<WorldClock> clock = null;
|
||||
private List<TypedKey<Timeline>> timelines = null;
|
||||
|
||||
private @Nullable Position spawnPositionOverride;
|
||||
private @Nullable Float spawnYawOverride;
|
||||
private @Nullable Float spawnPitchOverride;
|
||||
|
||||
/**
|
||||
* Creates an empty WorldCreationOptions for the given world name.
|
||||
@@ -161,8 +157,6 @@ public class WorldCreator {
|
||||
generateStructures = world.canGenerateStructures();
|
||||
hardcore = world.isHardcore();
|
||||
bonusChest = world.hasBonusChest();
|
||||
clock = world.getClock() == null ? null : TypedKey.create(RegistryKey.WORLD_CLOCK, world.getClock().getKey());
|
||||
timelines = world.getTimelines().stream().map(timeline -> TypedKey.create(RegistryKey.TIMELINE, timeline.getKey())).toList();
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -186,8 +180,6 @@ public class WorldCreator {
|
||||
generatorSettings = creator.generatorSettings();
|
||||
hardcore = creator.hardcore();
|
||||
bonusChest = creator.bonusChest();
|
||||
clock = creator.clock();
|
||||
timelines = creator.timelines() == null ? null : new ArrayList<>(creator.timelines());
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -274,6 +266,80 @@ public class WorldCreator {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the forced spawn position for the world created by this {@link WorldCreator}.
|
||||
* <p>
|
||||
* This overrides vanilla and custom generator behavior without loading any chunks.
|
||||
* When a forced spawn is specified, the bonus chest will not be generated.
|
||||
*
|
||||
* @param position the spawn position
|
||||
* @param yaw the yaw rotation at spawn
|
||||
* @param pitch the pitch rotation at spawn
|
||||
* @return this object, for chaining
|
||||
*/
|
||||
@NotNull
|
||||
public WorldCreator forcedSpawnPosition(@NotNull Position position, float yaw, float pitch) {
|
||||
this.spawnPositionOverride = position; // If you set this to null, it wont do anything!
|
||||
this.spawnYawOverride = yaw;
|
||||
this.spawnPitchOverride = pitch;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears any previously forced spawn position.
|
||||
* <p>
|
||||
* After calling this, vanilla spawn selection behavior is used.
|
||||
*
|
||||
* @return this object, for chaining
|
||||
*/
|
||||
@NotNull
|
||||
public WorldCreator clearForcedSpawnPosition() {
|
||||
this.spawnPositionOverride = null;
|
||||
this.spawnYawOverride = null;
|
||||
this.spawnPitchOverride = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the forced spawn position that will be applied when this world is created.
|
||||
* <p>
|
||||
* If this returns {@code null}, vanilla or custom generator behavior will be used
|
||||
* to determine the spawn position.
|
||||
*
|
||||
* @return the forced spawn position, or {@code null} for the vanilla behavior
|
||||
*/
|
||||
public @Nullable Position forcedSpawnPosition() {
|
||||
return this.spawnPositionOverride;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the forced spawn yaw that will be applied when this world is created.
|
||||
* <p>
|
||||
* If this returns {@code null}, the spawn yaw will be determined by vanilla behavior
|
||||
* or the world generator.
|
||||
* <p>
|
||||
* This value is only meaningful if a forced spawn position is present.
|
||||
*
|
||||
* @return the forced spawn yaw, or {@code null} for the vanilla behavior
|
||||
*/
|
||||
public @Nullable Float forcedSpawnYaw() {
|
||||
return this.spawnYawOverride;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the forced spawn pitch that will be applied when this world is created.
|
||||
* <p>
|
||||
* If this returns {@code null}, the spawn pitch will be determined by vanilla behavior
|
||||
* or the world generator.
|
||||
* <p>
|
||||
* This value is only meaningful if a forced spawn position is present.
|
||||
*
|
||||
* @return the forced spawn pitch, or {@code null} for the vanilla behavior
|
||||
*/
|
||||
public @Nullable Float forcedSpawnPitch() {
|
||||
return this.spawnPitchOverride;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the generator that will be used to create or load the world.
|
||||
* <p>
|
||||
@@ -526,56 +592,6 @@ public class WorldCreator {
|
||||
return bonusChest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the clock that will be used by this world.
|
||||
* <p>
|
||||
* If unset, the clock from the selected environment's dimension type will be used.
|
||||
* If set, this overrides the selected environment dimension type's clock.
|
||||
*
|
||||
* @param clock the clock, or null to use the environment default
|
||||
* @return This object, for chaining
|
||||
*/
|
||||
@NotNull
|
||||
public WorldCreator clock(@Nullable TypedKey<WorldClock> clock) {
|
||||
this.clock = clock;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the clock override that will be used by this world.
|
||||
*
|
||||
* @return the clock override, or null to use the environment default
|
||||
*/
|
||||
@Nullable
|
||||
public TypedKey<WorldClock> clock() {
|
||||
return this.clock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the timelines that will be used by this world's dimension type.
|
||||
* <p>
|
||||
* If unset, the timelines from the selected environment's dimension type will be used.
|
||||
* Passing an empty collection will create or load the world with no timelines.
|
||||
*
|
||||
* @param timelines the timelines, or null to use the environment default
|
||||
* @return This object, for chaining
|
||||
*/
|
||||
@NotNull
|
||||
public WorldCreator timelines(@Nullable Collection<TypedKey<Timeline>> timelines) {
|
||||
this.timelines = timelines == null ? null : new ArrayList<>(timelines);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the timeline overrides that will be used by this world's dimension type.
|
||||
*
|
||||
* @return the timeline overrides, or null to use the environment default
|
||||
*/
|
||||
@Nullable
|
||||
public List<TypedKey<Timeline>> timelines() {
|
||||
return this.timelines == null ? null : List.copyOf(this.timelines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a world with the specified options.
|
||||
* <p>
|
||||
|
||||
@@ -2,12 +2,15 @@ package org.bukkit.entity;
|
||||
|
||||
import org.bukkit.attribute.Attribute;
|
||||
import org.bukkit.damage.DamageSource;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.bukkit.damage.DamageType;
|
||||
import org.bukkit.event.entity.EntityRegainHealthEvent;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Represents an {@link Entity} that has health and can take damage.
|
||||
*/
|
||||
@NullMarked
|
||||
public interface Damageable extends Entity {
|
||||
/**
|
||||
* Deals the given amount of damage to this entity.
|
||||
@@ -32,7 +35,24 @@ public interface Damageable extends Entity {
|
||||
* @param amount amount of damage to deal
|
||||
* @param damageSource source to which the damage should be attributed
|
||||
*/
|
||||
void damage(double amount, @NotNull DamageSource damageSource);
|
||||
void damage(double amount, DamageSource damageSource);
|
||||
|
||||
/**
|
||||
* Sets the entity's health to 0 and kill the entity with a generic DamageSource.
|
||||
*
|
||||
* @throws IllegalStateException if is used in world generation
|
||||
*/
|
||||
default void kill() {
|
||||
this.kill(DamageSource.builder(DamageType.GENERIC_KILL).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entity's health to 0 and kill the entity with the specified DamageSource.
|
||||
*
|
||||
* @param damageSource the DamageSource to use for the kill
|
||||
* @throws IllegalStateException if is used in world generation
|
||||
*/
|
||||
void kill(DamageSource damageSource);
|
||||
|
||||
/**
|
||||
* Gets the entity's health from 0 to {@link #getMaxHealth()}, where 0 is dead.
|
||||
@@ -57,7 +77,7 @@ public interface Damageable extends Entity {
|
||||
* @param amount heal amount
|
||||
*/
|
||||
default void heal(final double amount) {
|
||||
this.heal(amount, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.CUSTOM);
|
||||
this.heal(amount, EntityRegainHealthEvent.RegainReason.CUSTOM);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,7 +86,7 @@ public interface Damageable extends Entity {
|
||||
* @param amount heal amount
|
||||
* @param reason heal reason
|
||||
*/
|
||||
void heal(double amount, @NotNull org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason reason);
|
||||
void heal(double amount, EntityRegainHealthEvent.RegainReason reason);
|
||||
|
||||
/**
|
||||
* Gets the entity's absorption amount.
|
||||
|
||||
@@ -10,8 +10,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Called when a block is ignited. If you want to catch when a Player places
|
||||
* fire, you need to use {@link BlockPlaceEvent}.
|
||||
* Called when a block is ignited.
|
||||
* <p>
|
||||
* If this event is cancelled, the block will not be ignited.
|
||||
*/
|
||||
|
||||
@@ -1,27 +1,30 @@
|
||||
package org.bukkit.event.hanging;
|
||||
|
||||
import org.bukkit.damage.DamageSource;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Hanging;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* Triggered when a hanging entity is removed by an entity
|
||||
*/
|
||||
@NullMarked
|
||||
public class HangingBreakByEntityEvent extends HangingBreakEvent {
|
||||
|
||||
private final Entity remover;
|
||||
private final DamageSource damageSource;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public HangingBreakByEntityEvent(@NotNull final Hanging hanging, @NotNull final Entity remover) {
|
||||
this(hanging, remover, HangingBreakEvent.RemoveCause.ENTITY);
|
||||
public HangingBreakByEntityEvent(final Hanging hanging, final Entity remover, final DamageSource damageSource) {
|
||||
this(hanging, remover, damageSource, HangingBreakEvent.RemoveCause.ENTITY);
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public HangingBreakByEntityEvent(@NotNull final Hanging hanging, @NotNull final Entity remover, @NotNull final HangingBreakEvent.RemoveCause cause) {
|
||||
public HangingBreakByEntityEvent(final Hanging hanging, final Entity remover, final DamageSource damageSource, final HangingBreakEvent.RemoveCause cause) {
|
||||
super(hanging, cause);
|
||||
this.remover = remover;
|
||||
this.damageSource = damageSource;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -29,8 +32,16 @@ public class HangingBreakByEntityEvent extends HangingBreakEvent {
|
||||
*
|
||||
* @return the entity that removed the hanging entity
|
||||
*/
|
||||
@NotNull
|
||||
public Entity getRemover() {
|
||||
return this.remover;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link DamageSource} that caused the hanging entity to be removed.
|
||||
*
|
||||
* @return the damage source
|
||||
*/
|
||||
public DamageSource getDamageSource() {
|
||||
return this.damageSource;
|
||||
}
|
||||
}
|
||||
@@ -6,24 +6,26 @@ import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Called when a player gets kicked from the server
|
||||
*/
|
||||
@NullMarked
|
||||
public class PlayerKickEvent extends PlayerEvent implements Cancellable {
|
||||
|
||||
private static final HandlerList HANDLER_LIST = new HandlerList();
|
||||
|
||||
private Component kickReason;
|
||||
private Component leaveMessage;
|
||||
private @Nullable Component leaveMessage;
|
||||
private final Cause cause;
|
||||
|
||||
private boolean cancelled;
|
||||
|
||||
@ApiStatus.Internal
|
||||
@Deprecated(forRemoval = true)
|
||||
public PlayerKickEvent(@NotNull final Player playerKicked, @NotNull final String kickReason, @NotNull final String leaveMessage) {
|
||||
public PlayerKickEvent(final Player playerKicked, final String kickReason, final String leaveMessage) {
|
||||
super(playerKicked);
|
||||
this.kickReason = LegacyComponentSerializer.legacySection().deserialize(kickReason);
|
||||
this.leaveMessage = LegacyComponentSerializer.legacySection().deserialize(leaveMessage);
|
||||
@@ -32,7 +34,7 @@ public class PlayerKickEvent extends PlayerEvent implements Cancellable {
|
||||
|
||||
@ApiStatus.Internal
|
||||
@Deprecated(forRemoval = true)
|
||||
public PlayerKickEvent(@NotNull final Player playerKicked, @NotNull final Component kickReason, @NotNull final Component leaveMessage) {
|
||||
public PlayerKickEvent(final Player playerKicked, final Component kickReason, final Component leaveMessage) {
|
||||
super(playerKicked);
|
||||
this.kickReason = kickReason;
|
||||
this.leaveMessage = leaveMessage;
|
||||
@@ -40,7 +42,7 @@ public class PlayerKickEvent extends PlayerEvent implements Cancellable {
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public PlayerKickEvent(@NotNull final Player playerKicked, @NotNull final Component kickReason, @NotNull final Component leaveMessage, @NotNull final Cause cause) {
|
||||
public PlayerKickEvent(final Player playerKicked, final Component kickReason, final Component leaveMessage, final Cause cause) {
|
||||
super(playerKicked);
|
||||
this.kickReason = kickReason;
|
||||
this.leaveMessage = leaveMessage;
|
||||
@@ -52,7 +54,7 @@ public class PlayerKickEvent extends PlayerEvent implements Cancellable {
|
||||
*
|
||||
* @return string kick reason
|
||||
*/
|
||||
public @NotNull Component reason() {
|
||||
public Component reason() {
|
||||
return this.kickReason;
|
||||
}
|
||||
|
||||
@@ -61,7 +63,7 @@ public class PlayerKickEvent extends PlayerEvent implements Cancellable {
|
||||
*
|
||||
* @param kickReason kick reason
|
||||
*/
|
||||
public void reason(@NotNull Component kickReason) {
|
||||
public void reason(Component kickReason) {
|
||||
this.kickReason = kickReason;
|
||||
}
|
||||
|
||||
@@ -71,7 +73,6 @@ public class PlayerKickEvent extends PlayerEvent implements Cancellable {
|
||||
* @return string kick reason
|
||||
* @deprecated in favour of {@link #reason()}
|
||||
*/
|
||||
@NotNull
|
||||
@Deprecated
|
||||
public String getReason() {
|
||||
return LegacyComponentSerializer.legacySection().serialize(this.kickReason);
|
||||
@@ -81,10 +82,10 @@ public class PlayerKickEvent extends PlayerEvent implements Cancellable {
|
||||
* Sets the reason why the player is getting kicked
|
||||
*
|
||||
* @param kickReason kick reason
|
||||
* @deprecated in favour of {@link #reason(net.kyori.adventure.text.Component)}
|
||||
* @deprecated in favour of {@link #reason(Component)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void setReason(@NotNull String kickReason) {
|
||||
public void setReason(String kickReason) {
|
||||
this.kickReason = LegacyComponentSerializer.legacySection().deserialize(kickReason);
|
||||
}
|
||||
|
||||
@@ -93,16 +94,16 @@ public class PlayerKickEvent extends PlayerEvent implements Cancellable {
|
||||
*
|
||||
* @return string kick reason
|
||||
*/
|
||||
public @NotNull Component leaveMessage() {
|
||||
public @Nullable Component leaveMessage() {
|
||||
return this.leaveMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the leave message send to all online players
|
||||
*
|
||||
* @param leaveMessage leave message
|
||||
* @param leaveMessage leave message. If {@code null}, no message will be sent
|
||||
*/
|
||||
public void leaveMessage(@NotNull Component leaveMessage) {
|
||||
public void leaveMessage(@Nullable Component leaveMessage) {
|
||||
this.leaveMessage = leaveMessage;
|
||||
}
|
||||
|
||||
@@ -112,28 +113,26 @@ public class PlayerKickEvent extends PlayerEvent implements Cancellable {
|
||||
* @return string kick reason
|
||||
* @deprecated in favour of {@link #leaveMessage()}
|
||||
*/
|
||||
@NotNull
|
||||
@Deprecated
|
||||
public String getLeaveMessage() {
|
||||
return LegacyComponentSerializer.legacySection().serialize(this.leaveMessage);
|
||||
public @Nullable String getLeaveMessage() {
|
||||
return LegacyComponentSerializer.legacySection().serializeOrNull(this.leaveMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the leave message send to all online players
|
||||
*
|
||||
* @param leaveMessage leave message
|
||||
* @deprecated in favour of {@link #leaveMessage(net.kyori.adventure.text.Component)}
|
||||
* @param leaveMessage leave message. If {@code null}, no message will be sent
|
||||
* @deprecated in favour of {@link #leaveMessage(Component)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void setLeaveMessage(@NotNull String leaveMessage) {
|
||||
this.leaveMessage = LegacyComponentSerializer.legacySection().deserialize(leaveMessage);
|
||||
public void setLeaveMessage(@Nullable String leaveMessage) {
|
||||
this.leaveMessage = LegacyComponentSerializer.legacySection().deserializeOrNull(leaveMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cause of this kick
|
||||
*/
|
||||
@NotNull
|
||||
public org.bukkit.event.player.PlayerKickEvent.Cause getCause() {
|
||||
public PlayerKickEvent.Cause getCause() {
|
||||
return this.cause;
|
||||
}
|
||||
|
||||
@@ -147,13 +146,11 @@ public class PlayerKickEvent extends PlayerEvent implements Cancellable {
|
||||
this.cancelled = cancel;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return HANDLER_LIST;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static HandlerList getHandlerList() {
|
||||
return HANDLER_LIST;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.bukkit.event.vehicle;
|
||||
|
||||
import org.bukkit.damage.DamageSource;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Vehicle;
|
||||
import org.bukkit.event.Cancellable;
|
||||
@@ -15,18 +16,30 @@ public class VehicleDamageEvent extends VehicleEvent implements Cancellable {
|
||||
|
||||
private static final HandlerList HANDLER_LIST = new HandlerList();
|
||||
|
||||
private final DamageSource damageSource;
|
||||
private final Entity attacker;
|
||||
private double damage;
|
||||
|
||||
private boolean cancelled;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public VehicleDamageEvent(@NotNull final Vehicle vehicle, @Nullable final Entity attacker, final double damage) {
|
||||
public VehicleDamageEvent(final @NotNull Vehicle vehicle, final @NotNull DamageSource damageSource, final @Nullable Entity attacker, final double damage) {
|
||||
super(vehicle);
|
||||
this.damageSource = damageSource;
|
||||
this.attacker = attacker;
|
||||
this.damage = damage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the DamageSource that caused the damage.
|
||||
*
|
||||
* @return the DamageSource that caused the damage
|
||||
*/
|
||||
@NotNull
|
||||
public DamageSource getDamageSource() {
|
||||
return this.damageSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Entity that is attacking the vehicle
|
||||
*
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.bukkit.event.vehicle;
|
||||
|
||||
import org.bukkit.damage.DamageSource;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Vehicle;
|
||||
import org.bukkit.event.Cancellable;
|
||||
@@ -17,15 +18,27 @@ public class VehicleDestroyEvent extends VehicleEvent implements Cancellable {
|
||||
|
||||
private static final HandlerList HANDLER_LIST = new HandlerList();
|
||||
|
||||
private final DamageSource damageSource;
|
||||
private final Entity attacker;
|
||||
private boolean cancelled;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public VehicleDestroyEvent(@NotNull final Vehicle vehicle, @Nullable final Entity attacker) {
|
||||
public VehicleDestroyEvent(final @NotNull Vehicle vehicle, final @NotNull DamageSource damageSource, final @Nullable Entity attacker) {
|
||||
super(vehicle);
|
||||
this.damageSource = damageSource;
|
||||
this.attacker = attacker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the DamageSource that has destroyed the vehicle.
|
||||
*
|
||||
* @return the DamageSource that has destroyed the vehicle
|
||||
*/
|
||||
@NotNull
|
||||
public DamageSource getDamageSource() {
|
||||
return this.damageSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Entity that has destroyed the vehicle, potentially null
|
||||
*
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
package org.bukkit.event.world;
|
||||
|
||||
import io.papermc.paper.world.WorldClock;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Called when the time skips for a world clock.
|
||||
* <p>
|
||||
* If the event is cancelled the time will not change.
|
||||
*/
|
||||
public class ClockTimeSkipEvent extends Event implements Cancellable {
|
||||
|
||||
private static final HandlerList HANDLER_LIST = new HandlerList();
|
||||
|
||||
private final WorldClock clock;
|
||||
private final SkipReason skipReason;
|
||||
private long skipAmount;
|
||||
|
||||
private boolean cancelled;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public ClockTimeSkipEvent(@NotNull WorldClock clock, @NotNull SkipReason skipReason, long skipAmount) {
|
||||
this.clock = clock;
|
||||
this.skipReason = skipReason;
|
||||
this.skipAmount = skipAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the clock whose time is changing.
|
||||
*
|
||||
* @return the clock, or null if the change does not target a specific clock
|
||||
*/
|
||||
@NotNull
|
||||
public WorldClock getClock() {
|
||||
return this.clock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the reason why the time has skipped.
|
||||
*
|
||||
* @return a SkipReason value detailing why the time has skipped
|
||||
*/
|
||||
@NotNull
|
||||
public SkipReason getSkipReason() {
|
||||
return this.skipReason;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the amount of time that was skipped.
|
||||
*
|
||||
* @return Amount of time skipped
|
||||
*/
|
||||
public long getSkipAmount() {
|
||||
return this.skipAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the amount of time to skip.
|
||||
*
|
||||
* @param skipAmount Amount of time to skip
|
||||
*/
|
||||
public void setSkipAmount(long skipAmount) {
|
||||
this.skipAmount = skipAmount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return this.cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancel) {
|
||||
this.cancelled = cancel;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return HANDLER_LIST;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static HandlerList getHandlerList() {
|
||||
return HANDLER_LIST;
|
||||
}
|
||||
|
||||
/**
|
||||
* An enum specifying the reason the time skipped.
|
||||
*/
|
||||
public enum SkipReason {
|
||||
|
||||
/**
|
||||
* When time is changed using the vanilla /time command.
|
||||
*/
|
||||
COMMAND,
|
||||
/**
|
||||
* When time is changed by a plugin.
|
||||
*/
|
||||
CUSTOM,
|
||||
/**
|
||||
* When time is changed by all players sleeping in their beds and the
|
||||
* night skips.
|
||||
*/
|
||||
NIGHT_SKIP
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.bukkit.event.world;
|
||||
|
||||
import io.papermc.paper.world.WorldClock;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -11,26 +11,59 @@ import org.jetbrains.annotations.NotNull;
|
||||
* <p>
|
||||
* If the event is cancelled the time will not change.
|
||||
*/
|
||||
public class TimeSkipEvent extends ClockTimeSkipEvent {
|
||||
// TODO - snapshot - 26.1 API needed for clock!
|
||||
public class TimeSkipEvent extends WorldEvent implements Cancellable {
|
||||
|
||||
private static final HandlerList HANDLER_LIST = new HandlerList();
|
||||
|
||||
private final World world;
|
||||
private final SkipReason skipReason;
|
||||
private long skipAmount;
|
||||
|
||||
private boolean cancelled;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public TimeSkipEvent(@NotNull World world, @NotNull WorldClock clock, @NotNull SkipReason skipReason, long skipAmount) {
|
||||
super(clock, skipReason, skipAmount);
|
||||
this.world = world;
|
||||
public TimeSkipEvent(@NotNull World world, @NotNull SkipReason skipReason, long skipAmount) {
|
||||
super(world);
|
||||
this.skipReason = skipReason;
|
||||
this.skipAmount = skipAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the world that time is skipped in.
|
||||
* Gets the reason why the time has skipped.
|
||||
*
|
||||
* @return world that time is skipped in
|
||||
* @return a SkipReason value detailing why the time has skipped
|
||||
*/
|
||||
@NotNull
|
||||
public World getWorld() {
|
||||
return world;
|
||||
public SkipReason getSkipReason() {
|
||||
return this.skipReason;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the amount of time that was skipped.
|
||||
*
|
||||
* @return Amount of time skipped
|
||||
*/
|
||||
public long getSkipAmount() {
|
||||
return this.skipAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the amount of time to skip.
|
||||
*
|
||||
* @param skipAmount Amount of time to skip
|
||||
*/
|
||||
public void setSkipAmount(long skipAmount) {
|
||||
this.skipAmount = skipAmount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return this.cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancel) {
|
||||
this.cancelled = cancel;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -43,4 +76,24 @@ public class TimeSkipEvent extends ClockTimeSkipEvent {
|
||||
public static HandlerList getHandlerList() {
|
||||
return HANDLER_LIST;
|
||||
}
|
||||
|
||||
/**
|
||||
* An enum specifying the reason the time skipped.
|
||||
*/
|
||||
public enum SkipReason {
|
||||
|
||||
/**
|
||||
* When time is changed using the vanilla /time command.
|
||||
*/
|
||||
COMMAND,
|
||||
/**
|
||||
* When time is changed by a plugin.
|
||||
*/
|
||||
CUSTOM,
|
||||
/**
|
||||
* When time is changed by all players sleeping in their beds and the
|
||||
* night skips.
|
||||
*/
|
||||
NIGHT_SKIP
|
||||
}
|
||||
}
|
||||
@@ -133,26 +133,74 @@ public class PotionEffect implements ConfigurationSerializable {
|
||||
}
|
||||
|
||||
// Paper start
|
||||
/**
|
||||
* Returns a new potion effect with the provided type and this effect's
|
||||
* duration, amplifier, ambient, particles, and icon.
|
||||
*
|
||||
* @param type effect type
|
||||
* @return a new potion effect with the provided type
|
||||
*/
|
||||
@NotNull
|
||||
public PotionEffect withType(@NotNull PotionEffectType type) {
|
||||
return new PotionEffect(type, duration, amplifier, ambient, particles, icon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new potion effect with the provided duration and this effect's
|
||||
* type, amplifier, ambient, particles, and icon.
|
||||
*
|
||||
* @param duration measured in ticks, see {@link
|
||||
* PotionEffect#getDuration()}
|
||||
* @return a new potion effect with the provided duration
|
||||
*/
|
||||
@NotNull
|
||||
public PotionEffect withDuration(int duration) {
|
||||
return new PotionEffect(this.type, duration, amplifier, ambient, particles, icon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new potion effect with the provided amplifier and this effect's
|
||||
* type, duration, ambient, particles, and icon.
|
||||
*
|
||||
* @param amplifier the amplifier, see {@link PotionEffect#getAmplifier()}
|
||||
* @return a new potion effect with the provided amplifier
|
||||
*/
|
||||
@NotNull
|
||||
public PotionEffect withAmplifier(int amplifier) {
|
||||
return new PotionEffect(this.type, duration, amplifier, ambient, particles, icon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new potion effect with the provided ambient and this effect's
|
||||
* type, duration, amplifier, particles, and icon.
|
||||
*
|
||||
* @param ambient the ambient status, see {@link PotionEffect#isAmbient()}
|
||||
* @return a new potion effect with the provided ambient
|
||||
*/
|
||||
@NotNull
|
||||
public PotionEffect withAmbient(boolean ambient) {
|
||||
return new PotionEffect(this.type, duration, amplifier, ambient, particles, icon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new potion effect with the provided particles and this effect's
|
||||
* type, duration, amplifier, ambient, and icon.
|
||||
*
|
||||
* @param particles the particle status, see {@link PotionEffect#hasParticles()}
|
||||
* @return a new potion effect with the provided particles
|
||||
*/
|
||||
@NotNull
|
||||
public PotionEffect withParticles(boolean particles) {
|
||||
return new PotionEffect(this.type, duration, amplifier, ambient, particles, icon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new potion effect with the provided icon and this effect's
|
||||
* type, duration, amplifier, ambient, and particles.
|
||||
*
|
||||
* @param icon the icon status, see {@link PotionEffect#hasIcon()}
|
||||
* @return a new potion effect with the provided icon
|
||||
*/
|
||||
@NotNull
|
||||
public PotionEffect withIcon(boolean icon) {
|
||||
return new PotionEffect(this.type, duration, amplifier, ambient, particles, icon);
|
||||
|
||||
@@ -27,6 +27,7 @@ import io.papermc.paper.datacomponent.item.SwingAnimation;
|
||||
import io.papermc.paper.datacomponent.item.consumable.ItemUseAnimation;
|
||||
import io.papermc.paper.dialog.Dialog;
|
||||
import io.papermc.paper.item.MapPostProcessing;
|
||||
import io.papermc.paper.entity.poi.PoiTypes;
|
||||
import io.papermc.paper.world.WeatheringCopperState;
|
||||
import io.papermc.typewriter.preset.EnumCloneRewriter;
|
||||
import io.papermc.typewriter.preset.model.EnumValue;
|
||||
@@ -231,6 +232,7 @@ public final class Rewriters {
|
||||
.register("PigSoundVariant", Pig.SoundVariant.class, new RegistryFieldRewriter<>(Registries.PIG_SOUND_VARIANT, "getSoundVariant"))
|
||||
.register("ZombieNautilusVariant", ZombieNautilus.Variant.class, new RegistryFieldRewriter<>(Registries.ZOMBIE_NAUTILUS_VARIANT, "getVariant"))
|
||||
.register("Dialog", Dialog.class, new RegistryFieldRewriter<>(Registries.DIALOG, "getDialog"))
|
||||
.register("PoiTypes", PoiTypes.class, new RegistryFieldRewriter<>(Registries.POINT_OF_INTEREST_TYPE, "get"))
|
||||
.register("MemoryKey", MemoryKey.class, new MemoryKeyRewriter())
|
||||
// .register("ItemType", org.bukkit.inventory.ItemType.class, new io.papermc.generator.rewriter.types.simple.ItemTypeRewriter()) - disable for now, lynx want the generic type
|
||||
.register("BlockType", BlockType.class, new BlockTypeRewriter())
|
||||
|
||||
@@ -4,6 +4,7 @@ import io.papermc.generator.utils.ClassHelper;
|
||||
import io.papermc.paper.datacomponent.DataComponentType;
|
||||
import io.papermc.paper.datacomponent.DataComponentTypes;
|
||||
import io.papermc.paper.dialog.Dialog;
|
||||
import io.papermc.paper.entity.poi.PoiTypes;
|
||||
import io.papermc.paper.registry.data.BannerPatternRegistryEntry;
|
||||
import io.papermc.paper.registry.data.CatTypeRegistryEntry;
|
||||
import io.papermc.paper.registry.data.ChickenVariantRegistryEntry;
|
||||
@@ -17,13 +18,9 @@ import io.papermc.paper.registry.data.JukeboxSongRegistryEntry;
|
||||
import io.papermc.paper.registry.data.PaintingVariantRegistryEntry;
|
||||
import io.papermc.paper.registry.data.PigVariantRegistryEntry;
|
||||
import io.papermc.paper.registry.data.SoundEventRegistryEntry;
|
||||
import io.papermc.paper.registry.data.TimelineRegistryEntry;
|
||||
import io.papermc.paper.registry.data.WolfVariantRegistryEntry;
|
||||
import io.papermc.paper.registry.data.WorldClockRegistryEntry;
|
||||
import io.papermc.paper.registry.data.ZombieNautilusVariantRegistryEntry;
|
||||
import io.papermc.paper.registry.data.dialog.DialogRegistryEntry;
|
||||
import io.papermc.paper.world.Timeline;
|
||||
import io.papermc.paper.world.WorldClock;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Type;
|
||||
@@ -69,7 +66,6 @@ import net.minecraft.world.item.alchemy.Potions;
|
||||
import net.minecraft.world.item.enchantment.Enchantments;
|
||||
import net.minecraft.world.item.equipment.trim.TrimMaterials;
|
||||
import net.minecraft.world.item.equipment.trim.TrimPatterns;
|
||||
import net.minecraft.world.clock.WorldClocks;
|
||||
import net.minecraft.world.level.biome.Biomes;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.entity.BannerPatterns;
|
||||
@@ -77,7 +73,6 @@ import net.minecraft.world.level.gamerules.GameRules;
|
||||
import net.minecraft.world.level.levelgen.structure.BuiltinStructures;
|
||||
import net.minecraft.world.level.material.Fluids;
|
||||
import net.minecraft.world.level.saveddata.maps.MapDecorationTypes;
|
||||
import net.minecraft.world.timeline.Timelines;
|
||||
import org.bukkit.Art;
|
||||
import org.bukkit.Fluid;
|
||||
import org.bukkit.GameEvent;
|
||||
@@ -181,7 +176,8 @@ public final class RegistryEntries {
|
||||
entry(Registries.FLUID, Fluids.class, Fluid.class),
|
||||
entry(Registries.SOUND_EVENT, SoundEvents.class, Sound.class).allowDirect().apiRegistryField("SOUNDS").apiRegistryBuilder(SoundEventRegistryEntry.Builder.class, "PaperSoundEventRegistryEntry.PaperBuilder", RegistryEntry.RegistryModificationApiSupport.NONE),
|
||||
entry(Registries.DATA_COMPONENT_TYPE, DataComponents.class, DataComponentType.class, "Paper").preload(DataComponentTypes.class).apiAccessName("of"),
|
||||
entry(Registries.GAME_RULE, GameRules.class, GameRule.class).genericArgCount(1)/*.preload(org.bukkit.GameRules.class)*/ // only preload once the old names are removed
|
||||
entry(Registries.GAME_RULE, GameRules.class, GameRule.class).genericArgCount(1)/*.preload(org.bukkit.GameRules.class)*/, // only preload once the old names are removed
|
||||
entry(Registries.POINT_OF_INTEREST_TYPE, PoiTypes.class, io.papermc.paper.entity.poi.PoiType.class, "Paper").preload(io.papermc.paper.entity.poi.PoiTypes.class)
|
||||
);
|
||||
|
||||
public static final List<RegistryEntry<?>> DATA_DRIVEN = List.of(
|
||||
@@ -207,9 +203,7 @@ public final class RegistryEntries {
|
||||
entry(Registries.PIG_VARIANT, PigVariants.class, Pig.Variant.class).writableApiRegistryBuilder(PigVariantRegistryEntry.Builder.class, "PaperPigVariantRegistryEntry.PaperBuilder"),
|
||||
entry(Registries.PIG_SOUND_VARIANT, PigSoundVariants.class, Pig.SoundVariant.class),
|
||||
entry(Registries.ZOMBIE_NAUTILUS_VARIANT, ZombieNautilusVariants.class, ZombieNautilus.Variant.class).writableApiRegistryBuilder(ZombieNautilusVariantRegistryEntry.Builder.class, "PaperZombieNautilusVariantRegistryEntry.PaperBuilder"),
|
||||
entry(Registries.DIALOG, Dialogs.class, Dialog.class, "Paper").allowDirect().writableApiRegistryBuilder(DialogRegistryEntry.Builder.class, "PaperDialogRegistryEntry.PaperBuilder"),
|
||||
entry(Registries.WORLD_CLOCK, WorldClocks.class, WorldClock.class).writableApiRegistryBuilder(WorldClockRegistryEntry.Builder.class, "PaperWorldClockRegistryEntry.PaperBuilder").delayed(),
|
||||
entry(Registries.TIMELINE, Timelines.class, Timeline.class).writableApiRegistryBuilder(TimelineRegistryEntry.Builder.class, "PaperTimelineRegistryEntry.PaperBuilder").delayed()
|
||||
entry(Registries.DIALOG, Dialogs.class, Dialog.class, "Paper").allowDirect().writableApiRegistryBuilder(DialogRegistryEntry.Builder.class, "PaperDialogRegistryEntry.PaperBuilder")
|
||||
);
|
||||
|
||||
public static final List<RegistryEntry<?>> API_ONLY = List.of(
|
||||
|
||||
@@ -23838,7 +23838,7 @@ index 3c1490ac7c259da04031db2f170e0c0a5f512191..470d7c770ae9d045b97e2df145cfe3cf
|
||||
}
|
||||
}
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index 2d9106648f8921ef60c2a24e166d17eb8ce01b48..cea3bd30d326ece39298262401681d77fe064464 100644
|
||||
index c11e2ce21e43f677bfccb65e6b5ad374912381fa..9aae782f8f9f1c0f7eae394ec0a1bda79e93f6f4 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -191,7 +191,7 @@ import net.minecraft.world.scores.ScoreboardSaveData;
|
||||
@@ -23943,7 +23943,7 @@ index 2d9106648f8921ef60c2a24e166d17eb8ce01b48..cea3bd30d326ece39298262401681d77
|
||||
|
||||
public MinecraftServer(
|
||||
// CraftBukkit start
|
||||
@@ -843,7 +929,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -853,7 +939,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
level.setSpawnSettings(level.isSpawningMonsters()); // Paper - per level difficulty (from setDifficulty(ServerLevel, Difficulty, boolean))
|
||||
this.updateEffectiveRespawnData();
|
||||
this.forceTicks = false; // CraftBukkit
|
||||
@@ -23952,7 +23952,7 @@ index 2d9106648f8921ef60c2a24e166d17eb8ce01b48..cea3bd30d326ece39298262401681d77
|
||||
new org.bukkit.event.world.WorldLoadEvent(level.getWorld()).callEvent(); // Paper - call WorldLoadEvent
|
||||
}
|
||||
|
||||
@@ -866,6 +952,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -876,6 +962,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
public abstract boolean shouldRconBroadcast();
|
||||
|
||||
public boolean saveAllChunks(final boolean silent, final boolean flush, final boolean force) {
|
||||
@@ -23964,7 +23964,7 @@ index 2d9106648f8921ef60c2a24e166d17eb8ce01b48..cea3bd30d326ece39298262401681d77
|
||||
this.scoreboard.storeToSaveDataIfDirty(this.getDataStorage().computeIfAbsent(ScoreboardSaveData.TYPE));
|
||||
boolean result = false;
|
||||
|
||||
@@ -874,7 +965,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -884,7 +975,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
LOGGER.info("Saving chunks for level '{}'/{}", level, level.dimension().identifier());
|
||||
}
|
||||
|
||||
@@ -23973,7 +23973,7 @@ index 2d9106648f8921ef60c2a24e166d17eb8ce01b48..cea3bd30d326ece39298262401681d77
|
||||
result = true;
|
||||
}
|
||||
|
||||
@@ -971,7 +1062,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -981,7 +1072,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23982,7 +23982,7 @@ index 2d9106648f8921ef60c2a24e166d17eb8ce01b48..cea3bd30d326ece39298262401681d77
|
||||
this.nextTickTimeNanos = Util.getNanos() + TimeUtil.NANOSECONDS_PER_MILLISECOND;
|
||||
|
||||
for (ServerLevel levelx : this.getAllLevels()) {
|
||||
@@ -982,17 +1073,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -992,17 +1083,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
this.waitUntilNextTick();
|
||||
}
|
||||
|
||||
@@ -24007,7 +24007,7 @@ index 2d9106648f8921ef60c2a24e166d17eb8ce01b48..cea3bd30d326ece39298262401681d77
|
||||
|
||||
this.isSaving = false;
|
||||
this.savedDataStorage.close();
|
||||
@@ -1013,6 +1101,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -1023,6 +1111,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
this.services().nameToIdCache().save(false); // Paper - Perf: Async GameProfileCache saving
|
||||
}
|
||||
// Spigot end
|
||||
@@ -24022,7 +24022,7 @@ index 2d9106648f8921ef60c2a24e166d17eb8ce01b48..cea3bd30d326ece39298262401681d77
|
||||
// Paper start - Improved watchdog support - move final shutdown items here
|
||||
Util.shutdownExecutors();
|
||||
this.onServerExit();
|
||||
@@ -1107,16 +1203,31 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -1117,16 +1213,31 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
// execute small amounts of other tasks just in case the number of tasks we are
|
||||
// draining is large - chunk system and packet processing may be latency sensitive
|
||||
|
||||
@@ -24057,7 +24057,7 @@ index 2d9106648f8921ef60c2a24e166d17eb8ce01b48..cea3bd30d326ece39298262401681d77
|
||||
profiler.pop(); // moonrise:run_all_chunk
|
||||
profiler.pop(); // moonrise:run_all_tasks
|
||||
|
||||
@@ -1417,6 +1528,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -1427,6 +1538,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
|
||||
private boolean pollTaskInternal() {
|
||||
if (super.pollTask()) {
|
||||
@@ -24065,7 +24065,7 @@ index 2d9106648f8921ef60c2a24e166d17eb8ce01b48..cea3bd30d326ece39298262401681d77
|
||||
return true;
|
||||
} else {
|
||||
boolean ret = false; // Paper - force execution of all worlds, do not just bias the first
|
||||
@@ -1558,6 +1670,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -1568,6 +1680,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
// Paper - improve tick loop - moved into runAllTasksAtTickStart
|
||||
this.runAllTasksAtTickStart(); // Paper - improve tick loop
|
||||
this.tickServer(sprinting ? () -> false : this::haveTime);
|
||||
@@ -24079,7 +24079,7 @@ index 2d9106648f8921ef60c2a24e166d17eb8ce01b48..cea3bd30d326ece39298262401681d77
|
||||
this.tickFrame.end();
|
||||
this.recordEndOfTick(); // Paper - improve tick loop
|
||||
profiler.pop();
|
||||
@@ -2600,6 +2719,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -2610,6 +2729,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27004,7 +27004,7 @@ index de75dda2e6f17f0ae6e7f6a5620fd3eac9d389e5..6774d12d04fa848624fb8bf7a3c2b8bc
|
||||
List<Entity> passengers = this.entity.getPassengers();
|
||||
if (!passengers.equals(this.lastPassengers)) {
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01dee9c2a60 100644
|
||||
index 5dc8f5d3738bb3b97e0a0d98fcec4fda44d741a3..0b7fb9e17a43d2eddd96a55e422626c3bb953056 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -182,7 +182,7 @@ import net.minecraft.world.waypoints.WaypointTransmitter;
|
||||
@@ -27420,7 +27420,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
|
||||
this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit
|
||||
}
|
||||
|
||||
@@ -615,8 +873,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -607,8 +865,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
profiler.push("checkDespawn");
|
||||
entity.checkDespawn();
|
||||
profiler.pop();
|
||||
@@ -27430,7 +27430,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
|
||||
Entity vehicle = entity.getVehicle();
|
||||
if (vehicle != null) {
|
||||
if (!vehicle.isRemoved() && vehicle.hasPassenger(entity)) {
|
||||
@@ -640,7 +897,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -632,7 +889,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
}
|
||||
|
||||
profiler.push("entityManagement");
|
||||
@@ -27439,7 +27439,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
|
||||
profiler.pop();
|
||||
profiler.push("debugSynchronizers");
|
||||
if (this.debugSynchronizers.hasAnySubscriberFor(DebugSubscriptions.NEIGHBOR_UPDATES)) {
|
||||
@@ -657,7 +914,10 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -649,7 +906,10 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
|
||||
@Override
|
||||
public boolean shouldTickBlocksAt(final long chunkPos) {
|
||||
@@ -27451,7 +27451,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
|
||||
}
|
||||
|
||||
protected void tickTime() {
|
||||
@@ -681,7 +941,60 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -673,7 +933,60 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
this.players.stream().filter(LivingEntity::isSleeping).collect(Collectors.toList()).forEach(player -> player.stopSleepInBed(false, false));
|
||||
}
|
||||
|
||||
@@ -27512,7 +27512,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
|
||||
ChunkPos chunkPos = chunk.getPos();
|
||||
int minX = chunkPos.getMinBlockX();
|
||||
int minZ = chunkPos.getMinBlockZ();
|
||||
@@ -690,7 +1003,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -682,7 +995,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
|
||||
if (!this.paperConfig().environment.disableIceAndSnow) { // Paper - Option to disable ice and snow
|
||||
for (int i = 0; i < tickSpeed; i++) {
|
||||
@@ -27521,7 +27521,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
|
||||
this.tickPrecipitation(this.getBlockRandomPos(minX, 0, minZ, 15));
|
||||
}
|
||||
}
|
||||
@@ -698,31 +1011,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -690,31 +1003,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
|
||||
profiler.popPush("tickBlocks");
|
||||
if (tickSpeed > 0) {
|
||||
@@ -27554,7 +27554,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
|
||||
}
|
||||
|
||||
profiler.pop();
|
||||
@@ -1045,6 +1334,12 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -1037,6 +1326,12 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
if (fluidState.is(type)) {
|
||||
fluidState.tick(this, pos, blockState);
|
||||
}
|
||||
@@ -27567,7 +27567,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
|
||||
}
|
||||
|
||||
private void tickBlock(final BlockPos pos, final Block type) {
|
||||
@@ -1052,6 +1347,12 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -1044,6 +1339,12 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
if (state.is(type)) {
|
||||
state.tick(this, pos, this.random);
|
||||
}
|
||||
@@ -27580,7 +27580,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
|
||||
}
|
||||
|
||||
// Paper start - log detailed entity tick information
|
||||
@@ -1136,6 +1437,11 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -1128,6 +1429,11 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
}
|
||||
|
||||
public void save(final @Nullable ProgressListener progressListener, final boolean flush, final boolean noSave) {
|
||||
@@ -27592,7 +27592,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
|
||||
ServerChunkCache chunkSource = this.getChunkSource();
|
||||
if (!noSave) {
|
||||
new org.bukkit.event.world.WorldSaveEvent(this.getWorld()).callEvent(); // CraftBukkit
|
||||
@@ -1148,13 +1454,18 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -1140,13 +1446,18 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
progressListener.progressStage(Component.translatable("menu.savingChunks"));
|
||||
}
|
||||
|
||||
@@ -27616,7 +27616,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
|
||||
}
|
||||
|
||||
private void saveLevelData(final boolean sync) {
|
||||
@@ -1275,7 +1586,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -1267,7 +1578,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
this.removePlayerImmediately((ServerPlayer)existing, Entity.RemovalReason.DISCARDED);
|
||||
}
|
||||
|
||||
@@ -27625,7 +27625,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
|
||||
}
|
||||
|
||||
// CraftBukkit start
|
||||
@@ -1306,7 +1617,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -1298,7 +1609,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
@@ -27634,7 +27634,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1317,7 +1628,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -1309,7 +1620,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
|
||||
public boolean tryAddFreshEntityWithPassengers(final Entity entity, final org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) {
|
||||
// CraftBukkit end
|
||||
@@ -27643,7 +27643,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
|
||||
return false;
|
||||
} else {
|
||||
this.addFreshEntityWithPassengers(entity, reason); // CraftBukkit
|
||||
@@ -2111,7 +2422,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -2102,7 +2413,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27652,7 +27652,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
|
||||
output.write(String.format(Locale.ROOT, "block_entity_tickers: %d\n", this.blockEntityTickers.size()));
|
||||
output.write(String.format(Locale.ROOT, "block_ticks: %d\n", this.getBlockTicks().count()));
|
||||
output.write(String.format(Locale.ROOT, "fluid_ticks: %d\n", this.getFluidTicks().count()));
|
||||
@@ -2129,13 +2440,13 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -2120,13 +2431,13 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
Path chunks = rootDir.resolve("chunks.csv");
|
||||
|
||||
try (Writer output = Files.newBufferedWriter(chunks)) {
|
||||
@@ -27668,7 +27668,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
|
||||
}
|
||||
|
||||
Path entities = rootDir.resolve("entities.csv");
|
||||
@@ -2230,8 +2541,8 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -2221,8 +2532,8 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
Locale.ROOT,
|
||||
"players: %s, entities: %s [%s], block_entities: %d [%s], block_ticks: %d, fluid_ticks: %d, chunk_source: %s",
|
||||
this.players.size(),
|
||||
@@ -27679,7 +27679,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
|
||||
this.blockEntityTickers.size(),
|
||||
getTypeCount(this.blockEntityTickers, TickingBlockEntity::getType),
|
||||
this.getBlockTicks().count(),
|
||||
@@ -2264,15 +2575,25 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -2255,15 +2566,25 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@Override
|
||||
public LevelEntityGetter<Entity> getEntities() {
|
||||
org.spigotmc.AsyncCatcher.catchOp("Chunk getEntities call"); // Spigot
|
||||
@@ -27708,7 +27708,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
|
||||
}
|
||||
|
||||
public void startTickingChunk(final LevelChunk levelChunk) {
|
||||
@@ -2289,8 +2610,8 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -2280,8 +2601,8 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
|
||||
public void waitForEntities(final ChunkPos centerChunk, final int radius) {
|
||||
List<ChunkPos> chunks = ChunkPos.rangeClosed(centerChunk, radius).toList();
|
||||
@@ -27719,7 +27719,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
|
||||
|
||||
for (ChunkPos chunk : chunks) {
|
||||
if (!this.areEntitiesLoaded(chunk.pack())) {
|
||||
@@ -2311,28 +2632,38 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -2302,28 +2623,38 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
super.close();
|
||||
@@ -27764,7 +27764,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
|
||||
}
|
||||
|
||||
public boolean anyPlayerCloseEnoughForSpawning(final BlockPos pos) {
|
||||
@@ -2349,7 +2680,10 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -2340,7 +2671,10 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
}
|
||||
|
||||
public boolean canSpawnEntitiesInChunk(final ChunkPos pos) {
|
||||
@@ -27776,7 +27776,7 @@ index e17e391862a6b888ed0d0a3c4ab136d9ac47375d..ed52a5e099b74b71ae05f21a4ad0d01d
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2397,7 +2731,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -2388,7 +2722,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
public CrashReportCategory fillReportDetails(final CrashReport report) {
|
||||
CrashReportCategory category = super.fillReportDetails(report);
|
||||
WeatherData weatherData = this.getWeatherData();
|
||||
@@ -28893,7 +28893,7 @@ index 1ceb7e4a3abd4d9de5133d182d3267d2164918f6..eb2fa32cff6824c14f865c8731df7d08
|
||||
+ // Paper end - block counting
|
||||
}
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index 3d7bc43e3e367a2328601ac6b74f99361b4be57e..f7b4da570ee28b7128b4d2cd1c67b89e3980bced 100644
|
||||
index 29896cb17fcf8ab967a937e9b0e102a8354b6889..e087d5596979044fe7fbcf7f2cccdae4e81a3d3a 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -167,7 +167,7 @@ public abstract class Entity
|
||||
@@ -29366,7 +29366,7 @@ index 3d7bc43e3e367a2328601ac6b74f99361b4be57e..f7b4da570ee28b7128b4d2cd1c67b89e
|
||||
}
|
||||
|
||||
public InteractionResult interact(final Player player, final InteractionHand hand, final Vec3 location) {
|
||||
@@ -4364,15 +4601,17 @@ public abstract class Entity
|
||||
@@ -4363,15 +4600,17 @@ public abstract class Entity
|
||||
}
|
||||
|
||||
public Iterable<Entity> getIndirectPassengers() {
|
||||
@@ -29392,7 +29392,7 @@ index 3d7bc43e3e367a2328601ac6b74f99361b4be57e..f7b4da570ee28b7128b4d2cd1c67b89e
|
||||
}
|
||||
|
||||
public int countPlayerPassengers() {
|
||||
@@ -4683,6 +4922,15 @@ public abstract class Entity
|
||||
@@ -4682,6 +4921,15 @@ public abstract class Entity
|
||||
}
|
||||
|
||||
public final void setPosRaw(double x, double y, double z, boolean forceBoundingBoxUpdate) {
|
||||
@@ -29408,7 +29408,7 @@ index 3d7bc43e3e367a2328601ac6b74f99361b4be57e..f7b4da570ee28b7128b4d2cd1c67b89e
|
||||
if (!checkPosition(this, x, y, z)) {
|
||||
return;
|
||||
}
|
||||
@@ -4832,6 +5080,12 @@ public abstract class Entity
|
||||
@@ -4831,6 +5079,12 @@ public abstract class Entity
|
||||
|
||||
@Override
|
||||
public final void setRemoved(final Entity.RemovalReason reason, org.bukkit.event.entity.EntityRemoveEvent.@Nullable Cause cause) { // CraftBukkit - add Bukkit remove cause
|
||||
@@ -29421,7 +29421,7 @@ index 3d7bc43e3e367a2328601ac6b74f99361b4be57e..f7b4da570ee28b7128b4d2cd1c67b89e
|
||||
org.bukkit.craftbukkit.event.CraftEventFactory.callEntityRemoveEvent(this, cause); // CraftBukkit
|
||||
final boolean alreadyRemoved = this.removalReason != null; // Paper - Folia schedulers
|
||||
if (this.removalReason == null) {
|
||||
@@ -4842,7 +5096,7 @@ public abstract class Entity
|
||||
@@ -4841,7 +5095,7 @@ public abstract class Entity
|
||||
this.stopRiding();
|
||||
}
|
||||
|
||||
@@ -29430,7 +29430,7 @@ index 3d7bc43e3e367a2328601ac6b74f99361b4be57e..f7b4da570ee28b7128b4d2cd1c67b89e
|
||||
this.levelCallback.onRemove(reason);
|
||||
this.onRemoval(reason);
|
||||
// Paper start - Folia schedulers
|
||||
@@ -4876,7 +5130,7 @@ public abstract class Entity
|
||||
@@ -4875,7 +5129,7 @@ public abstract class Entity
|
||||
public boolean shouldBeSaved() {
|
||||
return (this.removalReason == null || this.removalReason.shouldSave())
|
||||
&& !this.isPassenger()
|
||||
@@ -30053,7 +30053,7 @@ index e07a7bda45146686a6ac2d20507df9fc8630514f..f84e651e2a34e76a0a71993332e4be1b
|
||||
|
||||
// Paper start - Affects Spawning API
|
||||
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
|
||||
index 506efca7cec0e268f03c6c070d69afbc853aaf03..995b51d859316c6128b30d930e58dd47224514b2 100644
|
||||
index 30183d8752aa397a5c1fc554da49f595be286534..5566f449fb79b04822e533df4fd6de42116ffd14 100644
|
||||
--- a/net/minecraft/world/level/Level.java
|
||||
+++ b/net/minecraft/world/level/Level.java
|
||||
@@ -87,6 +87,7 @@ import net.minecraft.world.level.storage.LevelData;
|
||||
@@ -30941,7 +30941,7 @@ index 506efca7cec0e268f03c6c070d69afbc853aaf03..995b51d859316c6128b30d930e58dd47
|
||||
|
||||
public <T extends Entity> boolean hasEntities(final EntityTypeTest<Entity, T> type, final AABB bb, final Predicate<? super T> selector) {
|
||||
Profiler.get().incrementCounter("hasEntities");
|
||||
@@ -1469,13 +2173,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
||||
@@ -1465,13 +2169,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
||||
|
||||
// Paper start - allow patching this logic
|
||||
public final int getEntityCount() {
|
||||
@@ -33593,7 +33593,7 @@ index cc5d366f7b485f98901572b1aaf57d8b650d521f..7adb4be57bd9fdc9980b5b7e91e9d9ca
|
||||
private final Long2ObjectLinkedOpenHashMap<CompletableFuture<BitSet>> regionCacheForBlender = new Long2ObjectLinkedOpenHashMap<>();
|
||||
private static final int REGION_CACHE_SIZE = 1024;
|
||||
diff --git a/net/minecraft/world/level/chunk/storage/RegionFile.java b/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
index 728ec122b7af090427cc7511a168336d539835a1..5149635679ec03d881e63effbd79fc193d7e52c8 100644
|
||||
index 9409cc5c8dbd12ce6bdf34843da70170ebce41bd..c9acbdce97cb605a45124bb9d3944070396adc03 100644
|
||||
--- a/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
@@ -23,7 +23,7 @@ import net.minecraft.world.level.ChunkPos;
|
||||
@@ -33625,7 +33625,7 @@ index 728ec122b7af090427cc7511a168336d539835a1..5149635679ec03d881e63effbd79fc19
|
||||
+ // Paper end - rewrite chunk system
|
||||
+
|
||||
public RegionFile(final RegionStorageInfo info, final Path path, final Path externalFileDir, final boolean sync) throws IOException {
|
||||
this(info, path, externalFileDir, RegionFileVersion.getCompressionFormat(), sync); // Paper - Configurable region compression format
|
||||
this(info, path, externalFileDir, RegionFileVersion.getSelected(), sync);
|
||||
}
|
||||
@@ -201,6 +216,15 @@ public class RegionFile implements AutoCloseable {
|
||||
}
|
||||
|
||||
@@ -33491,7 +33491,7 @@ index 109d7b563ee799d998a5773c5a36e61ca3607d46..747755082639436731989d3ee4b3c30a
|
||||
return structureTemplate.save(new CompoundTag());
|
||||
}
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index 1b064f00c33712c3d2d85ed8520f1eda02abfbf9..65a971379f35c4b1b519b93787f690d891e29523 100644
|
||||
index e11862c7e0617acfb36212cfe349bdade3a5164b..7ae2d491a3b2f379df99ccc649b8bb8cbf2304b9 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -299,6 +299,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
|
||||
@@ -354,10 +354,10 @@ index 0000000000000000000000000000000000000000..ce6b57eeeeb1bd652f4bb53c19dcfbc0
|
||||
+ }
|
||||
+}
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index ed52a5e099b74b71ae05f21a4ad0d01dee9c2a60..9f43d29aa4fecb03d6ef49e71e4de846dca187de 100644
|
||||
index 3cd2fcc38f3989bb5124ba5bc2f4597103e7a111..6d847ff3646b27fe9bc75e792d813a7f611ab3e5 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -865,6 +865,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -857,6 +857,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
profiler.pop();
|
||||
}
|
||||
|
||||
@@ -365,7 +365,7 @@ index ed52a5e099b74b71ae05f21a4ad0d01dee9c2a60..9f43d29aa4fecb03d6ef49e71e4de846
|
||||
this.entityTickList
|
||||
.forEach(
|
||||
entity -> {
|
||||
@@ -1381,12 +1382,15 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -1373,12 +1374,15 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
entity.totalEntityAge++; // Paper - age-like counter for all entities
|
||||
profiler.push(entity.typeHolder()::getRegisteredName);
|
||||
profiler.incrementCounter("tickNonPassenger");
|
||||
@@ -382,7 +382,7 @@ index ed52a5e099b74b71ae05f21a4ad0d01dee9c2a60..9f43d29aa4fecb03d6ef49e71e4de846
|
||||
}
|
||||
// Paper start - log detailed entity tick information
|
||||
} finally {
|
||||
@@ -1397,7 +1401,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -1389,7 +1393,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
// Paper end - log detailed entity tick information
|
||||
}
|
||||
|
||||
@@ -391,7 +391,7 @@ index ed52a5e099b74b71ae05f21a4ad0d01dee9c2a60..9f43d29aa4fecb03d6ef49e71e4de846
|
||||
if (entity.isRemoved() || entity.getVehicle() != vehicle) {
|
||||
entity.stopRiding();
|
||||
} else if (entity instanceof Player || this.entityTickList.contains(entity)) {
|
||||
@@ -1407,12 +1411,21 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -1399,12 +1403,21 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
ProfilerFiller profiler = Profiler.get();
|
||||
profiler.push(entity.typeHolder()::getRegisteredName);
|
||||
profiler.incrementCounter("tickPassenger");
|
||||
@@ -462,7 +462,7 @@ index 0df8332933203a904bd9ef9efb3c9bce21e65441..1a502cbd8acea9420fa6dd8d716018b5
|
||||
public void tick() {
|
||||
super.tick();
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index f7b4da570ee28b7128b4d2cd1c67b89e3980bced..93d3fb313d538cc1067b1db010458169a1320122 100644
|
||||
index e087d5596979044fe7fbcf7f2cccdae4e81a3d3a..fea6c3b48c4eb162fbdc099fa775bc3161a4dd4e 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -382,6 +382,15 @@ public abstract class Entity
|
||||
@@ -540,10 +540,10 @@ index c4da41f3b5ba6d8492d3776654b1ce62a5015895..6a0ebbdfcc0bc07915574c1cf87463e8
|
||||
public void tick() {
|
||||
super.tick();
|
||||
diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java
|
||||
index c7c53ef16e104ebc48aeb8719783fb4c45d39fc2..90eaa775dc134041647618c3b965334a9bb9fd3b 100644
|
||||
index c7c53ef16e104ebc48aeb8719783fb4c45d39fc2..5bbb5cbb51953399c7bce547cd6968eb3c6bd130 100644
|
||||
--- a/net/minecraft/world/entity/Mob.java
|
||||
+++ b/net/minecraft/world/entity/Mob.java
|
||||
@@ -211,6 +211,19 @@ public abstract class Mob extends LivingEntity implements Targeting, EquipmentUs
|
||||
@@ -211,6 +211,20 @@ public abstract class Mob extends LivingEntity implements Targeting, EquipmentUs
|
||||
return this.lookControl;
|
||||
}
|
||||
|
||||
@@ -551,6 +551,7 @@ index c7c53ef16e104ebc48aeb8719783fb4c45d39fc2..90eaa775dc134041647618c3b965334a
|
||||
+ @Override
|
||||
+ public void inactiveTick() {
|
||||
+ super.inactiveTick();
|
||||
+ if (!this.aware) return; // Paper - Do not tick AI for inactive unaware mobs
|
||||
+ if (this.goalSelector.inactiveTick()) {
|
||||
+ this.goalSelector.tick();
|
||||
+ }
|
||||
@@ -673,7 +674,7 @@ index c482c3348267ca2b88109e34291aa860e0cde916..b11b59d80e37d272770ab51a63028fdd
|
||||
public void tick() {
|
||||
if (this.getItem().isEmpty()) {
|
||||
diff --git a/net/minecraft/world/entity/npc/villager/Villager.java b/net/minecraft/world/entity/npc/villager/Villager.java
|
||||
index a80e6a23cf29cb44943339101f15565dbc19af1f..f481fd661440f77c98940cbde4a8b95818e4a22c 100644
|
||||
index a80e6a23cf29cb44943339101f15565dbc19af1f..26108ac0543c7b4195f149c07d8348b5b15b8bdb 100644
|
||||
--- a/net/minecraft/world/entity/npc/villager/Villager.java
|
||||
+++ b/net/minecraft/world/entity/npc/villager/Villager.java
|
||||
@@ -244,11 +244,35 @@ public class Villager extends AbstractVillager implements VillagerDataHolder, Re
|
||||
@@ -687,7 +688,7 @@ index a80e6a23cf29cb44943339101f15565dbc19af1f..f481fd661440f77c98940cbde4a8b958
|
||||
+ if (this.getUnhappyCounter() > 0) {
|
||||
+ this.setUnhappyCounter(this.getUnhappyCounter() - 1);
|
||||
+ }
|
||||
+ if (this.isEffectiveAi()) {
|
||||
+ if (this.aware && this.isEffectiveAi()) { // Paper - Do not tick AI for inactive unaware mobs
|
||||
+ if (this.level().spigotConfig.tickInactiveVillagers) {
|
||||
+ this.customServerAiStep(this.level().getMinecraftWorld());
|
||||
+ } else {
|
||||
@@ -816,7 +817,7 @@ index 3a590d4dc980a2912e9cc043d8c8db4cf9d60803..06ab7c48b18c03af494ab10fc2b584ce
|
||||
+
|
||||
}
|
||||
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
|
||||
index 995b51d859316c6128b30d930e58dd47224514b2..329aef0e55664fbdd98c84d4d7cee74cd633e9c3 100644
|
||||
index 0958fe0438c3e934b885acf78a0f8003ef8bb694..31c7223d2d01573c24d4ad83737fa2c2752db028 100644
|
||||
--- a/net/minecraft/world/level/Level.java
|
||||
+++ b/net/minecraft/world/level/Level.java
|
||||
@@ -155,6 +155,12 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
||||
|
||||
@@ -9,7 +9,7 @@ creating too large of a packet to sed.
|
||||
Co-authored-by: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
|
||||
diff --git a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
||||
index 2bfc4cb47fc51345fc733b6ab3b19ace4f8bc171..dad2b89355b05900251955d015451fe195bfdb49 100644
|
||||
index 6ecda939755534ce0f35680be16a04c0a0e0a312..488d5654f5414c9950f19e86a791507366366e01 100644
|
||||
--- a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
||||
+++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
||||
@@ -32,6 +32,14 @@ public class ClientboundLevelChunkPacketData {
|
||||
|
||||
+4
-4
@@ -2326,7 +2326,7 @@ index 0000000000000000000000000000000000000000..298076a0db4e6ee6e4775ac43bf749d9
|
||||
+ }
|
||||
+}
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index 9f43d29aa4fecb03d6ef49e71e4de846dca187de..2e76ab96c66fd39b5e6619c3dbc75559d5c9b04a 100644
|
||||
index 26c05173e7fb2bf055afda1717af0dd2e997118d..ffe3ae41dd3d8c4141f81edf0b92001db957ebac 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -232,6 +232,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -2337,7 +2337,7 @@ index 9f43d29aa4fecb03d6ef49e71e4de846dca187de..2e76ab96c66fd39b5e6619c3dbc75559
|
||||
|
||||
@Override
|
||||
public @Nullable LevelChunk getChunkIfLoaded(int x, int z) {
|
||||
@@ -2774,6 +2775,13 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -2765,6 +2766,13 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
return this.debugSynchronizers;
|
||||
}
|
||||
|
||||
@@ -2352,10 +2352,10 @@ index 9f43d29aa4fecb03d6ef49e71e4de846dca187de..2e76ab96c66fd39b5e6619c3dbc75559
|
||||
return toLevel.dimension() != Level.NETHER || this.getGameRules().get(GameRules.ALLOW_ENTERING_NETHER_USING_PORTALS);
|
||||
}
|
||||
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
|
||||
index 329aef0e55664fbdd98c84d4d7cee74cd633e9c3..b9e07d5bd36993c5ac83b843866fabbea71c4281 100644
|
||||
index 31c7223d2d01573c24d4ad83737fa2c2752db028..8df070a7135411806d42ea692b566c4d8389a47b 100644
|
||||
--- a/net/minecraft/world/level/Level.java
|
||||
+++ b/net/minecraft/world/level/Level.java
|
||||
@@ -2156,6 +2156,17 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
||||
@@ -2152,6 +2152,17 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
||||
return this.palettedContainerFactory;
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -55,7 +55,7 @@ index a6f9435a8e2a6c8cc05bd3873cb6fc0ec98973b8..d44fd9eea0ccb09c894d6431ede8a4c2
|
||||
this.used.set(position, position + size);
|
||||
}
|
||||
diff --git a/net/minecraft/world/level/chunk/storage/RegionFile.java b/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
index fc712f9dbc893a6dee8ccecc54d0af07659dee62..b4a59b855b45018853e8122a5829a48ebd220a5f 100644
|
||||
index 5e77906d77da82b23fbd6e8d16fbd0847c83f4da..e077797cefffcb9db627cab12c7d88245e5baa69 100644
|
||||
--- a/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
@@ -47,6 +47,363 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
|
||||
@@ -760,7 +760,7 @@ index 5fa64ff30b8252f139ca7f77c14352c384c8c0e5..eae4963b6d18c5f92871477b93f81c2c
|
||||
|
||||
return var4;
|
||||
diff --git a/net/minecraft/world/level/chunk/storage/RegionFileVersion.java b/net/minecraft/world/level/chunk/storage/RegionFileVersion.java
|
||||
index 97d3da90c270a06d85e70d261658f71d97ae4e3c..11b65c688144e7ef5b7cba603949843a11ccd084 100644
|
||||
index ea229f63f81c5638ded74c28e1e13d8948f2e5e0..d991fccfc3d2cc51611f802a6d40429024e21d9a 100644
|
||||
--- a/net/minecraft/world/level/chunk/storage/RegionFileVersion.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/RegionFileVersion.java
|
||||
@@ -21,7 +21,7 @@ import org.slf4j.Logger;
|
||||
@@ -771,7 +771,7 @@ index 97d3da90c270a06d85e70d261658f71d97ae4e3c..11b65c688144e7ef5b7cba603949843a
|
||||
+ public static final Int2ObjectMap<RegionFileVersion> VERSIONS = new Int2ObjectOpenHashMap<>(); // Paper - private -> public
|
||||
private static final Object2ObjectMap<String, RegionFileVersion> VERSIONS_BY_NAME = new Object2ObjectOpenHashMap<>();
|
||||
public static final RegionFileVersion VERSION_GZIP = register(
|
||||
new RegionFileVersion(1, null, in -> new FastBufferedInputStream(new GZIPInputStream(in)), out -> new BufferedOutputStream(new GZIPOutputStream(out)))
|
||||
new RegionFileVersion(1, "gzip", in -> new FastBufferedInputStream(new GZIPInputStream(in)), out -> new BufferedOutputStream(new GZIPOutputStream(out))) // Paper - Enable for Configurable region compression format
|
||||
diff --git a/net/minecraft/world/level/chunk/storage/RegionStorageInfo.java b/net/minecraft/world/level/chunk/storage/RegionStorageInfo.java
|
||||
index 590bd1ca60956e5c5be58481f97401e55f9e1aeb..4e0c9fa1eecfa9f21285b7911e25830bbcbc8d86 100644
|
||||
--- a/net/minecraft/world/level/chunk/storage/RegionStorageInfo.java
|
||||
@@ -791,7 +791,7 @@ index 590bd1ca60956e5c5be58481f97401e55f9e1aeb..4e0c9fa1eecfa9f21285b7911e25830b
|
||||
return new RegionStorageInfo(this.level, this.dimension, this.type + suffix);
|
||||
}
|
||||
diff --git a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
|
||||
index 22a83f312d357176a5489e5b6b71c3ef3e737df7..74158a5d7ea8058c99ab215265987746aa043fc7 100644
|
||||
index 1e0afa9590de7fe492cf29aded96be3db498c51e..df0bb06176ad5cce3ed8785d22d68b2dcbf5a8ca 100644
|
||||
--- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
|
||||
@@ -120,6 +120,18 @@ public record SerializableChunkData(
|
||||
@@ -823,7 +823,7 @@ index 22a83f312d357176a5489e5b6b71c3ef3e737df7..74158a5d7ea8058c99ab215265987746
|
||||
tag.putString("Status", BuiltInRegistries.CHUNK_STATUS.getKey(this.chunkStatus).toString());
|
||||
tag.storeNullable("blending_data", BlendingData.Packed.CODEC, this.blendingData);
|
||||
diff --git a/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java b/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
|
||||
index 884fa9545a93008fa92863c845aef8f18d88e25a..15da3686bb1b5fddd388a88a7b300aa221c83f58 100644
|
||||
index 6e55cb304fd9a5a01bbe7666dddb882e684cdcf1..6d954064f409ddd2cb7cfd0d0c428ac186f5ca4d 100644
|
||||
--- a/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
|
||||
@@ -34,6 +34,7 @@ public class SimpleRegionStorage implements ca.spottedleaf.moonrise.patches.chun
|
||||
|
||||
@@ -5,10 +5,10 @@ Subject: [PATCH] Incremental chunk and player saving
|
||||
|
||||
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index 9a014a1a198293c9ce02354d8128cb19666055b8..c6691f02a1b2c1540b959fcc07eb8390906541ec 100644
|
||||
index 1e88021d476710cf4fce9bf50c02993e076e36ee..8c4b40362f77cdea6e57315a6639006ac7ab27c7 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -958,7 +958,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -968,7 +968,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
}
|
||||
public boolean saveAllChunks(final boolean silent, final boolean flush, final boolean force, final boolean close) {
|
||||
// Paper end - add close param
|
||||
@@ -17,7 +17,7 @@ index 9a014a1a198293c9ce02354d8128cb19666055b8..c6691f02a1b2c1540b959fcc07eb8390
|
||||
boolean result = false;
|
||||
|
||||
for (ServerLevel level : this.getAllLevels()) {
|
||||
@@ -970,13 +970,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -980,13 +980,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
result = true;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ index 9a014a1a198293c9ce02354d8128cb19666055b8..c6691f02a1b2c1540b959fcc07eb8390
|
||||
|
||||
if (flush) {
|
||||
for (ServerLevel level : this.getAllLevels()) {
|
||||
@@ -990,11 +984,25 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -1000,11 +994,25 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ index 9a014a1a198293c9ce02354d8128cb19666055b8..c6691f02a1b2c1540b959fcc07eb8390
|
||||
boolean result = this.saveAllChunks(silent, flush, force);
|
||||
this.warnOnLowDiskSpace();
|
||||
var5 = result;
|
||||
@@ -1641,9 +1649,31 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -1651,9 +1659,31 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
}
|
||||
|
||||
this.ticksUntilAutosave--;
|
||||
@@ -94,10 +94,10 @@ index 9a014a1a198293c9ce02354d8128cb19666055b8..c6691f02a1b2c1540b959fcc07eb8390
|
||||
ProfilerFiller profiler = Profiler.get();
|
||||
this.server.spark.executeMainThreadTasks(); // Paper - spark
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index 2e76ab96c66fd39b5e6619c3dbc75559d5c9b04a..b4a3c61a370939db94d57a911cfa5cfa1008a93a 100644
|
||||
index ffe3ae41dd3d8c4141f81edf0b92001db957ebac..98461e8468a7eeaef2544ee907ff64de1fd42a49 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -1449,6 +1449,15 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -1441,6 +1441,15 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
public boolean mayInteract(final Entity entity, final BlockPos pos) {
|
||||
return !(entity instanceof Player player && (this.server.isUnderSpawnProtection(this, pos, player) || !this.getWorldBorder().isWithinBounds(pos)));
|
||||
}
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ Subject: [PATCH] Optimise collision checking in player move packet handling
|
||||
Move collision logic to just the hasNewCollision call instead of getCubes + hasNewCollision
|
||||
|
||||
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
index 05a700ffc844b41f4fa129226f78dc6c52520f32..1d93a864c5fc040b5ec7c48b4db0bd54cd6255ff 100644
|
||||
index 0b86c393b041607278c1b712a62ec9ae3640f4de..80162ffef5d54d00663dcb58a087884ec8a3eaaa 100644
|
||||
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -634,6 +634,7 @@ public class ServerGamePacketListenerImpl
|
||||
|
||||
@@ -13,10 +13,10 @@ average over the last 5 seconds of keepalive transactions.
|
||||
|
||||
diff --git a/io/papermc/paper/util/KeepAlive.java b/io/papermc/paper/util/KeepAlive.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..4a2520f554c2ee74faf86d7c93baccf0f391a6b3
|
||||
index 0000000000000000000000000000000000000000..66d6af2833d76af28fbeb2ce70c194a559572460
|
||||
--- /dev/null
|
||||
+++ b/io/papermc/paper/util/KeepAlive.java
|
||||
@@ -0,0 +1,67 @@
|
||||
@@ -0,0 +1,84 @@
|
||||
+package io.papermc.paper.util;
|
||||
+
|
||||
+public class KeepAlive {
|
||||
@@ -34,6 +34,14 @@ index 0000000000000000000000000000000000000000..4a2520f554c2ee74faf86d7c93baccf0
|
||||
+ public final PingCalculator pingCalculator1m = new PingCalculator(java.util.concurrent.TimeUnit.MINUTES.toNanos(1L));
|
||||
+ public final PingCalculator pingCalculator5s = new PingCalculator(java.util.concurrent.TimeUnit.SECONDS.toNanos(5L));
|
||||
+
|
||||
+ public KeepAlive copyForListenerHandoff() {
|
||||
+ KeepAlive copy = new KeepAlive();
|
||||
+ copy.lastKeepAliveTx = this.lastKeepAliveTx;
|
||||
+ copy.pingCalculator1m.copyFrom(this.pingCalculator1m);
|
||||
+ copy.pingCalculator5s.copyFrom(this.pingCalculator5s);
|
||||
+ return copy;
|
||||
+ }
|
||||
+
|
||||
+ public static final class PingCalculator {
|
||||
+
|
||||
+ private final long intervalNS;
|
||||
@@ -47,6 +55,15 @@ index 0000000000000000000000000000000000000000..4a2520f554c2ee74faf86d7c93baccf0
|
||||
+ this.intervalNS = intervalNS;
|
||||
+ }
|
||||
+
|
||||
+ private void copyFrom(PingCalculator other) {
|
||||
+ for (KeepAliveResponse response : other.responses) {
|
||||
+ this.responses.add(response);
|
||||
+ }
|
||||
+ this.timeSumNS = other.timeSumNS;
|
||||
+ this.timeSumCount = other.timeSumCount;
|
||||
+ this.lastAverageNS = other.lastAverageNS;
|
||||
+ }
|
||||
+
|
||||
+ public void update(KeepAliveResponse response) {
|
||||
+ long currTime = response.txTimeNS;
|
||||
+
|
||||
@@ -100,7 +117,7 @@ index d87a00ee3cd04ce25dbe46ae6b386b7f0799102c..ace440c2b82a6368aa27a85ff433ba2c
|
||||
}
|
||||
}
|
||||
diff --git a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
|
||||
index f54d99c59bbca7fb5e0615f8fee9c3f6fbffa18a..d9b6f3826e2126d339e926afed782f6d0e890508 100644
|
||||
index 079ab378920c0e52ef4e42ef20b37bd389f40870..dc9020f210c21a626c948d013cc6c972cbc093f3 100644
|
||||
--- a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
|
||||
+++ b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
|
||||
@@ -39,12 +39,13 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
|
||||
@@ -223,11 +240,19 @@ index f54d99c59bbca7fb5e0615f8fee9c3f6fbffa18a..d9b6f3826e2126d339e926afed782f6d
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,6 +457,6 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
|
||||
@@ -424,6 +457,14 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
|
||||
}
|
||||
|
||||
protected CommonListenerCookie createCookie(final ClientInformation clientInformation) {
|
||||
- return new CommonListenerCookie(this.playerProfile(), this.latency, clientInformation, this.transferred, this.playerBrand, this.pluginMessagerChannels); // Paper
|
||||
+ return new CommonListenerCookie(this.playerProfile(), this.latency, clientInformation, this.transferred, this.playerBrand, this.pluginMessagerChannels, this.keepAlive); // Paper // Paper
|
||||
+ return new CommonListenerCookie(
|
||||
+ this.playerProfile(),
|
||||
+ this.latency,
|
||||
+ clientInformation,
|
||||
+ this.transferred,
|
||||
+ this.playerBrand,
|
||||
+ this.pluginMessagerChannels,
|
||||
+ this.keepAlive.copyForListenerHandoff()
|
||||
+ ); // Paper - listener handoff should reset pending keepalive expectations
|
||||
}
|
||||
}
|
||||
@@ -20,10 +20,10 @@ index 2bc436cdf5180a7943c45fabb9fbbedae6f7db56..f312a7f5b1b2a777ab36b94ce7cbf387
|
||||
|
||||
@Override
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index a3edf217dd2ffce19dc1111402975ba12bb86bad..2b6717833b56c3e33f3ac656a376dd5c841dc1bc 100644
|
||||
index 8c4b40362f77cdea6e57315a6639006ac7ab27c7..a5217deb236ee922d020a7a6a03141a5f67acd77 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -1784,32 +1784,22 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -1794,32 +1794,22 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,10 +66,10 @@ index a3edf217dd2ffce19dc1111402975ba12bb86bad..2b6717833b56c3e33f3ac656a376dd5c
|
||||
io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.ADVENTURE_CLICK_MANAGER.handleQueue(this.tickCount); // Paper
|
||||
io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.DIALOG_CLICK_MANAGER.handleQueue(this.tickCount); // Paper
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index 93d3fb313d538cc1067b1db010458169a1320122..9cd46a8619e575a865f4d00ce71d249c9b028b2f 100644
|
||||
index fea6c3b48c4eb162fbdc099fa775bc3161a4dd4e..82f6582a92f17bdeb3218c4c1fcac86981fd9c37 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -5147,6 +5147,11 @@ public abstract class Entity
|
||||
@@ -5146,6 +5146,11 @@ public abstract class Entity
|
||||
this.getBukkitEntity().taskScheduler.retire();
|
||||
}
|
||||
// Paper end - Folia schedulers
|
||||
|
||||
@@ -48,10 +48,10 @@ index 0000000000000000000000000000000000000000..24a2090e068ad3c0d08705050944abdf
|
||||
+ }
|
||||
+}
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index fbd70bcdc2efb42505fd5b3e5a6f703782c4060d..000943733f07cef2e508f78aadf5d28dec70e90c 100644
|
||||
index a5217deb236ee922d020a7a6a03141a5f67acd77..4fdf7354bc01789cf7480b14c2fc8fe75b748563 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -1836,6 +1836,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -1846,6 +1846,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
level.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - BlockPhysicsEvent
|
||||
level.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent
|
||||
level.updateLagCompensationTick(); // Paper - lag compensation
|
||||
|
||||
@@ -155,7 +155,7 @@ index a94e6ca1d396b6b0781de5d550c4a804274ee003..a1018e9ca9dfb978d6e6633577abd7d5
|
||||
|
||||
private ClientboundLevelChunkWithLightPacket(final RegistryFriendlyByteBuf input) {
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index b4a3c61a370939db94d57a911cfa5cfa1008a93a..be0e0b3a70bde1e2217ba4d3f90031e243d86941 100644
|
||||
index 78021db10860396fef4eb2e01926fbad123916fa..3f02da2014ed8e4169f4bb819b4d9e61154b7b06 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -630,7 +630,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -209,7 +209,7 @@ index b69bcaa1e27a4fbe9a9568f3c9e1843167abe1f5..9218322801c41657c00928ef90b0de99
|
||||
}
|
||||
// Paper end - Send empty chunk
|
||||
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
|
||||
index 630aec19b88a519fa4ec382fb2f5e9aebda59208..f765bc3d805de16909b0407da633208bb0dd9edc 100644
|
||||
index 06451a21c4bfbcd95063f96246013d63f65af8a3..e34e901dce06c6bc6060418593010dcfb38e9396 100644
|
||||
--- a/net/minecraft/world/level/Level.java
|
||||
+++ b/net/minecraft/world/level/Level.java
|
||||
@@ -145,6 +145,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Mon, 20 Apr 2026 02:56:22 -0700
|
||||
Subject: [PATCH] Delay open/close callbacks for chests
|
||||
|
||||
The logic for chests may invoke block updates, which may not
|
||||
be safe to perform. For example, inventory closing due to
|
||||
player logout (which restricts chunk loading due to being
|
||||
inside an entity status callback) or during shutdown (which has
|
||||
chunk loading restrictions due to the chunk system being halted).
|
||||
|
||||
diff --git a/net/minecraft/world/level/block/entity/BarrelBlockEntity.java b/net/minecraft/world/level/block/entity/BarrelBlockEntity.java
|
||||
index 9eb6cb86ff70f04863cae3def1006f82bcb4fa6b..da5fdf77dd26d97c8b92bac9a6af8d922c31eb9c 100644
|
||||
--- a/net/minecraft/world/level/block/entity/BarrelBlockEntity.java
|
||||
+++ b/net/minecraft/world/level/block/entity/BarrelBlockEntity.java
|
||||
@@ -66,6 +66,13 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity {
|
||||
Objects.requireNonNull(BarrelBlockEntity.this);
|
||||
}
|
||||
|
||||
+ // Paper start - delay open/close callbacks
|
||||
+ @Override
|
||||
+ public boolean delayCallbacks() {
|
||||
+ return true;
|
||||
+ }
|
||||
+ // Paper end - delay open/close callbacks
|
||||
+
|
||||
@Override
|
||||
protected void onOpen(final Level level, final BlockPos pos, final BlockState state) {
|
||||
BarrelBlockEntity.this.playSound(state, SoundEvents.BARREL_OPEN);
|
||||
diff --git a/net/minecraft/world/level/block/entity/ChestBlockEntity.java b/net/minecraft/world/level/block/entity/ChestBlockEntity.java
|
||||
index 81a293cc13653a9a0336cb25d75273969fee4d16..8ebcce4c4971a11d367fb39e9c7fd280f63594e3 100644
|
||||
--- a/net/minecraft/world/level/block/entity/ChestBlockEntity.java
|
||||
+++ b/net/minecraft/world/level/block/entity/ChestBlockEntity.java
|
||||
@@ -35,6 +35,13 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement
|
||||
Objects.requireNonNull(ChestBlockEntity.this);
|
||||
}
|
||||
|
||||
+ // Paper start - delay open/close callbacks
|
||||
+ @Override
|
||||
+ public boolean delayCallbacks() {
|
||||
+ return true;
|
||||
+ }
|
||||
+ // Paper end - delay open/close callbacks
|
||||
+
|
||||
@Override
|
||||
protected void onOpen(final Level level, final BlockPos pos, final BlockState blockState) {
|
||||
if (blockState.getBlock() instanceof ChestBlock chestBlock) {
|
||||
diff --git a/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java b/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java
|
||||
index 9af046cbe2c5024040057118a0e9ecd539815781..aa8b384b05738139fa3492b3c09084cda09448ce 100644
|
||||
--- a/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java
|
||||
+++ b/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java
|
||||
@@ -37,11 +37,23 @@ public abstract class ContainerOpenersCounter {
|
||||
|
||||
protected abstract void openerCountChanged(final Level level, final BlockPos pos, final BlockState blockState, int previous, int current);
|
||||
|
||||
+ // Paper start - delay open/close callbacks
|
||||
+ public boolean delayCallbacks() {
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Paper end - delay open/close callbacks
|
||||
+
|
||||
public abstract boolean isOwnContainer(final Player player);
|
||||
|
||||
public void incrementOpeners(
|
||||
final LivingEntity entity, final Level level, final BlockPos pos, final BlockState blockState, final double maxInteractionRange
|
||||
) {
|
||||
+ // Paper start - delay open/close callbacks
|
||||
+ if (this.delayCallbacks()) {
|
||||
+ scheduleRecheck(level, pos, blockState, 1);
|
||||
+ return;
|
||||
+ }
|
||||
+ // Paper end - delay open/close callbacks
|
||||
int oldPower = Math.max(0, Math.min(15, this.openCount)); // CraftBukkit - Get power before new viewer is added
|
||||
int previous = this.openCount++;
|
||||
|
||||
@@ -66,6 +78,12 @@ public abstract class ContainerOpenersCounter {
|
||||
}
|
||||
|
||||
public void decrementOpeners(final LivingEntity entity, final Level level, final BlockPos pos, final BlockState blockState) {
|
||||
+ // Paper start - delay open/close callbacks
|
||||
+ if (this.delayCallbacks()) {
|
||||
+ scheduleRecheck(level, pos, blockState, 1);
|
||||
+ return;
|
||||
+ }
|
||||
+ // Paper end - delay open/close callbacks
|
||||
int oldPower = Math.max(0, Math.min(15, this.openCount)); // CraftBukkit - Get power before new viewer is added
|
||||
if (this.openCount == 0) return; // Paper - Prevent ContainerOpenersCounter openCount from going negative
|
||||
int previous = this.openCount--;
|
||||
@@ -151,6 +169,11 @@ public abstract class ContainerOpenersCounter {
|
||||
}
|
||||
|
||||
private static void scheduleRecheck(final Level level, final BlockPos blockPos, final BlockState blockState) {
|
||||
- level.scheduleTick(blockPos, blockState.getBlock(), 5);
|
||||
+ // Paper start - delay open/close callbacks
|
||||
+ scheduleRecheck(level, blockPos, blockState, 5);
|
||||
+ }
|
||||
+ private static void scheduleRecheck(final Level level, final BlockPos blockPos, final BlockState blockState, final int delay) {
|
||||
+ level.scheduleTick(blockPos, blockState.getBlock(), delay);
|
||||
+ // Paper end - delay open/close callbacks
|
||||
}
|
||||
}
|
||||
diff --git a/net/minecraft/world/level/block/entity/EnderChestBlockEntity.java b/net/minecraft/world/level/block/entity/EnderChestBlockEntity.java
|
||||
index eeac644f60760570244380d3c454fd33e37b5a7f..751625124a87de63d933d9c7809e46361e3136a5 100644
|
||||
--- a/net/minecraft/world/level/block/entity/EnderChestBlockEntity.java
|
||||
+++ b/net/minecraft/world/level/block/entity/EnderChestBlockEntity.java
|
||||
@@ -18,6 +18,13 @@ public class EnderChestBlockEntity extends BlockEntity implements LidBlockEntity
|
||||
Objects.requireNonNull(EnderChestBlockEntity.this);
|
||||
}
|
||||
|
||||
+ // Paper start - delay open/close callbacks
|
||||
+ @Override
|
||||
+ public boolean delayCallbacks() {
|
||||
+ return true;
|
||||
+ }
|
||||
+ // Paper end - delay open/close callbacks
|
||||
+
|
||||
@Override
|
||||
protected void onOpen(final Level level, final BlockPos pos, final BlockState blockState) {
|
||||
level.playSound(
|
||||
File diff suppressed because it is too large.
Load diff
-11
@@ -1,11 +0,0 @@
|
||||
--- a/net/minecraft/gametest/framework/GameTestHelper.java
|
||||
+++ b/net/minecraft/gametest/framework/GameTestHelper.java
|
||||
@@ -929,7 +_,7 @@
|
||||
|
||||
public void setTime(final long ticks) {
|
||||
ServerLevel level = this.getLevel();
|
||||
- Holder<WorldClock> clock = level.dimensionType().defaultClock().orElseThrow();
|
||||
+ Holder<WorldClock> clock = level.getClock().orElseThrow();
|
||||
level.clockManager().setTotalTicks(clock, ticks);
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
|
||||
+ // Paper start - Validate Identifier
|
||||
+ // Check for the max network string length (capped at Short.MAX_VALUE) as well as the max bytes of a StringTag (length written as an unsigned short)
|
||||
+ final String id = namespace + ":" + path;
|
||||
+ if (id.length() > Short.MAX_VALUE || io.netty.buffer.ByteBufUtil.utf8MaxBytes(id) > 2 * Short.MAX_VALUE + 1) {
|
||||
+ throw new IdentifierException("Identifier too long: " + id);
|
||||
+ final int length = namespace.length() + path.length() + 1;
|
||||
+ if (length > Short.MAX_VALUE || io.netty.buffer.ByteBufUtil.utf8MaxBytes(length) > 2 * Short.MAX_VALUE + 1) {
|
||||
+ throw new IdentifierException("Identifier too long: " + length);
|
||||
+ }
|
||||
+ // Paper end - Validate Identifier
|
||||
this.namespace = namespace;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
OptionParser parser = new OptionParser();
|
||||
OptionSpec<Void> nogui = parser.accepts("nogui");
|
||||
OptionSpec<Void> initSettings = parser.accepts("initSettings", "Initializes 'server.properties' and 'eula.txt', then quits");
|
||||
@@ -89,39 +_,96 @@
|
||||
@@ -89,39 +_,95 @@
|
||||
parser.printHelpOn(System.err);
|
||||
return;
|
||||
}
|
||||
@@ -72,15 +72,14 @@
|
||||
}
|
||||
|
||||
- if (!eula.hasAgreedToEULA()) {
|
||||
+ // Spigot start
|
||||
+ // Paper start - eula system property
|
||||
+ boolean eulaAgreed = Boolean.getBoolean("com.mojang.eula.agree");
|
||||
+ if (eulaAgreed) {
|
||||
+ LOGGER.error("You have used the Spigot command line EULA agreement flag.");
|
||||
+ LOGGER.error("By using this setting you are indicating your agreement to Mojang's EULA (https://aka.ms/MinecraftEULA).");
|
||||
+ LOGGER.error("If you do not agree to the above EULA please stop your server and remove this flag immediately.");
|
||||
+ }
|
||||
+ if (!eula.hasAgreedToEULA() && !eulaAgreed) {
|
||||
+ // Spigot end
|
||||
+ LOGGER.warn("You have used the Paper command line EULA agreement flag.");
|
||||
+ LOGGER.warn("By using this setting you are indicating your agreement to Mojang's EULA (https://aka.ms/MinecraftEULA).");
|
||||
+ LOGGER.warn("If you do not agree to the above EULA please stop your server and remove this flag immediately.");
|
||||
+ } else if (!eula.hasAgreedToEULA()) {
|
||||
+ // Paper end - eula system property
|
||||
LOGGER.info("You need to agree to the EULA in order to run the server. Go to eula.txt for more info.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -300,7 +300,7 @@
|
||||
if (profiledDuration != null) {
|
||||
profiledDuration.finish(true);
|
||||
}
|
||||
@@ -422,33 +_,140 @@
|
||||
@@ -422,33 +_,150 @@
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,9 +430,9 @@
|
||||
+ );
|
||||
+ }
|
||||
+ this.addLevel(serverLevel);
|
||||
+ this.initWorld(serverLevel);
|
||||
+ this.initWorld(serverLevel, null);
|
||||
+ }
|
||||
+ public void initWorld(ServerLevel overworld) {
|
||||
+ public void initWorld(ServerLevel overworld, org.bukkit.@Nullable WorldCreator worldCreator) {
|
||||
+ final net.minecraft.world.level.storage.ServerLevelData levelData = overworld.serverLevelData;
|
||||
+ final WorldOptions worldOptions = overworld.worldGenSettings.options();
|
||||
+ final boolean isDebug = this.worldData.isDebugWorld();
|
||||
@@ -447,7 +447,17 @@
|
||||
if (!levelData.isInitialized()) {
|
||||
try {
|
||||
- setInitialSpawn(overworld, levelData, worldOptions.generateBonusChest(), isDebug, this.levelLoadListener);
|
||||
+ setInitialSpawn(overworld, levelData, worldOptions.generateBonusChest(), isDebug, overworld.levelLoadListener); // Paper - per world level load listener
|
||||
+ // Paper start - Allow direct setting spawn location
|
||||
+ if (worldCreator != null && worldCreator.forcedSpawnPosition() != null) {
|
||||
+ levelData.setSpawn(LevelData.RespawnData.of(overworld.dimension(),
|
||||
+ io.papermc.paper.util.MCUtil.toBlockPos(worldCreator.forcedSpawnPosition()),
|
||||
+ Objects.requireNonNullElse(worldCreator.forcedSpawnYaw(), LevelData.RespawnData.DEFAULT.yaw()),
|
||||
+ Objects.requireNonNullElse(worldCreator.forcedSpawnPitch(), LevelData.RespawnData.DEFAULT.pitch())
|
||||
+ ));
|
||||
+ } else {
|
||||
+ setInitialSpawn(overworld, levelData, worldOptions.generateBonusChest(), isDebug, overworld.levelLoadListener); // Paper - per world level load listener
|
||||
+ }
|
||||
+ // Paper end - Allow direct setting spawn location
|
||||
levelData.setInitialized(true);
|
||||
if (isDebug) {
|
||||
- this.setupDebugLevel(this.worldData);
|
||||
@@ -493,7 +503,7 @@
|
||||
+
|
||||
+ if (spawn != null) {
|
||||
+ if (spawn.getWorld() != level.getWorld()) {
|
||||
+ throw new IllegalStateException("Cannot set spawn point for " + levelData.getLevelName() + " to be in another world (" + io.papermc.paper.util.MCUtil.getLevelName(level) + ")");
|
||||
+ throw new IllegalStateException("Cannot set spawn point for " + levelData.getLevelName() + " to be in another world (" + (spawn.getWorld() != null ? spawn.getWorld().key() : null) + ")");
|
||||
+ } else {
|
||||
+ levelData.setSpawn(
|
||||
+ net.minecraft.world.level.storage.LevelData.RespawnData.of(
|
||||
@@ -1534,7 +1544,7 @@
|
||||
player.connection.send(new ClientboundEntityEventPacket(player, event));
|
||||
}
|
||||
} else if (rule == GameRules.LIMITED_CRAFTING || rule == GameRules.IMMEDIATE_RESPAWN) {
|
||||
@@ -2131,18 +_,19 @@
|
||||
@@ -2131,19 +_,21 @@
|
||||
? ClientboundGameEventPacket.LIMITED_CRAFTING
|
||||
: ClientboundGameEventPacket.IMMEDIATE_RESPAWN;
|
||||
ClientboundGameEventPacket packet = new ClientboundGameEventPacket(eventType, (Boolean)value ? 1.0F : 0.0F);
|
||||
@@ -1556,8 +1566,10 @@
|
||||
- this.updateMobSpawningFlags();
|
||||
+ level.setSpawnSettings(level.isSpawningMonsters()); // Paper - per-world game rules
|
||||
} else if (rule == GameRules.ADVANCE_TIME) {
|
||||
+ // TODO - snapshot - time handling
|
||||
this.getPlayerList().broadcastAll(this.clockManager().createFullSyncPacket());
|
||||
}
|
||||
}
|
||||
@@ -2157,12 +_,14 @@
|
||||
return this.savedDataStorage;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
list.deop(player);
|
||||
count++;
|
||||
- source.sendSuccess(() -> Component.translatable("commands.deop.success", players.iterator().next().name()), true);
|
||||
+ source.sendSuccess(() -> Component.translatable("commands.deop.success", player.name()), true); // Paper - fixes MC-253721
|
||||
+ source.sendSuccess(() -> Component.translatable("commands.deop.success", player.name()), true); // Paper - fixes MC-307513
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,56 +1,67 @@
|
||||
--- a/net/minecraft/server/commands/TimeCommand.java
|
||||
+++ b/net/minecraft/server/commands/TimeCommand.java
|
||||
@@ -169,16 +_,24 @@
|
||||
}
|
||||
@@ -170,23 +_,61 @@
|
||||
}
|
||||
|
||||
+ // Paper start - time skip event
|
||||
private static int setTotalTicks(final CommandSourceStack source, final Holder<WorldClock> clock, final int totalTicks) {
|
||||
+ // Paper start
|
||||
+ // TODO - snapshot - 26.1
|
||||
+ for (net.minecraft.server.level.ServerLevel level : io.papermc.paper.configuration.GlobalConfiguration.get().commands.timeCommandAffectsAllWorlds ? source.getServer().getAllLevels() : java.util.List.of(source.getLevel())) {
|
||||
+ long currentTime = level.clockManager().getTotalTicks(clock);
|
||||
+ org.bukkit.event.world.TimeSkipEvent event = new org.bukkit.event.world.TimeSkipEvent(level.getWorld(), org.bukkit.event.world.TimeSkipEvent.SkipReason.COMMAND, totalTicks - currentTime);
|
||||
+ if (event.callEvent()) {
|
||||
+ level.clockManager().setTotalTicks(clock, currentTime + event.getSkipAmount());
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
ServerClockManager clockManager = source.getServer().clockManager();
|
||||
- clockManager.setTotalTicks(clock, totalTicks);
|
||||
+ long currentTotalTicks = clockManager.getTotalTicks(clock);
|
||||
+ org.bukkit.event.world.ClockTimeSkipEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callTimeSkipEvent(clock, totalTicks - currentTotalTicks);
|
||||
+ if (event.callEvent()) {
|
||||
+ clockManager.setTotalTicks(clock, currentTotalTicks + event.getSkipAmount());
|
||||
+ }
|
||||
+ // clockManager.setTotalTicks(clock, totalTicks);
|
||||
source.sendSuccess(() -> Component.translatable("commands.time.set.absolute", clock.getRegisteredName(), totalTicks), true);
|
||||
return totalTicks;
|
||||
}
|
||||
|
||||
private static int addTime(final CommandSourceStack source, final Holder<WorldClock> clock, final int time) {
|
||||
+ // Paper start
|
||||
+ // TODO - snapshot - 26.1
|
||||
+ for (net.minecraft.server.level.ServerLevel level : io.papermc.paper.configuration.GlobalConfiguration.get().commands.timeCommandAffectsAllWorlds ? source.getServer().getAllLevels() : java.util.List.of(source.getLevel())) {
|
||||
+ org.bukkit.event.world.TimeSkipEvent event = new org.bukkit.event.world.TimeSkipEvent(level.getWorld(), org.bukkit.event.world.TimeSkipEvent.SkipReason.COMMAND, time);;
|
||||
+ if (event.callEvent()) {
|
||||
+ level.clockManager().addTicks(clock, event.getSkipAmount());
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
ServerClockManager clockManager = source.getServer().clockManager();
|
||||
- clockManager.addTicks(clock, time);
|
||||
+ org.bukkit.event.world.ClockTimeSkipEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callTimeSkipEvent(clock, time);
|
||||
+ if (event.callEvent()) {
|
||||
+ clockManager.addTicks(clock, event.getSkipAmount());
|
||||
+ }
|
||||
+ // Paper - move up
|
||||
long totalTicks = clockManager.getTotalTicks(clock);
|
||||
source.sendSuccess(() -> Component.translatable("commands.time.set.absolute", clock.getRegisteredName(), totalTicks), true);
|
||||
return wrapTime(totalTicks);
|
||||
@@ -186,9 +_,16 @@
|
||||
|
||||
private static int setTimeToTimeMarker(final CommandSourceStack source, final Holder<WorldClock> clock, final ResourceKey<ClockTimeMarker> timeMarkerId) throws CommandSyntaxException {
|
||||
ServerClockManager clockManager = source.getServer().clockManager();
|
||||
- if (!clockManager.moveToTimeMarker(clock, timeMarkerId)) {
|
||||
+ java.util.OptionalLong targetTime = clockManager.getTotalTicksToTimeMarker(clock, timeMarkerId);
|
||||
+ if (targetTime.isEmpty()) {
|
||||
throw ERROR_NO_TIME_MARKER_FOUND.create(clock.getRegisteredName(), timeMarkerId);
|
||||
} else {
|
||||
+ final long currentTime = clockManager.getTotalTicks(clock);
|
||||
+ final org.bukkit.event.world.ClockTimeSkipEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callTimeSkipEvent(clock, targetTime.getAsLong() - currentTime);
|
||||
+ if (event.callEvent()) {
|
||||
+ clockManager.setTotalTicks(clock, currentTime + event.getSkipAmount());
|
||||
+ }
|
||||
+ // Paper end - time skip event
|
||||
source.sendSuccess(
|
||||
() -> Component.translatable("commands.time.set.time_marker", clock.getRegisteredName(), timeMarkerId.identifier().toString()), true
|
||||
);
|
||||
@@ -214,7 +_,7 @@
|
||||
|
||||
private static Holder<WorldClock> getDefaultClock(final CommandSourceStack source) throws CommandSyntaxException {
|
||||
Holder<DimensionType> dimensionType = source.getLevel().dimensionTypeRegistration();
|
||||
- return dimensionType.value().defaultClock().orElseThrow(() -> ERROR_NO_DEFAULT_CLOCK.create(dimensionType.getRegisteredName()));
|
||||
+ return source.getLevel().getClock().orElseThrow(() -> ERROR_NO_DEFAULT_CLOCK.create(dimensionType.getRegisteredName())); // Paper - clock override
|
||||
}
|
||||
|
||||
private interface ClockGetter {
|
||||
private static int setTimeToTimeMarker(final CommandSourceStack source, final Holder<WorldClock> clock, final ResourceKey<ClockTimeMarker> timeMarkerId) throws CommandSyntaxException {
|
||||
+ // Paper start
|
||||
+ // TODO - snapshot - 26.1
|
||||
+ boolean foundMarker = false;
|
||||
+ for (net.minecraft.server.level.ServerLevel level : io.papermc.paper.configuration.GlobalConfiguration.get().commands.timeCommandAffectsAllWorlds ? source.getServer().getAllLevels() : java.util.List.of(source.getLevel())) {
|
||||
+ java.util.OptionalLong targetTime = level.clockManager().getTotalTicksToTimeMarker(clock, timeMarkerId);
|
||||
+ if (targetTime.isEmpty()) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ foundMarker = true;
|
||||
+ long currentTime = level.clockManager().getTotalTicks(clock);
|
||||
+ long delta = targetTime.getAsLong() - currentTime;
|
||||
+ org.bukkit.event.world.TimeSkipEvent event = new org.bukkit.event.world.TimeSkipEvent(level.getWorld(), org.bukkit.event.world.TimeSkipEvent.SkipReason.COMMAND, delta);
|
||||
+ if (event.callEvent()) {
|
||||
+ level.clockManager().setTotalTicks(clock, currentTime + event.getSkipAmount());
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
ServerClockManager clockManager = source.getServer().clockManager();
|
||||
- if (!clockManager.moveToTimeMarker(clock, timeMarkerId)) {
|
||||
+ if (!foundMarker) { // Paper
|
||||
throw ERROR_NO_TIME_MARKER_FOUND.create(clock.getRegisteredName(), timeMarkerId);
|
||||
} else {
|
||||
source.sendSuccess(
|
||||
@@ -262,42 +262,24 @@
|
||||
|
||||
@Deprecated
|
||||
@VisibleForTesting
|
||||
@@ -326,6 +_,13 @@
|
||||
}
|
||||
|
||||
@Override
|
||||
+ public Optional<Holder<WorldClock>> getClock() {
|
||||
+ return this.serverLevelData.clock()
|
||||
+ .<Holder<WorldClock>>map(clock -> this.registryAccess().lookupOrThrow(Registries.WORLD_CLOCK).getOrThrow(clock))
|
||||
+ .or(() -> this.dimensionType().defaultClock());
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
public EnvironmentAttributeSystem environmentAttributes() {
|
||||
return this.environmentAttributes;
|
||||
}
|
||||
@@ -353,12 +_,26 @@
|
||||
|
||||
@@ -354,11 +_,24 @@
|
||||
int percentage = this.getGameRules().get(GameRules.PLAYERS_SLEEPING_PERCENTAGE);
|
||||
if (this.sleepStatus.areEnoughSleeping(percentage) && this.sleepStatus.areEnoughDeepSleeping(percentage, this.players)) {
|
||||
- Optional<Holder<WorldClock>> defaultClock = this.dimensionType().defaultClock();
|
||||
- if (this.getGameRules().get(GameRules.ADVANCE_TIME) && defaultClock.isPresent()) {
|
||||
- this.server.clockManager().moveToTimeMarker(defaultClock.get(), ClockTimeMarkers.WAKE_UP_FROM_SLEEP);
|
||||
+ Optional<Holder<WorldClock>> clock = this.getClock();
|
||||
Optional<Holder<WorldClock>> defaultClock = this.dimensionType().defaultClock();
|
||||
+ org.bukkit.event.world.TimeSkipEvent event = null; // Paper - time skip event
|
||||
+ if (this.getGameRules().get(GameRules.ADVANCE_TIME) && clock.isPresent()) {
|
||||
if (this.getGameRules().get(GameRules.ADVANCE_TIME) && defaultClock.isPresent()) {
|
||||
- this.server.clockManager().moveToTimeMarker(defaultClock.get(), ClockTimeMarkers.WAKE_UP_FROM_SLEEP);
|
||||
+ // Paper start - time skip event
|
||||
+ long currentTime = this.server.clockManager().getTotalTicks(clock.get());
|
||||
+ long delta = this.server.clockManager().getTotalTicksToTimeMarker(clock.get(), ClockTimeMarkers.WAKE_UP_FROM_SLEEP).orElse(0L) - currentTime;
|
||||
+ long currentTime = this.server.clockManager().getTotalTicks(defaultClock.get());
|
||||
+ long delta = this.server.clockManager().getTotalTicksToTimeMarker(defaultClock.get(), ClockTimeMarkers.WAKE_UP_FROM_SLEEP).orElse(0L) - currentTime;
|
||||
+ event = new org.bukkit.event.world.TimeSkipEvent(
|
||||
+ this.getWorld(),
|
||||
+ org.bukkit.craftbukkit.CraftWorldClock.minecraftHolderToBukkit(clock.get()),
|
||||
+ org.bukkit.event.world.ClockTimeSkipEvent.SkipReason.NIGHT_SKIP,
|
||||
+ org.bukkit.event.world.TimeSkipEvent.SkipReason.NIGHT_SKIP,
|
||||
+ delta
|
||||
+ );
|
||||
+
|
||||
+ if (event.callEvent()) {
|
||||
+ this.server.clockManager().setTotalTicks(clock.get(), currentTime + event.getSkipAmount());
|
||||
+ this.server.clockManager().setTotalTicks(defaultClock.get(), currentTime + event.getSkipAmount()); // TODO - snapshot - per world time
|
||||
+ }
|
||||
+ // Paper end - time skip event
|
||||
}
|
||||
@@ -1005,16 +987,15 @@
|
||||
this.getServer().getDataStorage().set(MapItemSavedData.type(id), data);
|
||||
}
|
||||
|
||||
@@ -1492,7 +_,21 @@
|
||||
@@ -1492,7 +_,20 @@
|
||||
|
||||
@Override
|
||||
public void setRespawnData(final LevelData.RespawnData respawnData) {
|
||||
- this.getServer().setRespawnData(respawnData);
|
||||
+ // Paper start
|
||||
+ final LevelData.RespawnData previousRespawnData = this.serverLevelData.getRespawnData();
|
||||
+ this.serverLevelData.setSpawn(respawnData);
|
||||
+ if (!previousRespawnData.positionEquals(respawnData)) {
|
||||
+ if (!this.serverLevelData.getRespawnData().positionEquals(respawnData)) {
|
||||
+ org.bukkit.Location previousLocation = this.getWorld().getSpawnLocation();
|
||||
+ this.serverLevelData.setSpawn(respawnData);
|
||||
+ this.server.getPlayerList().broadcastAll(new net.minecraft.network.protocol.game.ClientboundSetDefaultSpawnPositionPacket(respawnData), this.dimension());
|
||||
+ this.server.updateEffectiveRespawnData();
|
||||
+ new org.bukkit.event.world.SpawnChangeEvent(this.getWorld(), previousLocation).callEvent();
|
||||
|
||||
+2
-2
@@ -243,7 +243,7 @@
|
||||
+ if (!this.cserver.isPrimaryThread()) {
|
||||
+ org.bukkit.craftbukkit.util.Waitable waitable = new org.bukkit.craftbukkit.util.Waitable() {
|
||||
+ @Override
|
||||
+ protected Object evaluate() {
|
||||
+ protected @Nullable Object evaluate() {
|
||||
+ ServerCommonPacketListenerImpl.this.disconnect(details);
|
||||
+ return null;
|
||||
+ }
|
||||
@@ -285,7 +285,7 @@
|
||||
+ }
|
||||
+
|
||||
+ reason = io.papermc.paper.adventure.PaperAdventure.asVanilla(event.reason());
|
||||
+ leaveMessage = io.papermc.paper.adventure.PaperAdventure.asVanilla(event.leaveMessage());
|
||||
+ leaveMessage = io.papermc.paper.adventure.PaperAdventure.asVanilla(event.leaveMessage());
|
||||
+ serverGamePacketListener.player.quitReason = org.bukkit.event.player.PlayerQuitEvent.QuitReason.KICKED; // Paper - Add API for quit reason
|
||||
+ // Log kick to console *after* event was processed.
|
||||
+ switch (cause) {
|
||||
|
||||
+7
-13
@@ -27,18 +27,10 @@
|
||||
this.setDirty();
|
||||
|
||||
for (ServerLevel level : this.server.getAllLevels()) {
|
||||
@@ -127,12 +_,29 @@
|
||||
@@ -126,7 +_,23 @@
|
||||
return this.getInstance(definition).totalTicks;
|
||||
}
|
||||
|
||||
public ClientboundSetTimePacket createFullSyncPacket() {
|
||||
+ // TODO - snapshot: 26.1 per-player time
|
||||
return new ClientboundSetTimePacket(this.getGameTime(), Util.mapValues(this.clocks, clock -> clock.packNetworkState(this.server)));
|
||||
}
|
||||
|
||||
private long getGameTime() {
|
||||
return this.server.overworld().getGameTime();
|
||||
}
|
||||
+
|
||||
+ // Paper start
|
||||
+ // TODO - snapshot: just make the inner class, fields and getter public
|
||||
+ public boolean isPaused(final Holder<WorldClock> definition) {
|
||||
@@ -52,8 +44,10 @@
|
||||
+ public float rate(final Holder<WorldClock> definition) {
|
||||
+ return this.getInstance(definition).rate;
|
||||
+ }
|
||||
+
|
||||
+ // Paper end
|
||||
+
|
||||
public ClientboundSetTimePacket createFullSyncPacket() {
|
||||
+ // TODO - snapshot: 26.1 per-player time
|
||||
return new ClientboundSetTimePacket(this.getGameTime(), Util.mapValues(this.clocks, clock -> clock.packNetworkState(this.server)));
|
||||
}
|
||||
|
||||
public boolean isAtTimeMarker(final Holder<WorldClock> clock, final ResourceKey<ClockTimeMarker> timeMarkerId) {
|
||||
ServerClockManager.ClockInstance clockInstance = this.getInstance(clock);
|
||||
+18
-1
@@ -1,6 +1,14 @@
|
||||
--- a/net/minecraft/world/damagesource/CombatTracker.java
|
||||
+++ b/net/minecraft/world/damagesource/CombatTracker.java
|
||||
@@ -29,15 +_,24 @@
|
||||
@@ -22,22 +_,40 @@
|
||||
private static final Style INTENTIONAL_GAME_DESIGN_STYLE = Style.EMPTY
|
||||
.withClickEvent(new ClickEvent.OpenUrl(CommonLinks.INTENTIONAL_GAME_DESIGN_BUG))
|
||||
.withHoverEvent(new HoverEvent.ShowText(Component.literal("MCPE-28723")));
|
||||
- public final List<CombatEntry> entries = Lists.newArrayList();
|
||||
+ public final List<CombatEntry> entries = new net.minecraft.util.ArrayListDeque<>(); // Paper - Fix mem leak (MC-301114); Use ArrayListDeque
|
||||
public final LivingEntity mob;
|
||||
private int lastDamageTime;
|
||||
private int combatStartTime;
|
||||
private int combatEndTime;
|
||||
public boolean inCombat;
|
||||
public boolean takingDamage;
|
||||
@@ -22,6 +30,15 @@
|
||||
+ public void recordDamageAndCheckCombatState(final CombatEntry entry) {
|
||||
+ final DamageSource source = entry.source();
|
||||
+ // Paper end - Combat tracker API
|
||||
+ // Paper start - Fix mem leak (MC-301114)
|
||||
+ final io.papermc.paper.configuration.type.number.IntOr.Disabled maxTrackingCombatEntries;
|
||||
+ if (!this.entries.isEmpty() && (maxTrackingCombatEntries = io.papermc.paper.configuration.GlobalConfiguration.get().misc.maxTrackingCombatEntries).enabled()) {
|
||||
+ final int maxEntries = maxTrackingCombatEntries.intValue();
|
||||
+ while (this.entries.size() > maxEntries) {
|
||||
+ this.entries.removeFirst();
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - Fix mem leak (MC-301114)
|
||||
this.entries.add(entry);
|
||||
this.lastDamageTime = this.mob.tickCount;
|
||||
this.takingDamage = true;
|
||||
|
||||
@@ -1355,7 +1355,7 @@
|
||||
this.setTicksFrozen(0);
|
||||
}
|
||||
|
||||
@@ -2762,11 +_,43 @@
|
||||
@@ -2762,11 +_,42 @@
|
||||
|
||||
public void thunderHit(final ServerLevel level, final LightningBolt lightningBolt) {
|
||||
this.setRemainingFireTicks(this.remainingFireTicks + 1);
|
||||
@@ -1383,11 +1383,10 @@
|
||||
+ }
|
||||
+
|
||||
+ // CraftBukkit start
|
||||
+ final DamageSource damageSource = this.damageSources().lightningBolt().eventEntityDamager(lightningBolt);
|
||||
+ if (thisBukkitEntity instanceof org.bukkit.entity.Hanging) {
|
||||
+ org.bukkit.event.hanging.HangingBreakByEntityEvent hangingEvent = new org.bukkit.event.hanging.HangingBreakByEntityEvent((org.bukkit.entity.Hanging) thisBukkitEntity, stormBukkitEntity);
|
||||
+ pluginManager.callEvent(hangingEvent);
|
||||
+
|
||||
+ if (hangingEvent.isCancelled()) {
|
||||
+ org.bukkit.event.hanging.HangingBreakByEntityEvent hangingEvent = new org.bukkit.event.hanging.HangingBreakByEntityEvent((org.bukkit.entity.Hanging) thisBukkitEntity, stormBukkitEntity, new org.bukkit.craftbukkit.damage.CraftDamageSource(damageSource));
|
||||
+ if (!hangingEvent.callEvent()) {
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
@@ -1396,7 +1395,7 @@
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (!this.hurtServer(level, this.damageSources().lightningBolt().eventEntityDamager(lightningBolt), 5.0F)) { // Paper - fix DamageSource API
|
||||
+ if (!this.hurtServer(level, damageSource, 5.0F)) { // Paper - fix DamageSource API
|
||||
+ return;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
|
||||
-11
@@ -1,16 +1,5 @@
|
||||
--- a/net/minecraft/world/entity/ai/village/VillageSiege.java
|
||||
+++ b/net/minecraft/world/entity/ai/village/VillageSiege.java
|
||||
@@ -34,8 +_,8 @@
|
||||
@Override
|
||||
public void tick(final ServerLevel level, final boolean spawnEnemies) {
|
||||
if (!level.isBrightOutside() && spawnEnemies) {
|
||||
- Optional<Holder<WorldClock>> defaultClock = level.dimensionType().defaultClock();
|
||||
- if (defaultClock.isPresent() && level.clockManager().isAtTimeMarker(defaultClock.get(), ClockTimeMarkers.ROLL_VILLAGE_SIEGE)) {
|
||||
+ Optional<Holder<WorldClock>> clock = level.getClock();
|
||||
+ if (clock.isPresent() && level.clockManager().isAtTimeMarker(clock.get(), ClockTimeMarkers.ROLL_VILLAGE_SIEGE)) {
|
||||
this.siegeState = level.getRandom().nextInt(10) == 0 ? VillageSiege.State.SIEGE_TONIGHT : VillageSiege.State.SIEGE_DONE;
|
||||
}
|
||||
|
||||
@@ -102,11 +_,12 @@
|
||||
zombie.finalizeSpawn(level, level.getCurrentDifficultyAt(zombie.blockPosition()), EntitySpawnReason.EVENT, null);
|
||||
} catch (Exception var5) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/world/entity/animal/goat/Goat.java
|
||||
+++ b/net/minecraft/world/entity/animal/goat/Goat.java
|
||||
@@ -213,13 +_,22 @@
|
||||
@@ -213,8 +_,16 @@
|
||||
public InteractionResult mobInteract(final Player player, final InteractionHand hand) {
|
||||
ItemStack heldItem = player.getItemInHand(hand);
|
||||
if (heldItem.is(Items.BUCKET) && !this.isBaby()) {
|
||||
@@ -18,13 +18,6 @@
|
||||
player.setItemInHand(hand, bucketOrMilkBucket);
|
||||
return InteractionResult.SUCCESS;
|
||||
} else {
|
||||
+ boolean isFood = this.isFood(heldItem); // Paper - track before stack is possibly decreased to 0 (Fixes MC-244739)
|
||||
InteractionResult interactionResult = super.mobInteract(player, hand);
|
||||
- if (interactionResult.consumesAction() && this.isFood(heldItem)) {
|
||||
+ if (interactionResult.consumesAction() && isFood) { // Paper
|
||||
this.playEatingSound();
|
||||
}
|
||||
|
||||
@@ -328,8 +_,7 @@
|
||||
double deltaY = Mth.randomBetween(this.random, 0.3F, 0.7F);
|
||||
double deltaZ = Mth.randomBetween(this.random, -0.2F, 0.2F);
|
||||
|
||||
+14
-3
@@ -1,6 +1,15 @@
|
||||
--- a/net/minecraft/world/entity/animal/happyghast/HappyGhast.java
|
||||
+++ b/net/minecraft/world/entity/animal/happyghast/HappyGhast.java
|
||||
@@ -314,8 +_,12 @@
|
||||
@@ -300,7 +_,7 @@
|
||||
@Override
|
||||
protected void addPassenger(final Entity passenger) {
|
||||
if (!this.isVehicle()) {
|
||||
- this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.HARNESS_GOGGLES_DOWN, this.getSoundSource(), 1.0F, 1.0F);
|
||||
+ this.playSound(SoundEvents.HARNESS_GOGGLES_DOWN, 1.0F, 1.0F); // Paper - don't play sound if silenced (Fixes MC-297169)
|
||||
}
|
||||
|
||||
super.addPassenger(passenger);
|
||||
@@ -314,16 +_,21 @@
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -15,9 +24,11 @@
|
||||
if (!this.level().isClientSide()) {
|
||||
this.setServerStillTimeout(10);
|
||||
}
|
||||
@@ -324,6 +_,7 @@
|
||||
|
||||
if (!this.isVehicle()) {
|
||||
this.clearHome();
|
||||
this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.HARNESS_GOGGLES_UP, this.getSoundSource(), 1.0F, 1.0F);
|
||||
- this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.HARNESS_GOGGLES_UP, this.getSoundSource(), 1.0F, 1.0F);
|
||||
+ this.playSound(SoundEvents.HARNESS_GOGGLES_UP, 1.0F, 1.0F); // Paper - don't play sound if silenced (Fixes MC-297169)
|
||||
}
|
||||
+ return true; // Paper - cancellable passengers
|
||||
}
|
||||
|
||||
+4
-4
@@ -43,15 +43,15 @@
|
||||
} else {
|
||||
if (!this.isRemoved()) {
|
||||
+ // CraftBukkit start - fire break events
|
||||
+ Entity damager = (!source.isDirect() && source.getEntity() != null) ? source.getEntity() : source.getDirectEntity(); // Paper - fix DamageSource API
|
||||
+ org.bukkit.event.hanging.HangingBreakEvent event;
|
||||
+ Entity damager = source.getDirectEntity();
|
||||
+ final org.bukkit.event.hanging.HangingBreakEvent event;
|
||||
+ if (damager != null) {
|
||||
+ event = new org.bukkit.event.hanging.HangingBreakByEntityEvent((org.bukkit.entity.Hanging) this.getBukkitEntity(), damager.getBukkitEntity(), source.is(net.minecraft.tags.DamageTypeTags.IS_EXPLOSION) ? org.bukkit.event.hanging.HangingBreakEvent.RemoveCause.EXPLOSION : org.bukkit.event.hanging.HangingBreakEvent.RemoveCause.ENTITY);
|
||||
+ event = new org.bukkit.event.hanging.HangingBreakByEntityEvent((org.bukkit.entity.Hanging) this.getBukkitEntity(), damager.getBukkitEntity(), new org.bukkit.craftbukkit.damage.CraftDamageSource(source), source.is(net.minecraft.tags.DamageTypeTags.IS_EXPLOSION) ? org.bukkit.event.hanging.HangingBreakEvent.RemoveCause.EXPLOSION : org.bukkit.event.hanging.HangingBreakEvent.RemoveCause.ENTITY);
|
||||
+ } else {
|
||||
+ event = new org.bukkit.event.hanging.HangingBreakEvent((org.bukkit.entity.Hanging) this.getBukkitEntity(), source.is(net.minecraft.tags.DamageTypeTags.IS_EXPLOSION) ? org.bukkit.event.hanging.HangingBreakEvent.RemoveCause.EXPLOSION : org.bukkit.event.hanging.HangingBreakEvent.RemoveCause.DEFAULT);
|
||||
+ }
|
||||
+
|
||||
+ this.level().getCraftServer().getPluginManager().callEvent(event);
|
||||
+ event.callEvent();
|
||||
+
|
||||
+ if (this.isRemoved() || event.isCancelled()) {
|
||||
+ return true;
|
||||
|
||||
+3
-3
@@ -8,7 +8,7 @@
|
||||
+ this.splash(hitResult);
|
||||
+ }
|
||||
+
|
||||
+ public void splash(final HitResult hitResult) {
|
||||
+ public void splash(final @org.jspecify.annotations.Nullable HitResult hitResult) { // Paper
|
||||
+ // Paper end - More projectile API
|
||||
if (this.level() instanceof ServerLevel level) {
|
||||
ItemStack potionItemStack = this.getItem();
|
||||
@@ -37,7 +37,7 @@
|
||||
- private void onHitAsWater(final ServerLevel level) {
|
||||
+ private static final Predicate<LivingEntity> APPLY_WATER_GET_ENTITIES_PREDICATE = AbstractThrownPotion.WATER_SENSITIVE_OR_ON_FIRE.or(Axolotl.class::isInstance); // Paper - Fix potions splash events
|
||||
+
|
||||
+ private boolean onHitAsWater(final ServerLevel level, final HitResult hitResult) { // Paper - Fix potions splash events
|
||||
+ private boolean onHitAsWater(final ServerLevel level, final @org.jspecify.annotations.Nullable HitResult hitResult) { // Paper - Fix potions splash events
|
||||
AABB aabb = this.getBoundingBox().inflate(4.0, 2.0, 4.0);
|
||||
|
||||
- for (LivingEntity entity : this.level().getEntitiesOfClass(LivingEntity.class, aabb, WATER_SENSITIVE_OR_ON_FIRE)) {
|
||||
@@ -88,7 +88,7 @@
|
||||
}
|
||||
|
||||
- protected abstract void onHitAsPotion(ServerLevel level, ItemStack potionItem, HitResult hitResult);
|
||||
+ protected abstract boolean onHitAsPotion(ServerLevel level, ItemStack potionItem, HitResult hitResult); // Paper - Fix potions splash events & More Projectile API
|
||||
+ protected abstract boolean onHitAsPotion(ServerLevel level, ItemStack potionItem, @org.jspecify.annotations.Nullable HitResult hitResult); // Paper - Fix potions splash events & More Projectile API
|
||||
|
||||
private void dowseFire(final BlockPos pos) {
|
||||
BlockState blockState = this.level().getBlockState(pos);
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
|
||||
@Override
|
||||
- public void onHitAsPotion(final ServerLevel level, final ItemStack potionItem, final HitResult hitResult) {
|
||||
+ public boolean onHitAsPotion(final ServerLevel level, final ItemStack potionItem, final HitResult hitResult) { // Paper - More projectile API
|
||||
+ public boolean onHitAsPotion(final ServerLevel level, final ItemStack potionItem, final @org.jspecify.annotations.Nullable HitResult hitResult) { // Paper - More projectile API
|
||||
AreaEffectCloud cloud = new AreaEffectCloud(this.level(), this.getX(), this.getY(), this.getZ());
|
||||
if (this.getOwner() instanceof LivingEntity owner) {
|
||||
cloud.setOwner(owner);
|
||||
|
||||
+3
-2
@@ -5,11 +5,12 @@
|
||||
|
||||
@Override
|
||||
- public void onHitAsPotion(final ServerLevel level, final ItemStack potionItem, final HitResult hitResult) {
|
||||
+ public boolean onHitAsPotion(final ServerLevel level, final ItemStack potionItem, final HitResult hitResult) { // Paper - More projectile API
|
||||
+ public boolean onHitAsPotion(final ServerLevel level, final ItemStack potionItem, final @org.jspecify.annotations.Nullable HitResult hitResult) { // Paper - More projectile API
|
||||
PotionContents contents = potionItem.getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY);
|
||||
float durationScale = potionItem.getOrDefault(DataComponents.POTION_DURATION_SCALE, 1.0F);
|
||||
Iterable<MobEffectInstance> mobEffects = contents.getAllEffects();
|
||||
AABB potionAabb = this.getBoundingBox().move(hitResult.getLocation().subtract(this.position()));
|
||||
- AABB potionAabb = this.getBoundingBox().move(hitResult.getLocation().subtract(this.position()));
|
||||
+ AABB potionAabb = hitResult == null ? this.getBoundingBox() : this.getBoundingBox().move(hitResult.getLocation().subtract(this.position())); // Paper - More projectile API
|
||||
AABB effectAabb = potionAabb.inflate(4.0, 2.0, 4.0);
|
||||
List<LivingEntity> entities = this.level().getEntitiesOfClass(LivingEntity.class, effectAabb);
|
||||
+ java.util.Map<org.bukkit.entity.LivingEntity, Double> affected = new java.util.HashMap<>(); // CraftBukkit
|
||||
|
||||
+8
-6
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/world/entity/vehicle/VehicleEntity.java
|
||||
+++ b/net/minecraft/world/entity/vehicle/VehicleEntity.java
|
||||
@@ -32,12 +_,20 @@
|
||||
@@ -32,12 +_,22 @@
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -13,9 +13,11 @@
|
||||
} else {
|
||||
+ // CraftBukkit start
|
||||
+ org.bukkit.entity.Vehicle vehicle = (org.bukkit.entity.Vehicle) this.getBukkitEntity();
|
||||
+ org.bukkit.entity.Entity attacker = (source.getEntity() == null) ? null : source.getEntity().getBukkitEntity();
|
||||
+
|
||||
+ org.bukkit.event.vehicle.VehicleDamageEvent event = new org.bukkit.event.vehicle.VehicleDamageEvent(vehicle, attacker, damage);
|
||||
+ org.bukkit.craftbukkit.damage.CraftDamageSource damageSourceBukkit = new org.bukkit.craftbukkit.damage.CraftDamageSource(source);
|
||||
+ org.bukkit.entity.Entity attacker = net.minecraft.Optionull.map(
|
||||
+ source.eventEntityDamager() != null ? source.eventEntityDamager() : source.getDirectEntity(), Entity::getBukkitEntity
|
||||
+ );
|
||||
+ org.bukkit.event.vehicle.VehicleDamageEvent event = new org.bukkit.event.vehicle.VehicleDamageEvent(vehicle, damageSourceBukkit, attacker, damage);
|
||||
+ if (!event.callEvent()) return false;
|
||||
+ damage = (float) event.getDamage();
|
||||
+ // CraftBukkit end
|
||||
@@ -28,7 +30,7 @@
|
||||
if (creativePlayer) {
|
||||
- this.discard();
|
||||
+ // CraftBukkit start
|
||||
+ org.bukkit.event.vehicle.VehicleDestroyEvent destroyEvent = new org.bukkit.event.vehicle.VehicleDestroyEvent(vehicle, attacker);
|
||||
+ org.bukkit.event.vehicle.VehicleDestroyEvent destroyEvent = new org.bukkit.event.vehicle.VehicleDestroyEvent(vehicle, damageSourceBukkit, attacker);
|
||||
+ if (!destroyEvent.callEvent()) {
|
||||
+ this.setDamage(40.0F); // Maximize damage so this doesn't get triggered again right away
|
||||
+ return true;
|
||||
@@ -38,7 +40,7 @@
|
||||
}
|
||||
} else {
|
||||
+ // CraftBukkit start
|
||||
+ org.bukkit.event.vehicle.VehicleDestroyEvent destroyEvent = new org.bukkit.event.vehicle.VehicleDestroyEvent(vehicle, attacker);
|
||||
+ org.bukkit.event.vehicle.VehicleDestroyEvent destroyEvent = new org.bukkit.event.vehicle.VehicleDestroyEvent(vehicle, damageSourceBukkit, attacker);
|
||||
+ if (!destroyEvent.callEvent()) {
|
||||
+ this.setDamage(40.0F); // Maximize damage so this doesn't get triggered again right away
|
||||
+ return true;
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
+ this.spigotConfig = new org.spigotmc.SpigotWorldConfig(bukkitName, CraftNamespacedKey.fromMinecraft(dimension.identifier())); // Spigot
|
||||
+ this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config
|
||||
+ this.generator = generator;
|
||||
+ this.world = new CraftWorld((ServerLevel) this, generator, biomeProvider, environment);
|
||||
+ this.world = new CraftWorld((ServerLevel) this, dimension.identifier(), biomeProvider, environment);
|
||||
+
|
||||
+ for (SpawnCategory spawnCategory : SpawnCategory.values()) {
|
||||
+ if (org.bukkit.craftbukkit.util.CraftSpawnCategory.isValidForLimits(spawnCategory)) {
|
||||
@@ -528,26 +528,6 @@
|
||||
this.getChunkAt(pos).addAndRegisterBlockEntity(blockEntity);
|
||||
}
|
||||
}
|
||||
@@ -883,7 +_,7 @@
|
||||
}
|
||||
|
||||
public long getDefaultClockTime() {
|
||||
- return this.getClockTimeTicks(this.dimensionType().defaultClock());
|
||||
+ return this.getClockTimeTicks(this.getClock());
|
||||
}
|
||||
|
||||
private long getClockTimeTicks(final Optional<? extends Holder<WorldClock>> clock) {
|
||||
@@ -1032,6 +_,10 @@
|
||||
return this.dimensionTypeRegistration;
|
||||
}
|
||||
|
||||
+ public Optional<Holder<WorldClock>> getClock() {
|
||||
+ return this.dimensionType().defaultClock();
|
||||
+ }
|
||||
+
|
||||
public ResourceKey<Level> dimension() {
|
||||
return this.dimension;
|
||||
}
|
||||
@@ -1110,7 +_,8 @@
|
||||
BLOCK("block"),
|
||||
MOB("mob"),
|
||||
|
||||
+9
@@ -1,5 +1,14 @@
|
||||
--- a/net/minecraft/world/level/block/ComparatorBlock.java
|
||||
+++ b/net/minecraft/world/level/block/ComparatorBlock.java
|
||||
@@ -137,7 +_,7 @@
|
||||
float pitch = state.getValue(MODE) == ComparatorMode.SUBTRACT ? 0.55F : 0.5F;
|
||||
level.playSound(player, pos, SoundEvents.COMPARATOR_CLICK, SoundSource.BLOCKS, 0.3F, pitch);
|
||||
level.setBlock(pos, state, Block.UPDATE_CLIENTS);
|
||||
- this.refreshOutputState(level, pos, state);
|
||||
+ if (level.getBlockState(pos).is(this)) { this.refreshOutputState(level, pos, state); } // Paper - Fix blockstate validation issues
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -168,8 +_,18 @@
|
||||
boolean sourceOn = this.shouldTurnOn(level, pos, state);
|
||||
boolean isOn = state.getValue(POWERED);
|
||||
|
||||
@@ -19,3 +19,11 @@
|
||||
level.setBlock(pos, state.setValue(POWERED, true), Block.UPDATE_CLIENTS);
|
||||
if (!shouldTurnOn) {
|
||||
level.scheduleTick(pos, this, this.getDelay(state), TickPriority.VERY_HIGH);
|
||||
@@ -85,6 +_,7 @@
|
||||
protected void neighborChanged(
|
||||
final BlockState state, final Level level, final BlockPos pos, final Block block, final @Nullable Orientation orientation, final boolean movedByPiston
|
||||
) {
|
||||
+ if (!level.getBlockState(pos).is(this)) return; // Paper - Fix blockstate validation issues
|
||||
if (state.canSurvive(level, pos)) {
|
||||
this.checkTickOnNeighbor(level, pos, state);
|
||||
} else {
|
||||
+11
-1
@@ -17,7 +17,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +_,7 @@
|
||||
@@ -60,12 +_,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,3 +26,13 @@
|
||||
}
|
||||
|
||||
private boolean slightlyMelt(final BlockState state, final Level level, final BlockPos pos) {
|
||||
int age = state.getValue(AGE);
|
||||
if (age < 3) {
|
||||
+ // Paper start - FrostedIce BlockFadeEvent
|
||||
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(level, pos, state.setValue(AGE, age + 1)).isCancelled()) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Paper end - FrostedIce BlockFadeEvent
|
||||
level.setBlock(pos, state.setValue(AGE, age + 1), Block.UPDATE_CLIENTS);
|
||||
return false;
|
||||
} else {
|
||||
+4
-2
@@ -8,7 +8,7 @@
|
||||
|
||||
public BeehiveBlockEntity(final BlockPos worldPosition, final BlockState blockState) {
|
||||
super(BlockEntityType.BEEHIVE, worldPosition, blockState);
|
||||
@@ -119,7 +_,7 @@
|
||||
@@ -119,16 +_,17 @@
|
||||
}
|
||||
|
||||
public boolean isFull() {
|
||||
@@ -17,7 +17,9 @@
|
||||
}
|
||||
|
||||
public void emptyAllLivingFromHive(final @Nullable Player player, final BlockState state, final BeehiveBlockEntity.BeeReleaseStatus releaseReason) {
|
||||
@@ -128,7 +_,7 @@
|
||||
+ if (this.level == null) return; // Paper - do not fire from worldgen thread
|
||||
List<Entity> releasedFromHive = this.releaseAllOccupants(state, releaseReason);
|
||||
if (player != null) {
|
||||
for (Entity released : releasedFromHive) {
|
||||
if (released instanceof Bee bee && player.position().distanceToSqr(released.position()) <= 16.0) {
|
||||
if (!this.isSedated()) {
|
||||
|
||||
+14
-1
@@ -62,11 +62,24 @@
|
||||
if (this.openCount == 0) {
|
||||
this.onClose(level, pos, blockState);
|
||||
level.gameEvent(entity, GameEvent.CONTAINER_CLOSE, pos);
|
||||
@@ -74,6 +_,7 @@
|
||||
@@ -74,8 +_,20 @@
|
||||
}
|
||||
|
||||
int openCount = containerUsers.size();
|
||||
+ if (this.opened) openCount++; // CraftBukkit - add dummy count from API
|
||||
int prevCount = this.openCount;
|
||||
if (prevCount != openCount) {
|
||||
+ // CraftBukkit start - Call redstone event
|
||||
+ if (level.getBlockState(pos).is(net.minecraft.world.level.block.Blocks.TRAPPED_CHEST)) {
|
||||
+ int oldPower = net.minecraft.util.Mth.clamp(prevCount, 0, 15);
|
||||
+ int newPower = net.minecraft.util.Mth.clamp(openCount, 0, 15);
|
||||
+
|
||||
+ if (oldPower != newPower) {
|
||||
+ org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(level, pos, oldPower, newPower);
|
||||
+ }
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
boolean isOpen = openCount != 0;
|
||||
boolean wasOpen = prevCount != 0;
|
||||
if (isOpen && !wasOpen) {
|
||||
-9
@@ -1,14 +1,5 @@
|
||||
--- a/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
@@ -47,7 +_,7 @@
|
||||
protected final RegionBitmap usedSectors = new RegionBitmap();
|
||||
|
||||
public RegionFile(final RegionStorageInfo info, final Path path, final Path externalFileDir, final boolean sync) throws IOException {
|
||||
- this(info, path, externalFileDir, RegionFileVersion.getSelected(), sync);
|
||||
+ this(info, path, externalFileDir, RegionFileVersion.getCompressionFormat(), sync); // Paper - Configurable region compression format
|
||||
}
|
||||
|
||||
public RegionFile(final RegionStorageInfo info, final Path path, final Path externalFileDir, final RegionFileVersion version, final boolean sync) throws IOException {
|
||||
@@ -83,6 +_,14 @@
|
||||
if (offset != 0) {
|
||||
int sectorNumber = getSectorNumber(offset);
|
||||
|
||||
+9
-17
@@ -1,19 +1,11 @@
|
||||
--- a/net/minecraft/world/level/chunk/storage/RegionFileVersion.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/RegionFileVersion.java
|
||||
@@ -49,6 +_,16 @@
|
||||
private final RegionFileVersion.StreamWrapper<InputStream> inputWrapper;
|
||||
private final RegionFileVersion.StreamWrapper<OutputStream> outputWrapper;
|
||||
|
||||
+ // Paper start - Configurable region compression format
|
||||
+ public static RegionFileVersion getCompressionFormat() {
|
||||
+ return switch (io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.compressionFormat) {
|
||||
+ case GZIP -> VERSION_GZIP;
|
||||
+ case ZLIB -> VERSION_DEFLATE;
|
||||
+ case LZ4 -> VERSION_LZ4;
|
||||
+ case NONE -> VERSION_NONE;
|
||||
+ };
|
||||
+ }
|
||||
+ // Paper end - Configurable region compression format
|
||||
private RegionFileVersion(
|
||||
final int id,
|
||||
final @Nullable String optionName,
|
||||
@@ -24,7 +_,7 @@
|
||||
private static final Int2ObjectMap<RegionFileVersion> VERSIONS = new Int2ObjectOpenHashMap<>();
|
||||
private static final Object2ObjectMap<String, RegionFileVersion> VERSIONS_BY_NAME = new Object2ObjectOpenHashMap<>();
|
||||
public static final RegionFileVersion VERSION_GZIP = register(
|
||||
- new RegionFileVersion(1, null, in -> new FastBufferedInputStream(new GZIPInputStream(in)), out -> new BufferedOutputStream(new GZIPOutputStream(out)))
|
||||
+ new RegionFileVersion(1, "gzip", in -> new FastBufferedInputStream(new GZIPInputStream(in)), out -> new BufferedOutputStream(new GZIPOutputStream(out))) // Paper - Enable for Configurable region compression format
|
||||
);
|
||||
public static final RegionFileVersion VERSION_DEFLATE = register(
|
||||
new RegionFileVersion(
|
||||
+10
-1
@@ -5,7 +5,7 @@
|
||||
if (spawnEnemies) {
|
||||
if (level.getGameRules().get(GameRules.SPAWN_PHANTOMS)) {
|
||||
+ // Paper start - Ability to control player's insomnia and phantoms
|
||||
+ if (level.paperConfig().entities.behavior.phantomsSpawnAttemptMaxSeconds <= 0) {
|
||||
+ if (level.paperConfig().entities.behavior.phantomsSpawnAttemptMaxSeconds <= 0 || level.paperConfig().entities.behavior.playerInsomniaStartTicks < 0) {
|
||||
+ return;
|
||||
+ }
|
||||
+ // Paper end - Ability to control player's insomnia and phantoms
|
||||
@@ -25,6 +25,15 @@
|
||||
BlockPos playerPos = player.blockPosition();
|
||||
if (!level.dimensionType().hasSkyLight() || playerPos.getY() >= level.getSeaLevel() && level.canSeeSky(playerPos)) {
|
||||
DifficultyInstance difficulty = level.getCurrentDifficultyAt(playerPos);
|
||||
@@ -39,7 +_,7 @@
|
||||
ServerStatsCounter stats = player.getStats();
|
||||
int value = Mth.clamp(stats.getValue(Stats.CUSTOM.get(Stats.TIME_SINCE_REST)), 1, Integer.MAX_VALUE);
|
||||
int dayLength = 24000;
|
||||
- if (random.nextInt(value) >= 72000) {
|
||||
+ if (random.nextInt(value) >= level.paperConfig().entities.behavior.playerInsomniaStartTicks) { // Paper - Ability to control player's insomnia and phantoms
|
||||
BlockPos spawnPos = playerPos.above(20 + random.nextInt(15))
|
||||
.east(-10 + random.nextInt(21))
|
||||
.south(-10 + random.nextInt(21));
|
||||
@@ -50,11 +_,21 @@
|
||||
int groupSize = 1 + random.nextInt(difficulty.getDifficulty().getId() + 1);
|
||||
|
||||
|
||||
+6
-1
@@ -15,7 +15,7 @@
|
||||
.mapOrElse(
|
||||
Function.identity(),
|
||||
error -> {
|
||||
@@ -174,9 +_,9 @@
|
||||
@@ -174,13 +_,13 @@
|
||||
}
|
||||
|
||||
public static <T extends SavedData> DataResult<T> readExistingSavedData(
|
||||
@@ -27,6 +27,11 @@
|
||||
|
||||
CompoundTag fileContents;
|
||||
try {
|
||||
- fileContents = NbtIo.readCompressed(dataLocation, NbtAccounter.defaultQuota());
|
||||
+ fileContents = NbtIo.readCompressed(dataLocation, NbtAccounter.create(net.minecraft.nbt.NbtAccounter.DEFAULT_NBT_QUOTA * 50L)); // Paper - raise quota to account for custom data
|
||||
} catch (IOException var6) {
|
||||
return DataResult.error(var6::getMessage);
|
||||
}
|
||||
@@ -396,7 +_,7 @@
|
||||
|
||||
public LevelStorageSource.LevelStorageAccess validateAndCreateAccess(final String levelId) throws IOException, ContentValidationException {
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
--- a/net/minecraft/world/timeline/Timeline.java
|
||||
+++ b/net/minecraft/world/timeline/Timeline.java
|
||||
@@ -49,7 +_,7 @@
|
||||
public static final Codec<Timeline> NETWORK_CODEC = DIRECT_CODEC.xmap(Timeline::filterSyncableTracks, Timeline::filterSyncableTracks);
|
||||
private final Holder<WorldClock> clock;
|
||||
private final Optional<Integer> periodTicks;
|
||||
- private final Map<EnvironmentAttribute<?>, AttributeTrack<?, ?>> tracks;
|
||||
+ public final Map<EnvironmentAttribute<?>, AttributeTrack<?, ?>> tracks;
|
||||
private final Map<ResourceKey<ClockTimeMarker>, Timeline.TimeMarkerInfo> timeMarkers;
|
||||
|
||||
private static Timeline filterSyncableTracks(final Timeline timeline) {
|
||||
@@ -179,6 +_,11 @@
|
||||
KeyframeTrack.Builder<Argument> argumentTrack = new KeyframeTrack.Builder<>();
|
||||
builder.accept(argumentTrack);
|
||||
this.tracks.put(attribute, new AttributeTrack<>(modifier, argumentTrack.build()));
|
||||
+ return this;
|
||||
+ }
|
||||
+
|
||||
+ public Timeline.Builder addTracks(final Map<EnvironmentAttribute<?>, AttributeTrack<?, ?>> tracks) {
|
||||
+ this.tracks.putAll(tracks);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,9 @@ import org.bukkit.GameRule;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.craftbukkit.CraftGameRule;
|
||||
import org.bukkit.craftbukkit.block.CraftBiome;
|
||||
import io.papermc.paper.entity.poi.PaperPoiType;
|
||||
import io.papermc.paper.entity.poi.PoiType;
|
||||
import net.minecraft.world.entity.ai.village.poi.PoiManager;
|
||||
import org.bukkit.craftbukkit.damage.CraftDamageEffect;
|
||||
import org.bukkit.craftbukkit.damage.CraftDamageSource;
|
||||
import org.bukkit.craftbukkit.entity.CraftLivingEntity;
|
||||
@@ -62,6 +65,11 @@ public class PaperServerInternalAPIBridge implements InternalAPIBridge {
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PoiType.Occupancy createOccupancy(final String enumNameEntry) {
|
||||
return new PaperPoiType.PaperOccupancy(PoiManager.Occupancy.valueOf(enumNameEntry));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CombatEntry createCombatEntry(
|
||||
final DamageSource damageSource,
|
||||
|
||||
@@ -94,8 +94,9 @@ public final class EntityCommand implements PaperSubcommand {
|
||||
return;
|
||||
}
|
||||
Map<Identifier, MutablePair<Integer, Map<ChunkPos, Integer>>> list = Maps.newHashMap();
|
||||
@Nullable World bukkitWorld = Bukkit.getWorld(NamespacedKey.fromString(worldName));
|
||||
if (bukkitWorld == null) {
|
||||
@Nullable NamespacedKey worldKey = NamespacedKey.fromString(worldName);
|
||||
@Nullable World bukkitWorld;
|
||||
if (worldKey == null || (bukkitWorld = Bukkit.getWorld(worldKey)) == null) {
|
||||
sender.sendMessage(text("Could not load world for " + worldName + ". Please select a valid world.", RED));
|
||||
sender.sendMessage(text("Usage: /paper entity list [filter] [worldKey]", RED));
|
||||
return;
|
||||
|
||||
@@ -8,6 +8,7 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.ToIntFunction;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.ComponentLike;
|
||||
import net.kyori.adventure.text.JoinConfiguration;
|
||||
@@ -91,7 +92,7 @@ public final class MobcapsCommand implements PaperSubcommand {
|
||||
if (sender instanceof Player player) {
|
||||
worlds = List.of(player.getWorld());
|
||||
} else {
|
||||
sender.sendMessage(Component.text("Must specify a world! ex: '/paper mobcaps world'", NamedTextColor.RED));
|
||||
sender.sendMessage(Component.text("Must specify a world! ex: '/paper mobcaps overworld'", NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
} else if (args.length == 1) {
|
||||
@@ -99,8 +100,9 @@ public final class MobcapsCommand implements PaperSubcommand {
|
||||
if (input.equals("*")) {
|
||||
worlds = Bukkit.getWorlds();
|
||||
} else {
|
||||
final @Nullable World world = Bukkit.getWorld(input);
|
||||
if (world == null) {
|
||||
final @Nullable Key worldKey = NamespacedKey.fromString(input);
|
||||
final @Nullable World world;
|
||||
if (worldKey == null || (world = Bukkit.getWorld(worldKey)) == null) {
|
||||
sender.sendMessage(Component.text("'" + input + "' is not a valid world!", NamedTextColor.RED));
|
||||
return;
|
||||
} else {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package io.papermc.paper.configuration;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import io.papermc.paper.FeatureHooks;
|
||||
import io.papermc.paper.configuration.constraint.Constraints;
|
||||
import io.papermc.paper.configuration.serializer.collection.map.WriteKeyBack;
|
||||
import io.papermc.paper.configuration.type.number.DoubleOr;
|
||||
@@ -183,19 +182,10 @@ public class GlobalConfiguration extends ConfigurationPart {
|
||||
public boolean allowHeadlessPistons = false;
|
||||
@Comment("This setting controls if the vanilla damage tick should be skipped if damage was blocked via a shield.")
|
||||
public boolean skipVanillaDamageTickWhenShieldBlocked = false;
|
||||
@Comment("This setting controls what compression format is used for region files.")
|
||||
public CompressionFormat compressionFormat = CompressionFormat.ZLIB;
|
||||
@Comment("This setting controls if equipment should be updated when handling certain player actions.")
|
||||
public boolean updateEquipmentOnPlayerActions = true;
|
||||
@Comment("This setting controls what item data components don't need to be sanitized in oversized item obfuscation. Adding them re-enables exploits, but may be needed for certain resource packs. (Expected: minecraft:container, minecraft:charged_projectiles and minecraft:bundle_contents)")
|
||||
public OversizedItemComponentSanitizer.AssetOversizedItemComponentSanitizerConfiguration oversizedItemComponentSanitizer = new OversizedItemComponentSanitizer.AssetOversizedItemComponentSanitizerConfiguration(Set.of());
|
||||
|
||||
public enum CompressionFormat {
|
||||
GZIP,
|
||||
ZLIB,
|
||||
LZ4,
|
||||
NONE
|
||||
}
|
||||
}
|
||||
|
||||
public Commands commands;
|
||||
@@ -344,6 +334,8 @@ public class GlobalConfiguration extends ConfigurationPart {
|
||||
public boolean enableNether = true;
|
||||
@Comment("Keeps Paper's fix for MC-159283 enabled. Disable to use vanilla End ring terrain.")
|
||||
public boolean fixFarEndTerrainGeneration = true;
|
||||
@Comment("Fix for MC-301114. This removes the oldest combat entry when it hits the cap, to fix a memory leak on constant entity damage.")
|
||||
public IntOr.Disabled maxTrackingCombatEntries = new IntOr.Disabled(OptionalInt.of(10240));
|
||||
}
|
||||
|
||||
public BlockUpdates blockUpdates;
|
||||
|
||||
@@ -51,6 +51,7 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@@ -150,7 +151,7 @@ public class PaperConfigurations extends Configurations<GlobalConfiguration, Wor
|
||||
""";
|
||||
|
||||
@VisibleForTesting
|
||||
public static final Supplier<SpigotWorldConfig> SPIGOT_WORLD_DEFAULTS = Suppliers.memoize(() -> new SpigotWorldConfig(RandomStringUtils.randomAlphabetic(255), Key.key(RandomStringUtils.randomAlphabetic(255).toLowerCase())) {
|
||||
public static final Supplier<SpigotWorldConfig> SPIGOT_WORLD_DEFAULTS = Suppliers.memoize(() -> new SpigotWorldConfig(RandomStringUtils.randomAlphabetic(255), Key.key(RandomStringUtils.randomAlphabetic(255).toLowerCase(Locale.ROOT))) {
|
||||
@Override // override to ensure "verbose" is false
|
||||
public void init() {
|
||||
SpigotConfig.readConfig(SpigotWorldConfig.class, this);
|
||||
|
||||
@@ -86,6 +86,7 @@ interface RemovedConfigurations {
|
||||
path("misc", "fix-entity-position-desync"),
|
||||
path("chunk-system", "gen-parallelism"),
|
||||
path("logging"), // server is no longer obfuscated since 26.1
|
||||
path("unsupported-settings", "compression-format"),
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package io.papermc.paper.entity.poi;
|
||||
|
||||
import net.minecraft.world.entity.ai.village.poi.PoiRecord;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.util.CraftLocation;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public record PaperPoiSearchResult(PoiType poiType, Location location) implements PoiSearchResult {
|
||||
|
||||
@Override
|
||||
public Location location() {
|
||||
return location.clone();
|
||||
}
|
||||
|
||||
public static PoiSearchResult from(PoiRecord record, World world) {
|
||||
return new PaperPoiSearchResult(PaperPoiType.minecraftHolderToBukkit(record.getPoiType()), CraftLocation.toBukkit(record.getPos(), world));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package io.papermc.paper.entity.poi;
|
||||
|
||||
import io.papermc.paper.registry.HolderableBase;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.world.entity.ai.village.poi.PoiManager;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.craftbukkit.CraftRegistry;
|
||||
import org.bukkit.craftbukkit.block.data.CraftBlockData;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public class PaperPoiType extends HolderableBase<net.minecraft.world.entity.ai.village.poi.PoiType> implements PoiType {
|
||||
|
||||
public static PaperPoiType minecraftHolderToBukkit(final Holder<net.minecraft.world.entity.ai.village.poi.PoiType> minecraft) {
|
||||
return CraftRegistry.minecraftHolderToBukkit(minecraft, Registries.POINT_OF_INTEREST_TYPE);
|
||||
}
|
||||
|
||||
public static Holder<net.minecraft.world.entity.ai.village.poi.PoiType> bukkitToMinecraftHolder(final PoiType bukkit) {
|
||||
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
|
||||
}
|
||||
|
||||
public PaperPoiType(final Holder<net.minecraft.world.entity.ai.village.poi.PoiType> holder) {
|
||||
super(holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean is(final BlockData data) {
|
||||
return this.getHandle().is(((CraftBlockData) data).getState());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasOccupants() {
|
||||
return this.getHandle().maxTickets() != 0;
|
||||
}
|
||||
|
||||
public record PaperOccupancy(PoiManager.Occupancy handle) implements Occupancy {
|
||||
public static PoiManager.Occupancy bukkitToMinecraft(final Occupancy occupancy) {
|
||||
return ((PaperOccupancy) occupancy).handle();
|
||||
}
|
||||
|
||||
public static Occupancy minecraftToBukkit(final PoiManager.Occupancy occupancy) {
|
||||
return switch (occupancy) {
|
||||
case ANY -> Occupancy.ANY;
|
||||
case HAS_SPACE -> Occupancy.HAS_SPACE;
|
||||
case IS_OCCUPIED -> Occupancy.IS_OCCUPIED;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import io.papermc.paper.datacomponent.DataComponentTypes;
|
||||
import io.papermc.paper.datacomponent.PaperDataComponentType;
|
||||
import io.papermc.paper.dialog.Dialog;
|
||||
import io.papermc.paper.dialog.PaperDialog;
|
||||
import io.papermc.paper.entity.poi.PaperPoiType;
|
||||
import io.papermc.paper.entity.poi.PoiTypes;
|
||||
import io.papermc.paper.registry.data.PaperBannerPatternRegistryEntry;
|
||||
import io.papermc.paper.registry.data.PaperCatTypeRegistryEntry;
|
||||
import io.papermc.paper.registry.data.PaperChickenVariantRegistryEntry;
|
||||
@@ -19,16 +21,12 @@ import io.papermc.paper.registry.data.PaperJukeboxSongRegistryEntry;
|
||||
import io.papermc.paper.registry.data.PaperPaintingVariantRegistryEntry;
|
||||
import io.papermc.paper.registry.data.PaperPigVariantRegistryEntry;
|
||||
import io.papermc.paper.registry.data.PaperSoundEventRegistryEntry;
|
||||
import io.papermc.paper.registry.data.PaperTimelineRegistryEntry;
|
||||
import io.papermc.paper.registry.data.PaperWolfVariantRegistryEntry;
|
||||
import io.papermc.paper.registry.data.PaperWorldClockRegistryEntry;
|
||||
import io.papermc.paper.registry.data.PaperZombieNautilusVariantRegistryEntry;
|
||||
import io.papermc.paper.registry.data.dialog.PaperDialogRegistryEntry;
|
||||
import io.papermc.paper.registry.entry.RegistryEntry;
|
||||
import io.papermc.paper.registry.entry.RegistryEntryMeta;
|
||||
import io.papermc.paper.registry.tag.TagKey;
|
||||
import io.papermc.paper.world.Timeline;
|
||||
import io.papermc.paper.world.WorldClock;
|
||||
import java.util.Collections;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
@@ -56,8 +54,6 @@ import org.bukkit.craftbukkit.CraftGameRule;
|
||||
import org.bukkit.craftbukkit.CraftJukeboxSong;
|
||||
import org.bukkit.craftbukkit.CraftMusicInstrument;
|
||||
import org.bukkit.craftbukkit.CraftSound;
|
||||
import org.bukkit.craftbukkit.CraftTimeline;
|
||||
import org.bukkit.craftbukkit.CraftWorldClock;
|
||||
import org.bukkit.craftbukkit.attribute.CraftAttribute;
|
||||
import org.bukkit.craftbukkit.block.CraftBiome;
|
||||
import org.bukkit.craftbukkit.block.CraftBlockType;
|
||||
@@ -127,6 +123,7 @@ public final class PaperRegistries {
|
||||
start(Registries.SOUND_EVENT, RegistryKey.SOUND_EVENT).craft(Sound.class, CraftSound::new, true).create(PaperSoundEventRegistryEntry.PaperBuilder::new, RegistryEntryMeta.RegistryModificationApiSupport.NONE),
|
||||
start(Registries.DATA_COMPONENT_TYPE, RegistryKey.DATA_COMPONENT_TYPE).craft(DataComponentTypes.class, PaperDataComponentType::of).build(),
|
||||
start(Registries.GAME_RULE, RegistryKey.GAME_RULE).craft(GameRule.class, CraftGameRule::new).build(),
|
||||
start(Registries.POINT_OF_INTEREST_TYPE, RegistryKey.POINT_OF_INTEREST_TYPE).craft(PoiTypes.class, PaperPoiType::new).build(),
|
||||
|
||||
// data-driven
|
||||
start(Registries.BIOME, RegistryKey.BIOME).craft(Biome.class, CraftBiome::new).build().delayed(),
|
||||
@@ -152,8 +149,6 @@ public final class PaperRegistries {
|
||||
start(Registries.PIG_SOUND_VARIANT, RegistryKey.PIG_SOUND_VARIANT).craft(Pig.SoundVariant.class, CraftPig.CraftSoundVariant::new).build(),
|
||||
start(Registries.ZOMBIE_NAUTILUS_VARIANT, RegistryKey.ZOMBIE_NAUTILUS_VARIANT).craft(ZombieNautilus.Variant.class, CraftZombieNautilus.CraftVariant::new).writable(PaperZombieNautilusVariantRegistryEntry.PaperBuilder::new),
|
||||
start(Registries.DIALOG, RegistryKey.DIALOG).craft(Dialog.class, PaperDialog::new, true).writable(PaperDialogRegistryEntry.PaperBuilder::new),
|
||||
start(Registries.WORLD_CLOCK, RegistryKey.WORLD_CLOCK).craft(WorldClock.class, CraftWorldClock::new).writable(PaperWorldClockRegistryEntry.PaperBuilder::new).delayed(),
|
||||
start(Registries.TIMELINE, RegistryKey.TIMELINE).craft(Timeline.class, CraftTimeline::new).writable(PaperTimelineRegistryEntry.PaperBuilder::new).delayed(),
|
||||
|
||||
// api-only
|
||||
start(Registries.ENTITY_TYPE, RegistryKey.ENTITY_TYPE).apiOnly(PaperSimpleRegistry::entityType),
|
||||
|
||||
-101
@@ -1,101 +0,0 @@
|
||||
package io.papermc.paper.registry.data;
|
||||
|
||||
import io.papermc.paper.adventure.PaperAdventure;
|
||||
import io.papermc.paper.registry.PaperRegistries;
|
||||
import io.papermc.paper.registry.PaperRegistryBuilder;
|
||||
import io.papermc.paper.registry.TypedKey;
|
||||
import io.papermc.paper.registry.data.util.Conversions;
|
||||
import io.papermc.paper.world.WorldClock;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.OptionalInt;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.world.timeline.Timeline;
|
||||
import org.jetbrains.annotations.Range;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import static io.papermc.paper.registry.data.util.Checks.asArgument;
|
||||
import static io.papermc.paper.registry.data.util.Checks.asArgumentMin;
|
||||
import static io.papermc.paper.registry.data.util.Checks.asConfigured;
|
||||
|
||||
public class PaperTimelineRegistryEntry implements TimelineRegistryEntry {
|
||||
|
||||
protected final Conversions conversions;
|
||||
protected @Nullable Timeline internal;
|
||||
protected @Nullable Holder<net.minecraft.world.clock.WorldClock> clock;
|
||||
protected OptionalInt periodTicks = OptionalInt.empty();
|
||||
protected final Map<Key, TimeMarker> timeMarkers = new LinkedHashMap<>();
|
||||
|
||||
public PaperTimelineRegistryEntry(final Conversions conversions, final @Nullable Timeline internal) {
|
||||
this.conversions = conversions;
|
||||
this.internal = internal;
|
||||
|
||||
if (internal == null) {
|
||||
return;
|
||||
}
|
||||
this.clock = internal.clock();
|
||||
internal.periodTicks().ifPresent(period -> this.periodTicks = OptionalInt.of(period));
|
||||
internal.registerTimeMarkers((key, marker) -> this.timeMarkers.put(PaperAdventure.asAdventure(key.identifier()), new TimeMarker(marker.ticks(), marker.showInCommands())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedKey<WorldClock> clock() {
|
||||
return PaperRegistries.fromNms(asConfigured(this.clock, "clock").unwrapKey().orElseThrow());
|
||||
}
|
||||
|
||||
@Override
|
||||
public OptionalInt periodTicks() {
|
||||
return this.periodTicks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Key, TimeMarker> timeMarkers() {
|
||||
return Map.copyOf(this.timeMarkers);
|
||||
}
|
||||
|
||||
public static final class PaperBuilder extends PaperTimelineRegistryEntry implements TimelineRegistryEntry.Builder, PaperRegistryBuilder<Timeline, io.papermc.paper.world.Timeline> {
|
||||
|
||||
public PaperBuilder(final Conversions conversions, final @Nullable Timeline internal) {
|
||||
super(conversions, internal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder clock(final TypedKey<WorldClock> clock) {
|
||||
this.clock = this.conversions.getReferenceHolder(PaperRegistries.toNms(asArgument(clock, "clock")));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder periodTicks(final @Range(from = 1, to = Integer.MAX_VALUE) int periodTicks) {
|
||||
this.periodTicks = OptionalInt.of(asArgumentMin(periodTicks, "periodTicks", 1));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder clearPeriodTicks() {
|
||||
this.periodTicks = OptionalInt.empty();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addTimeMarker(final Key key, final @Range(from = 0, to = Integer.MAX_VALUE) int ticks, final boolean showInCommands) {
|
||||
this.timeMarkers.put(asArgument(key, "key"), new TimeMarker(asArgumentMin(ticks, "ticks", 0), showInCommands));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Timeline build() {
|
||||
final Timeline.Builder builder = Timeline.builder(asConfigured(this.clock, "clock"));
|
||||
if (this.periodTicks.isPresent()) {
|
||||
builder.setPeriodTicks(this.periodTicks.getAsInt());
|
||||
}
|
||||
if (this.internal != null) {
|
||||
builder.addTracks(this.internal.tracks);
|
||||
}
|
||||
this.timeMarkers.forEach((key, marker) -> builder.addTimeMarker(ResourceKey.create(net.minecraft.world.clock.ClockTimeMarkers.ROOT_ID, PaperAdventure.asVanilla(key)), marker.ticks(), marker.showInCommands()));
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
package io.papermc.paper.registry.data;
|
||||
|
||||
import io.papermc.paper.registry.PaperRegistryBuilder;
|
||||
import io.papermc.paper.registry.data.util.Conversions;
|
||||
import net.minecraft.world.clock.WorldClock;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public class PaperWorldClockRegistryEntry implements WorldClockRegistryEntry {
|
||||
|
||||
public PaperWorldClockRegistryEntry(final Conversions ignoredConversions, final @Nullable WorldClock ignoredInternal) {
|
||||
}
|
||||
|
||||
public static final class PaperBuilder extends PaperWorldClockRegistryEntry implements WorldClockRegistryEntry.Builder, PaperRegistryBuilder<WorldClock, io.papermc.paper.world.WorldClock> {
|
||||
|
||||
public PaperBuilder(final Conversions conversions, final @Nullable WorldClock internal) {
|
||||
super(conversions, internal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldClock build() {
|
||||
return new WorldClock();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -145,6 +145,37 @@ public final class EntityScheduler {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to invoke the callback immediately if the current thread owns the Entity and the Entity is not retired,
|
||||
* returning {@code null} when it happens. If the current thread neither owns the Entity nor the Entity is retired,
|
||||
* then this function has the same effect as invoking {@link #schedule(Consumer, Consumer, long)} with no retired
|
||||
* callback and a delay of {@code 1L}.
|
||||
*
|
||||
* @param run The callback to run, may not be null.
|
||||
* @return {@code Boolean.TRUE} if the task was scheduled, which means that either the run function or the retired function
|
||||
* will be invoked (but never both), or {@code Boolean.FALSE} indicating neither the run nor retired function will be invoked
|
||||
* since the scheduler has been retired. Returns {@code null} if the callback was executed immediately.
|
||||
*/
|
||||
public Boolean scheduleOrExecute(final Consumer<? extends Entity> run) {
|
||||
Objects.requireNonNull(run, "Run task may not be null");
|
||||
|
||||
final Entity handle = this.entity.getHandleRaw();
|
||||
|
||||
if (!TickThread.isTickThreadFor(handle)) {
|
||||
return Boolean.valueOf(this.schedule(run, null, 1L));
|
||||
}
|
||||
|
||||
synchronized (this.stateLock) {
|
||||
if (this.tickCount == RETIRED_TICK_COUNT) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
((Consumer<Entity>)run).accept(handle);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task with the given delay. If the task failed to schedule because the scheduler is retired (entity
|
||||
* removed), then returns {@code false}. Otherwise, either the run callback will be invoked after the specified delay,
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package io.papermc.paper.util.capture;
|
||||
|
||||
import java.util.Optional;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public interface BlockPlacementPredictor {
|
||||
|
||||
Optional<BlockState> getLatestBlockAt(BlockPos pos);
|
||||
|
||||
Optional<LoadedBlockState> getLatestBlockAtIfLoaded(BlockPos pos);
|
||||
|
||||
Optional<BlockEntityPlacement> getLatestBlockEntityAt(BlockPos pos);
|
||||
|
||||
record BlockEntityPlacement(boolean removed, @Nullable BlockEntity blockEntity) {
|
||||
|
||||
public static final Optional<BlockEntityPlacement> ABSENT = Optional.of(new BlockEntityPlacement(false, null));
|
||||
|
||||
public @Nullable BlockEntity res() {
|
||||
return this.removed ? null : this.blockEntity;
|
||||
}
|
||||
}
|
||||
|
||||
record LoadedBlockState(boolean present, @Nullable BlockState state) {
|
||||
|
||||
public static final Optional<LoadedBlockState> UNLOADED = Optional.of(new LoadedBlockState(false, null));
|
||||
|
||||
public @Nullable BlockState res() {
|
||||
return this.present ? this.state : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package io.papermc.paper.util.capture;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.bukkit.craftbukkit.block.CraftBlock;
|
||||
import org.bukkit.craftbukkit.block.CraftBlockStates;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/*
|
||||
The premise of this storage is to essentially "mock" block placement in the game.
|
||||
This should attempt to mirror block placement the best it can, in order to provide a read solution of possibly what blocks
|
||||
are going to be placed in the world.
|
||||
|
||||
The reason why we use a custom capture map rather than just storing the blocks placed in the world is to ensure
|
||||
that blocks are placed in the correct order, and that the vanilla block chain can occur, which includes physics updates.
|
||||
*/
|
||||
public final class CaptureRecordMap {
|
||||
|
||||
private final Map<BlockPos, CaptureRecord> recordsByPos = new HashMap<>();
|
||||
|
||||
public void setLatestBlockStateAt(BlockPos pos, BlockState blockState, @Block.UpdateFlags int updateFlags) {
|
||||
this.add(new CaptureRecord(pos, blockState, updateFlags));
|
||||
}
|
||||
|
||||
public void setLatestBlockEntityAt(BlockPos pos, boolean clearPreviousBlockEntity, @Nullable BlockEntity add) {
|
||||
CaptureRecord oldRecord = this.recordsByPos.get(pos);
|
||||
if (oldRecord != null) {
|
||||
oldRecord.setBlockEntity(clearPreviousBlockEntity, add);
|
||||
}
|
||||
}
|
||||
|
||||
private void add(CaptureRecord record) {
|
||||
this.recordsByPos.put(record.pos, record);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return this.recordsByPos.isEmpty();
|
||||
}
|
||||
|
||||
public @Nullable BlockState getLatestBlockStateAt(BlockPos pos) {
|
||||
CaptureRecord record = this.recordsByPos.get(pos);
|
||||
if (record == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return record.blockState;
|
||||
}
|
||||
|
||||
// Null indicates that it's not present, no override
|
||||
// Optional empty indicates its being removed
|
||||
public @Nullable Optional<BlockEntity> getLatestBlockEntityAt(BlockPos pos) {
|
||||
CaptureRecord record = this.recordsByPos.get(pos);
|
||||
if (record == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Optional.ofNullable(record.blockEntity);
|
||||
}
|
||||
|
||||
public void applyBlockEntities(ServerLevel parent) {
|
||||
this.recordsByPos.keySet().forEach((pos) -> {
|
||||
Optional<BlockEntity> res = this.getLatestBlockEntityAt(pos);
|
||||
if (res != null && res.isPresent()) {
|
||||
parent.setBlockEntity(res.get());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void applyApiPatch(ServerLevel level) {
|
||||
this.recordsByPos.keySet().forEach((pos) -> {
|
||||
this.recordsByPos.get(pos).applyApiPatch(level);
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: Clean this up
|
||||
public List<org.bukkit.block.BlockState> calculateLatestSnapshots(ServerLevel level) {
|
||||
List<org.bukkit.block.BlockState> out = new ArrayList<>();
|
||||
|
||||
for (Map.Entry<BlockPos, CaptureRecord> entry : this.recordsByPos.entrySet()) {
|
||||
CaptureRecord captureRecord = entry.getValue();
|
||||
out.add(CraftBlockStates.getBlockState(level.getWorld(), entry.getKey(), captureRecord.blockState, captureRecord.blockEntity));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
public Stream<org.bukkit.block.Block> getAffectedBlocks(ServerLevel level) {
|
||||
return this.recordsByPos.keySet().stream().map(pos -> CraftBlock.at(level, pos));
|
||||
}
|
||||
|
||||
public static class CaptureRecord {
|
||||
|
||||
private final BlockPos pos;
|
||||
|
||||
private BlockState blockState;
|
||||
private @Nullable BlockEntity blockEntity;
|
||||
private boolean clearPreviousBlockEntity;
|
||||
private @Block.UpdateFlags int updateFlags;
|
||||
|
||||
public CaptureRecord(BlockPos pos, BlockState blockState, BlockEntity blockEntity) {
|
||||
this.pos = pos;
|
||||
this.blockState = blockState;
|
||||
this.blockEntity = blockEntity;
|
||||
}
|
||||
|
||||
public CaptureRecord(BlockPos pos, BlockState blockState, @Block.UpdateFlags int updateFlags) {
|
||||
this.pos = pos;
|
||||
this.blockState = blockState;
|
||||
this.updateFlags = updateFlags;
|
||||
}
|
||||
|
||||
public CaptureRecord(boolean remove, @Nullable BlockEntity add, BlockPos pos) {
|
||||
this.clearPreviousBlockEntity = remove;
|
||||
this.blockEntity = add;
|
||||
this.pos = pos;
|
||||
}
|
||||
|
||||
public void applyApiPatch(ServerLevel level) {
|
||||
if (this.clearPreviousBlockEntity) {
|
||||
level.removeBlockEntity(this.pos);
|
||||
}
|
||||
|
||||
level.setBlock(this.pos, this.blockState, this.updateFlags);
|
||||
if (this.blockEntity != null) {
|
||||
level.setBlockEntity(this.blockEntity);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBlockEntity(boolean clearPreviousBlockEntity, @Nullable BlockEntity add) {
|
||||
this.clearPreviousBlockEntity = clearPreviousBlockEntity;
|
||||
this.blockEntity = add;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package io.papermc.paper.util.capture;
|
||||
|
||||
import net.minecraft.Optionull;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.data.worldgen.features.TreeFeatures;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
|
||||
import org.bukkit.TreeType;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public final class GrowthContext {
|
||||
|
||||
public static GrowthContext empty() {
|
||||
return new GrowthContext(null, false);
|
||||
}
|
||||
|
||||
public static GrowthContext usingBoneMeal(@Nullable Player user) {
|
||||
return new GrowthContext(user, true);
|
||||
}
|
||||
|
||||
private final @Nullable Player player;
|
||||
private final boolean usedBoneMeal;
|
||||
|
||||
public boolean cancelled = false;
|
||||
public Holder<ConfiguredFeature<?, ?>> feature; // just make this a field
|
||||
|
||||
private GrowthContext(@Nullable Player player, boolean usedBoneMeal) {
|
||||
this.player = player;
|
||||
this.usedBoneMeal = usedBoneMeal;
|
||||
}
|
||||
|
||||
public org.bukkit.entity.@Nullable Player getBukkitPlayer() {
|
||||
return (org.bukkit.entity.Player) Optionull.map(this.player, Entity::getBukkitEntity);
|
||||
}
|
||||
|
||||
public boolean usedBoneMeal() {
|
||||
return this.usedBoneMeal;
|
||||
}
|
||||
|
||||
public TreeType getTreeSpecies() {
|
||||
return getTreeSpecies(this.feature);
|
||||
}
|
||||
|
||||
private static TreeType getTreeSpecies(Holder<ConfiguredFeature<?, ?>> feature) {
|
||||
if (feature.is(TreeFeatures.HUGE_RED_MUSHROOM)) {
|
||||
return TreeType.RED_MUSHROOM;
|
||||
} else if (feature.is(TreeFeatures.HUGE_BROWN_MUSHROOM)) {
|
||||
return TreeType.BROWN_MUSHROOM;
|
||||
} else if (feature.is(TreeFeatures.WARPED_FUNGUS_PLANTED)) {
|
||||
return TreeType.WARPED_FUNGUS;
|
||||
} else if (feature.is(TreeFeatures.CRIMSON_FUNGUS_PLANTED)) {
|
||||
return TreeType.CRIMSON_FUNGUS;
|
||||
} else if (feature.is(TreeFeatures.OAK) || feature.is(TreeFeatures.OAK_BEES_005)) {
|
||||
return TreeType.TREE;
|
||||
} else if (feature.is(TreeFeatures.JUNGLE_TREE)) {
|
||||
return TreeType.COCOA_TREE;
|
||||
} else if (feature.is(TreeFeatures.JUNGLE_TREE_NO_VINE)) {
|
||||
return TreeType.SMALL_JUNGLE;
|
||||
} else if (feature.is(TreeFeatures.PINE)) {
|
||||
return TreeType.TALL_REDWOOD;
|
||||
} else if (feature.is(TreeFeatures.SPRUCE)) {
|
||||
return TreeType.REDWOOD;
|
||||
} else if (feature.is(TreeFeatures.ACACIA)) {
|
||||
return TreeType.ACACIA;
|
||||
} else if (feature.is(TreeFeatures.BIRCH) || feature.is(TreeFeatures.BIRCH_BEES_005)) {
|
||||
return TreeType.BIRCH;
|
||||
} else if (feature.is(TreeFeatures.SUPER_BIRCH_BEES_0002)) {
|
||||
return TreeType.TALL_BIRCH;
|
||||
} else if (feature.is(TreeFeatures.SWAMP_OAK)) {
|
||||
return TreeType.SWAMP;
|
||||
} else if (feature.is(TreeFeatures.FANCY_OAK) || feature.is(TreeFeatures.FANCY_OAK_BEES_005)) {
|
||||
return TreeType.BIG_TREE;
|
||||
} else if (feature.is(TreeFeatures.JUNGLE_BUSH)) {
|
||||
return TreeType.JUNGLE_BUSH;
|
||||
} else if (feature.is(TreeFeatures.DARK_OAK)) {
|
||||
return TreeType.DARK_OAK;
|
||||
} else if (feature.is(TreeFeatures.MEGA_SPRUCE)) {
|
||||
return TreeType.MEGA_REDWOOD;
|
||||
} else if (feature.is(TreeFeatures.MEGA_PINE)) {
|
||||
return TreeType.MEGA_PINE;
|
||||
} else if (feature.is(TreeFeatures.MEGA_JUNGLE_TREE)) {
|
||||
return TreeType.JUNGLE;
|
||||
} else if (feature.is(TreeFeatures.AZALEA_TREE)) {
|
||||
return TreeType.AZALEA;
|
||||
} else if (feature.is(TreeFeatures.MANGROVE)) {
|
||||
return TreeType.MANGROVE;
|
||||
} else if (feature.is(TreeFeatures.TALL_MANGROVE)) {
|
||||
return TreeType.TALL_MANGROVE;
|
||||
} else if (feature.is(TreeFeatures.CHERRY) || feature.is(TreeFeatures.CHERRY_BEES_005)) {
|
||||
return TreeType.CHERRY;
|
||||
} else if (feature.is(TreeFeatures.PALE_OAK) || feature.is(TreeFeatures.PALE_OAK_BONEMEAL)) {
|
||||
return TreeType.PALE_OAK;
|
||||
} else if (feature.is(TreeFeatures.PALE_OAK_CREAKING)) {
|
||||
return TreeType.PALE_OAK_CREAKING;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown tree feature: " + feature);
|
||||
}
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package io.papermc.paper.util.capture;
|
||||
|
||||
import java.util.Optional;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
public record LayeredBlockPlacementPredictor(
|
||||
BlockPlacementPredictor... predictors
|
||||
) implements BlockPlacementPredictor {
|
||||
|
||||
@Override
|
||||
public Optional<BlockState> getLatestBlockAt(BlockPos pos) {
|
||||
for (BlockPlacementPredictor predictor : this.predictors) {
|
||||
Optional<BlockState> state = predictor.getLatestBlockAt(pos);
|
||||
if (state.isPresent()) {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<LoadedBlockState> getLatestBlockAtIfLoaded(BlockPos pos) {
|
||||
for (BlockPlacementPredictor predictor : this.predictors) {
|
||||
Optional<LoadedBlockState> state = predictor.getLatestBlockAtIfLoaded(pos);
|
||||
if (state.isPresent()) {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<BlockEntityPlacement> getLatestBlockEntityAt(BlockPos pos) {
|
||||
for (BlockPlacementPredictor predictor : this.predictors) {
|
||||
Optional<BlockEntityPlacement> state = predictor.getLatestBlockEntityAt(pos);
|
||||
if (state.isPresent()) {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package io.papermc.paper.util.capture;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public record LiveBlockPlacementLayer(WorldCapturer capturer, ServerLevel level) implements BlockPlacementPredictor {
|
||||
|
||||
@Override
|
||||
public Optional<BlockState> getLatestBlockAt(BlockPos pos) {
|
||||
return Optional.of(provideLive(() -> this.level.getBlockState(pos)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<LoadedBlockState> getLatestBlockAtIfLoaded(BlockPos pos) {
|
||||
BlockState state = provideLive(() -> this.level.getBlockStateIfLoaded(pos));
|
||||
if (state == null) {
|
||||
return LoadedBlockState.UNLOADED;
|
||||
}
|
||||
|
||||
return Optional.of(new LoadedBlockState(true, state));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<BlockEntityPlacement> getLatestBlockEntityAt(BlockPos pos) {
|
||||
BlockEntity blockEntity = provideLive(() -> this.level.getBlockEntity(pos));
|
||||
if (blockEntity == null) {
|
||||
return BlockEntityPlacement.ABSENT;
|
||||
}
|
||||
|
||||
return Optional.of(new BlockEntityPlacement(false, blockEntity));
|
||||
}
|
||||
|
||||
public <T> @Nullable T provideLive(Supplier<@Nullable T> valueProvider) {
|
||||
SimpleBlockCapture blockCapture = this.capturer.getCapture();
|
||||
this.capturer.releaseCapture(null);
|
||||
T value = valueProvider.get();
|
||||
this.capturer.releaseCapture(blockCapture);
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,448 @@
|
||||
package io.papermc.paper.util.capture;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.core.particles.ParticleOptions;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.DifficultyInstance;
|
||||
import net.minecraft.world.attribute.EnvironmentAttributeReader;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.flag.FeatureFlagSet;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeManager;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.border.WorldBorder;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.dimension.DimensionType;
|
||||
import net.minecraft.world.level.entity.EntityTypeTest;
|
||||
import net.minecraft.world.level.gameevent.GameEvent;
|
||||
import net.minecraft.world.level.gamerules.GameRules;
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
import net.minecraft.world.level.lighting.LevelLightEngine;
|
||||
import net.minecraft.world.level.material.Fluid;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import net.minecraft.world.level.storage.LevelData;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraft.world.ticks.LevelTickAccess;
|
||||
import net.minecraft.world.ticks.ScheduledTick;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public class MinecraftCaptureBridge implements PaperCapturingWorldLevel {
|
||||
|
||||
private final ServerLevel parent;
|
||||
private final List<Runnable> queuedTasks = new ArrayList<>();
|
||||
|
||||
// Effective represents plugin set blocks -> predicted blocks -> actual server level
|
||||
private final BlockPlacementPredictor effectiveReadLayer;
|
||||
private SimpleBlockPlacementPredictor writeLayer;
|
||||
|
||||
// This is the layer that is written to in the server level potentially.
|
||||
// Mostly plugin set blocks
|
||||
private final SimpleBlockPlacementPredictor serverLevelOverlayLayer;
|
||||
|
||||
private final CapturingTickAccess<Block> blocks;
|
||||
private final CapturingTickAccess<Fluid> liquids;
|
||||
|
||||
private Consumer<Runnable> sink = this.queuedTasks::add;
|
||||
|
||||
public MinecraftCaptureBridge(ServerLevel parent, BlockPlacementPredictor baseReadLayer) {
|
||||
this.parent = parent;
|
||||
|
||||
this.serverLevelOverlayLayer = new SimpleBlockPlacementPredictor();
|
||||
SimpleBlockPlacementPredictor predictedBlocks = new SimpleBlockPlacementPredictor();
|
||||
|
||||
this.effectiveReadLayer = new LayeredBlockPlacementPredictor(
|
||||
this.serverLevelOverlayLayer, // The overlay layer represents plugin set blocks!
|
||||
predictedBlocks, // Now predicted blocks
|
||||
baseReadLayer
|
||||
);
|
||||
|
||||
this.writeLayer = predictedBlocks; // Predicting
|
||||
|
||||
this.blocks = new CapturingTickAccess<>(parent.getBlockTicks());
|
||||
this.liquids = new CapturingTickAccess<>(parent.getFluidTicks());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSeed() {
|
||||
return this.parent.getSeed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerLevel getLevel() {
|
||||
return this.parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DifficultyInstance getCurrentDifficultyAt(BlockPos pos) {
|
||||
return this.parent.getCurrentDifficultyAt(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long nextSubTickCount() {
|
||||
return this.parent.nextSubTickCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LevelData getLevelData() {
|
||||
return this.parent.getLevelData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable MinecraftServer getServer() {
|
||||
return this.parent.getServer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerChunkCache getChunkSource() {
|
||||
return this.parent.getChunkSource();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setBlockAndUpdate(BlockPos pos, BlockState blockState) {
|
||||
return this.setBlock(pos, blockState, Block.UPDATE_ALL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleBlockCapture forkCaptureSession() {
|
||||
return this.parent.capturer.createCaptureSession(new BlockPlacementPredictor() {
|
||||
@Override
|
||||
public Optional<BlockState> getLatestBlockAt(BlockPos pos) {
|
||||
return MinecraftCaptureBridge.this.effectiveReadLayer.getLatestBlockAt(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<LoadedBlockState> getLatestBlockAtIfLoaded(BlockPos pos) {
|
||||
return MinecraftCaptureBridge.this.effectiveReadLayer.getLatestBlockAtIfLoaded(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<BlockEntityPlacement> getLatestBlockEntityAt(BlockPos pos) {
|
||||
return MinecraftCaptureBridge.this.effectiveReadLayer.getLatestBlockEntityAt(pos);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public RandomSource getRandom() {
|
||||
// TODO: Support rolling this back?
|
||||
return this.parent.getRandom();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playSound(@Nullable Entity except, BlockPos pos, SoundEvent sound, SoundSource source, float volume, float pitch) {
|
||||
this.addTask((level) -> level.playSound(except, pos, sound, source, volume, pitch));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addParticle(ParticleOptions particle, double x, double y, double z, double xd, double yd, double zd) {
|
||||
this.addTask((level) -> level.addParticle(particle, x, y, z, xd, yd, zd));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void levelEvent(@Nullable Entity source, int type, BlockPos pos, int data) {
|
||||
this.addTask((level) -> level.levelEvent(source, type, pos, data));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void gameEvent(Holder<GameEvent> gameEvent, Vec3 position, GameEvent.Context context) {
|
||||
this.addTask((level) -> level.gameEvent(gameEvent, position, context));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LevelLightEngine getLightEngine() {
|
||||
return this.parent.getLightEngine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldBorder getWorldBorder() {
|
||||
return this.parent.getWorldBorder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable BlockEntity getBlockEntity(BlockPos pos) {
|
||||
return this.effectiveReadLayer.getLatestBlockEntityAt(pos)
|
||||
.map(BlockPlacementPredictor.BlockEntityPlacement::blockEntity)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlockState(BlockPos pos) {
|
||||
return this.effectiveReadLayer.getLatestBlockAt(pos).orElseThrow(); // Should not ever be null, parent should pass value
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable BlockState getBlockStateIfLoaded(BlockPos pos) {
|
||||
return this.effectiveReadLayer.getLatestBlockAtIfLoaded(pos).map(BlockPlacementPredictor.LoadedBlockState::state).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable FluidState getFluidIfLoaded(BlockPos pos) {
|
||||
return this.getBlockStateIfLoaded(pos).getFluidState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidState getFluidState(BlockPos pos) {
|
||||
return this.getBlockState(pos).getFluidState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Entity> getEntities(@Nullable Entity except, AABB bb, Predicate<? super Entity> selector) {
|
||||
return this.parent.getEntities(except, bb, selector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Entity> List<T> getEntities(EntityTypeTest<Entity, T> entityTypeTest, AABB bb, Predicate<? super T> selector) {
|
||||
return this.parent.getEntities(entityTypeTest, bb, selector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends Player> players() {
|
||||
return this.parent.players();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ChunkAccess getChunk(int chunkX, int chunkZ, ChunkStatus status, boolean loadOrGenerate) {
|
||||
return this.parent.getChunk(chunkX, chunkZ, status, loadOrGenerate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ChunkAccess getChunkIfLoadedImmediately(int x, int z) {
|
||||
return this.parent.getChunkIfLoadedImmediately(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight(Heightmap.Types type, int x, int z) {
|
||||
return this.parent.getHeight(type, x, z); // TODO?
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSkyDarken() {
|
||||
return this.parent.getSkyDarken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeManager getBiomeManager() {
|
||||
return this.parent.getBiomeManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Holder<Biome> getUncachedNoiseBiome(int quartX, int quartY, int quartZ) {
|
||||
return this.parent.getUncachedNoiseBiome(quartX, quartY, quartZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClientSide() {
|
||||
return this.parent.isClientSide();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSeaLevel() {
|
||||
return this.parent.getSeaLevel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DimensionType dimensionType() {
|
||||
return this.parent.dimensionType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegistryAccess registryAccess() {
|
||||
return this.parent.registryAccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureFlagSet enabledFeatures() {
|
||||
return this.parent.enabledFeatures();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnvironmentAttributeReader environmentAttributes() {
|
||||
return this.parent.environmentAttributes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStateAtPosition(BlockPos pos, Predicate<BlockState> predicate) {
|
||||
return predicate.test(this.getBlockState(pos));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFluidAtPosition(BlockPos pos, Predicate<FluidState> predicate) {
|
||||
return predicate.test(this.getFluidState(pos));
|
||||
}
|
||||
|
||||
public boolean silentSet(BlockPos pos, BlockState blockState, @Block.UpdateFlags int updateFlags) {
|
||||
return this.writeLayer.setBlockState(this.effectiveReadLayer, pos, blockState, updateFlags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setBlock(BlockPos pos, BlockState blockState, @Block.UpdateFlags int updateFlags, int updateLimit) {
|
||||
BlockPos copy = pos.immutable();
|
||||
this.addTask((level) -> level.setBlock(copy, blockState, updateFlags, updateLimit));
|
||||
|
||||
return this.writeLayer.setBlockState(this.effectiveReadLayer, copy, blockState, updateFlags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeBlock(BlockPos pos, boolean movedByPiston) {
|
||||
BlockPos copy = pos.immutable();
|
||||
this.addTask((level) -> level.removeBlock(copy, movedByPiston));
|
||||
|
||||
FluidState fluidState = this.getFluidState(copy);
|
||||
return this.silentSet(copy, fluidState.createLegacyBlock(), Block.UPDATE_ALL | (movedByPiston ? Block.UPDATE_MOVE_BY_PISTON : 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean destroyBlock(BlockPos pos, boolean dropResources, @Nullable Entity breaker, int updateLimit) {
|
||||
BlockPos copy = pos.immutable();
|
||||
this.addTask((level) -> level.destroyBlock(copy, dropResources, breaker, updateLimit));
|
||||
|
||||
BlockState blockState = this.getBlockState(copy);
|
||||
if (blockState.isAir()) {
|
||||
return false;
|
||||
} else {
|
||||
FluidState fluidState = this.getFluidState(copy);
|
||||
|
||||
return this.silentSet(copy, fluidState.createLegacyBlock(), Block.UPDATE_ALL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendBlockUpdated(BlockPos pos, BlockState old, BlockState current, @Block.UpdateFlags int updateFlags) {
|
||||
BlockPos copy = pos.immutable();
|
||||
this.addTask((level) -> level.sendBlockUpdated(copy, old, current, updateFlags));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBlockEntity(BlockEntity blockEntity) {
|
||||
this.writeLayer.getRecordMap().setLatestBlockEntityAt(blockEntity.getBlockPos().immutable(), false, blockEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setBlockSilent(BlockPos pos, BlockState blockState, @Block.UpdateFlags int updateFlags, int updateLimit) {
|
||||
BlockPos copy = pos.immutable();
|
||||
return this.silentSet(copy, blockState, updateFlags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LevelTickAccess<Block> getBlockTicks() {
|
||||
return this.blocks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LevelTickAccess<Fluid> getFluidTicks() {
|
||||
return this.liquids;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GameRules getGameRules() {
|
||||
return this.parent.getGameRules();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTask(Consumer<ServerLevel> level) {
|
||||
this.sink.accept(() -> level.accept(this.parent));
|
||||
}
|
||||
|
||||
public @Nullable BlockState getLatestBlockState(BlockPos pos) {
|
||||
return this.effectiveReadLayer.getLatestBlockAt(pos).orElse(null);
|
||||
}
|
||||
|
||||
public @Nullable Optional<BlockEntity> getLatestBlockEntity(BlockPos pos) {
|
||||
Optional<BlockPlacementPredictor.BlockEntityPlacement> placement = this.effectiveReadLayer.getLatestBlockEntityAt(pos);
|
||||
if (placement.isEmpty() || (placement.get().blockEntity() == null && !placement.get().removed())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return placement.map(BlockPlacementPredictor.BlockEntityPlacement::blockEntity);
|
||||
}
|
||||
|
||||
public List<org.bukkit.block.BlockState> calculateLatestSnapshots(ServerLevel level) {
|
||||
return this.writeLayer.getRecordMap().calculateLatestSnapshots(level);
|
||||
}
|
||||
|
||||
public Stream<org.bukkit.block.Block> getAffectedBlocks(ServerLevel level) {
|
||||
return this.writeLayer.getRecordMap().getAffectedBlocks(level);
|
||||
}
|
||||
|
||||
public void applyTasks() {
|
||||
this.sink = Runnable::run;
|
||||
for (Runnable runnable : this.queuedTasks) {
|
||||
runnable.run();
|
||||
}
|
||||
this.blocks.apply();
|
||||
this.liquids.apply();
|
||||
|
||||
// If we have changes that the plugin applied on top of the already existing changes, we know that we can apply them.
|
||||
// So, do that!
|
||||
if (!this.serverLevelOverlayLayer.isEmpty()) {
|
||||
this.serverLevelOverlayLayer.getRecordMap().applyApiPatch(this.parent);
|
||||
}
|
||||
|
||||
// Apply block entities, those may have been written in our estimation. So just apply them.
|
||||
// I don't really like this, as I in general don't really want to apply things from the prediction layer.
|
||||
// But, these may be mutated by anything.
|
||||
if (!this.writeLayer.getRecordMap().isEmpty()) {
|
||||
this.writeLayer.getRecordMap().applyBlockEntities(this.parent);
|
||||
}
|
||||
}
|
||||
|
||||
public void allowWriteOnLevel() {
|
||||
this.writeLayer = this.serverLevelOverlayLayer;
|
||||
}
|
||||
|
||||
public static class CapturingTickAccess<T> implements LevelTickAccess<T> {
|
||||
|
||||
private final LevelTickAccess<T> wrapped;
|
||||
private final Set<BlockPos> scheduled = new HashSet<>();
|
||||
private final List<ScheduledTick<T>> ticks = new ArrayList<>();
|
||||
|
||||
public CapturingTickAccess(LevelTickAccess<T> wrapped) {
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean willTickThisTick(BlockPos pos, T type) {
|
||||
return this.wrapped.willTickThisTick(pos, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void schedule(ScheduledTick<T> tick) {
|
||||
this.scheduled.add(tick.pos());
|
||||
this.ticks.add(tick);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasScheduledTick(BlockPos pos, T type) {
|
||||
return this.wrapped.hasScheduledTick(pos, type) || this.scheduled.contains(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int count() {
|
||||
return this.wrapped.count() + this.scheduled.size();
|
||||
}
|
||||
|
||||
public void apply() {
|
||||
this.ticks.forEach(this.wrapped::schedule);
|
||||
}
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package io.papermc.paper.util.capture;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.gamerules.GameRules;
|
||||
|
||||
public interface PaperCapturingWorldLevel extends WorldGenLevel {
|
||||
|
||||
GameRules getGameRules();
|
||||
|
||||
void sendBlockUpdated(BlockPos pos, BlockState old, BlockState current, @Block.UpdateFlags int updateFlags);
|
||||
|
||||
void setBlockEntity(BlockEntity blockEntity);
|
||||
|
||||
boolean setBlockSilent(BlockPos pos, BlockState blockState, @Block.UpdateFlags int updateFlags, int updateLimit);
|
||||
|
||||
ServerChunkCache getChunkSource();
|
||||
|
||||
boolean setBlockAndUpdate(BlockPos pos, BlockState blockState);
|
||||
|
||||
void addTask(Consumer<ServerLevel> level);
|
||||
|
||||
SimpleBlockCapture forkCaptureSession();
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package io.papermc.paper.util.capture;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.gamerules.GameRules;
|
||||
|
||||
public interface ServerLevelPaperCapturingWorldLevel extends PaperCapturingWorldLevel {
|
||||
|
||||
@Override
|
||||
default GameRules getGameRules() {
|
||||
return this.getLevel().getGameRules();
|
||||
}
|
||||
|
||||
@Override
|
||||
default void sendBlockUpdated(BlockPos pos, BlockState old, BlockState current, @Block.UpdateFlags int updateFlags) {
|
||||
this.getLevel().sendBlockUpdated(pos, old, current, updateFlags);
|
||||
}
|
||||
|
||||
@Override
|
||||
default void setBlockEntity(BlockEntity blockEntity) {
|
||||
this.getLevel().setBlockEntity(blockEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean setBlockSilent(BlockPos pos, BlockState blockState, @Block.UpdateFlags int updateFlags, int updateLimit) {
|
||||
return this.getLevel().setBlock(pos, blockState, updateFlags, updateLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean setBlockAndUpdate(BlockPos pos, BlockState blockState) {
|
||||
return this.getLevel().setBlockAndUpdate(pos, blockState);
|
||||
}
|
||||
|
||||
@Override
|
||||
default void addTask(Consumer<ServerLevel> level) {
|
||||
level.accept(this.getLevel());
|
||||
}
|
||||
|
||||
@Override
|
||||
default SimpleBlockCapture forkCaptureSession() {
|
||||
return this.getLevel().capturer.createCaptureSession();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package io.papermc.paper.util.capture;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public class SimpleBlockCapture implements AutoCloseable {
|
||||
|
||||
private final MinecraftCaptureBridge capturingWorldLevel;
|
||||
private final ServerLevel level;
|
||||
private final @Nullable SimpleBlockCapture oldCapture;
|
||||
|
||||
private boolean isOverlayingCaptureOnLevel = false;
|
||||
|
||||
public SimpleBlockCapture(BlockPlacementPredictor base, ServerLevel level, @Nullable SimpleBlockCapture oldCapture) {
|
||||
this.capturingWorldLevel = new MinecraftCaptureBridge(level, base);
|
||||
this.level = level;
|
||||
this.oldCapture = oldCapture;
|
||||
}
|
||||
|
||||
public MinecraftCaptureBridge capturingWorldLevel() {
|
||||
return this.capturingWorldLevel;
|
||||
}
|
||||
|
||||
public boolean isCapturing() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<BlockState> getCapturedSnapshots() {
|
||||
return this.capturingWorldLevel.calculateLatestSnapshots(this.level);
|
||||
}
|
||||
|
||||
public Stream<Block> getAffectedBlocks() {
|
||||
return this.capturingWorldLevel.getAffectedBlocks(this.level);
|
||||
}
|
||||
|
||||
public net.minecraft.world.level.block.state.@Nullable BlockState getOverlayBlockState(BlockPos pos) {
|
||||
return this.capturingWorldLevel.getLatestBlockState(pos);
|
||||
}
|
||||
|
||||
public @Nullable Optional<BlockEntity> getOverlayBlockEntity(BlockPos pos) {
|
||||
return this.capturingWorldLevel.getLatestBlockEntity(pos);
|
||||
}
|
||||
|
||||
// This is done so that the captured blocks appear ontop of the world.
|
||||
public void overlayCaptureOnLevel() {
|
||||
this.isOverlayingCaptureOnLevel = true;
|
||||
this.capturingWorldLevel.allowWriteOnLevel();
|
||||
}
|
||||
|
||||
public boolean isOverlayingCaptureOnLevel() {
|
||||
return this.isOverlayingCaptureOnLevel;
|
||||
}
|
||||
|
||||
public void finalizePlacement() {
|
||||
this.level.capturer.releaseCapture(this.oldCapture);
|
||||
this.capturingWorldLevel.applyTasks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
this.level.capturer.releaseCapture(this.oldCapture);
|
||||
}
|
||||
|
||||
public net.minecraft.world.level.block.state.@Nullable BlockState getCaptureBlockStateIfLoaded(BlockPos pos) {
|
||||
return this.capturingWorldLevel.getBlockStateIfLoaded(pos);
|
||||
}
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
package io.papermc.paper.util.capture;
|
||||
|
||||
import java.util.Optional;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.EntityBlock;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
// Block state
|
||||
class SimpleBlockPlacementPredictor implements BlockPlacementPredictor {
|
||||
|
||||
private final CaptureRecordMap guesstimationMap = new CaptureRecordMap();
|
||||
|
||||
public boolean setBlockState(BlockPlacementPredictor layer, BlockPos pos, BlockState blockState, @Block.UpdateFlags int updateFlags) {
|
||||
BlockState oldState = layer.getLatestBlockAt(pos).orElse(Blocks.AIR.defaultBlockState());
|
||||
// Don't do any processing if the same
|
||||
if (oldState == blockState) {
|
||||
return false;
|
||||
} else {
|
||||
Block newBlock = blockState.getBlock();
|
||||
|
||||
this.setLatestBlockAt(pos, blockState, updateFlags);
|
||||
|
||||
// TODO: local heightmaps?
|
||||
// this.heightmaps.get(Heightmap.Types.MOTION_BLOCKING).update(i, y, i2, blockState);
|
||||
// this.heightmaps.get(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES).update(i, y, i2, blockState);
|
||||
// this.heightmaps.get(Heightmap.Types.OCEAN_FLOOR).update(i, y, i2, blockState);
|
||||
// this.heightmaps.get(Heightmap.Types.WORLD_SURFACE).update(i, y, i2, blockState);
|
||||
|
||||
// LIGHT ENGINE CALCULATIONS
|
||||
|
||||
boolean blockChanged = !oldState.is(newBlock);
|
||||
if (blockChanged && oldState.hasBlockEntity() && !blockState.shouldChangedStateKeepBlockEntity(oldState)) {
|
||||
this.removeBlockEntity(pos);
|
||||
}
|
||||
|
||||
// if ((blockChanged || newBlock instanceof BaseRailBlock) && ((updateFlags & Block.UPDATE_NEIGHBORS) != 0 || updateMoveByPiston)) {
|
||||
// BlockState finalOldState = oldState;
|
||||
// this.capturingWorldLevel.addTask((level) -> {
|
||||
// finalOldState.affectNeighborsAfterRemoval(level, pos, updateMoveByPiston);
|
||||
// });
|
||||
// }
|
||||
|
||||
if (blockState.hasBlockEntity()) {
|
||||
BlockEntity blockEntity = this.getLatestBlockEntityAt(pos).map(BlockEntityPlacement::blockEntity).orElse(null);
|
||||
if (blockEntity != null && !blockEntity.isValidBlockState(blockState)) {
|
||||
blockEntity = null;
|
||||
}
|
||||
|
||||
if (blockEntity == null) {
|
||||
blockEntity = ((EntityBlock) newBlock).newBlockEntity(pos, blockState);
|
||||
if (blockEntity != null) {
|
||||
this.addAndRegisterBlockEntity(blockEntity);
|
||||
}
|
||||
} else {
|
||||
blockEntity.setBlockState(blockState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private void addAndRegisterBlockEntity(BlockEntity blockEntity) {
|
||||
this.guesstimationMap.setLatestBlockEntityAt(blockEntity.getBlockPos(), false, blockEntity);
|
||||
}
|
||||
|
||||
private void removeBlockEntity(BlockPos pos) {
|
||||
this.guesstimationMap.setLatestBlockEntityAt(pos, true, null);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return this.guesstimationMap.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<BlockEntityPlacement> getLatestBlockEntityAt(BlockPos pos) {
|
||||
Optional<BlockEntity> value = this.guesstimationMap.getLatestBlockEntityAt(pos);
|
||||
if (value == null) {
|
||||
return Optional.empty();
|
||||
} else {
|
||||
return value
|
||||
.map(block -> new BlockEntityPlacement(false, block))
|
||||
.or(() -> BlockEntityPlacement.ABSENT);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<BlockState> getLatestBlockAt(BlockPos pos) {
|
||||
return Optional.ofNullable(this.guesstimationMap.getLatestBlockStateAt(pos));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<LoadedBlockState> getLatestBlockAtIfLoaded(BlockPos pos) {
|
||||
return Optional.ofNullable(this.guesstimationMap.getLatestBlockStateAt(pos))
|
||||
.map((state) -> new LoadedBlockState(true, state));
|
||||
}
|
||||
|
||||
public void setLatestBlockAt(BlockPos pos, BlockState blockState, @Block.UpdateFlags int updateFlags) {
|
||||
this.guesstimationMap.setLatestBlockStateAt(pos, blockState, updateFlags);
|
||||
}
|
||||
|
||||
public CaptureRecordMap getRecordMap() {
|
||||
return this.guesstimationMap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package io.papermc.paper.util.capture;
|
||||
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
// TODO: Cleanup this state, because its held on the server world. I had proper state handling but threw it away at some point
|
||||
public class WorldCapturer {
|
||||
|
||||
public final ServerLevel level;
|
||||
|
||||
private @Nullable SimpleBlockCapture capture;
|
||||
|
||||
public WorldCapturer(Level level) {
|
||||
this.level = (ServerLevel) level;
|
||||
}
|
||||
|
||||
public SimpleBlockCapture createCaptureSession(BlockPlacementPredictor blockPlacementPredictor) {
|
||||
this.capture = new SimpleBlockCapture(blockPlacementPredictor, this.level, this.capture);
|
||||
return this.capture;
|
||||
}
|
||||
|
||||
public SimpleBlockCapture createCaptureSession() {
|
||||
return this.createCaptureSession(new LiveBlockPlacementLayer(this, this.level));
|
||||
}
|
||||
|
||||
public void releaseCapture(@Nullable SimpleBlockCapture oldCapture) {
|
||||
this.capture = oldCapture;
|
||||
}
|
||||
|
||||
public @Nullable SimpleBlockCapture getCapture() {
|
||||
return this.capture;
|
||||
}
|
||||
|
||||
public boolean isCapturing() {
|
||||
return this.capture != null;
|
||||
}
|
||||
}
|
||||
Loaded 100 of 119 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user