mirror of
https://github.com/PaperMC/Paper.git
synced 2026-11-04 08:52:01 -05:00
Compare commits
21
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d854e66b8 | ||
|
|
57c202e015 | ||
|
|
bd79e20c66 | ||
|
|
35b2c6ece4 | ||
|
|
e382e6872c | ||
|
|
4e1a2555be | ||
|
|
7ec3174a33 | ||
|
|
c9e89f49b0 | ||
|
|
aa6cd74c03 | ||
|
|
ea10fa4a79 | ||
|
|
692e93a91f | ||
|
|
bee287927c | ||
|
|
0caf75f839 | ||
|
|
d7510efc16 | ||
|
|
aa6ee44a7a | ||
|
|
7c90c7c497 | ||
|
|
2f083acbed | ||
|
|
38c1ddb52a | ||
|
|
46b4b0b8d5 | ||
|
|
d0e808f44c | ||
|
|
e454fef40e |
No files matched your search
@@ -11,7 +11,7 @@ java {
|
||||
|
||||
val annotationsVersion = "26.0.2"
|
||||
// Keep in sync with paper-server adventure-text-serializer-ansi dep
|
||||
val adventureVersion = "4.21.0"
|
||||
val adventureVersion = "4.23.0"
|
||||
val bungeeCordChatVersion = "1.21-R0.2-deprecated+build.21"
|
||||
val slf4jVersion = "2.0.16"
|
||||
val log4jVersion = "2.24.1"
|
||||
|
||||
@@ -4,6 +4,8 @@ import com.destroystokyo.paper.util.SneakyThrow;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InaccessibleObjectException;
|
||||
|
||||
/**
|
||||
* An <b>internal</b> utility type that holds logic for loading a provider-like type from a classloaders.
|
||||
@@ -56,7 +58,14 @@ public final class ProviderUtil {
|
||||
throw new ClassCastException("class '%s' does not extend '%s'".formatted(clazz, classType));
|
||||
}
|
||||
|
||||
clazzInstance = pluginClass.getDeclaredConstructor().newInstance();
|
||||
final Constructor<? extends T> constructor = pluginClass.getDeclaredConstructor();
|
||||
try {
|
||||
constructor.setAccessible(true); // Allow non-public constructors
|
||||
} catch (final InaccessibleObjectException | SecurityException ex) {
|
||||
throw new RuntimeException("Inaccessible constructor");
|
||||
}
|
||||
|
||||
clazzInstance = constructor.newInstance();
|
||||
} catch (final IllegalAccessException exception) {
|
||||
throw new RuntimeException("No public constructor");
|
||||
} catch (final InstantiationException exception) {
|
||||
|
||||
@@ -37,7 +37,7 @@ public enum SoundCategory implements Sound.Source.Provider {
|
||||
case PLAYERS -> Sound.Source.PLAYER;
|
||||
case AMBIENT -> Sound.Source.AMBIENT;
|
||||
case VOICE -> Sound.Source.VOICE;
|
||||
case UI -> throw new UnsupportedOperationException("Waiting on adventure release for the UI sound source"); // todo adventure
|
||||
case UI -> Sound.Source.UI;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,10 @@ public interface Fireball extends Projectile, Explosive {
|
||||
* The acceleration gets applied to the velocity every tick, depending on
|
||||
* the specific type of the fireball a damping / drag factor is applied so
|
||||
* that the velocity does not grow into infinity.
|
||||
* <br>
|
||||
* <b>Note:</b> that the client may not respect non-default acceleration
|
||||
* power and will therefore mispredict the location of the fireball, causing
|
||||
* visual stutter.
|
||||
*
|
||||
* @param acceleration the acceleration
|
||||
*/
|
||||
|
||||
@@ -29,7 +29,7 @@ public interface PluginManager extends io.papermc.paper.plugin.PermissionManager
|
||||
/**
|
||||
* Checks if the given plugin is loaded and returns it when applicable
|
||||
* <p>
|
||||
* Please note that the name of the plugin is case-sensitive
|
||||
* Please note that the name of the plugin is case-insensitive
|
||||
*
|
||||
* @param name Name of the plugin to check
|
||||
* @return Plugin if it exists, otherwise null
|
||||
@@ -48,7 +48,7 @@ public interface PluginManager extends io.papermc.paper.plugin.PermissionManager
|
||||
/**
|
||||
* Checks if the given plugin is enabled or not
|
||||
* <p>
|
||||
* Please note that the name of the plugin is case-sensitive.
|
||||
* Please note that the name of the plugin is case-insensitive.
|
||||
*
|
||||
* @param name Name of the plugin to check
|
||||
* @return true if the plugin is enabled, otherwise false
|
||||
|
||||
@@ -464,7 +464,7 @@ public final class SimplePluginManager implements PluginManager {
|
||||
/**
|
||||
* Checks if the given plugin is loaded and returns it when applicable
|
||||
* <p>
|
||||
* Please note that the name of the plugin is case-sensitive
|
||||
* Please note that the name of the plugin is case-insensitive
|
||||
*
|
||||
* @param name Name of the plugin to check
|
||||
* @return Plugin if it exists, otherwise null
|
||||
@@ -486,7 +486,7 @@ public final class SimplePluginManager implements PluginManager {
|
||||
/**
|
||||
* Checks if the given plugin is enabled or not
|
||||
* <p>
|
||||
* Please note that the name of the plugin is case-sensitive.
|
||||
* Please note that the name of the plugin is case-insensitive.
|
||||
*
|
||||
* @param name Name of the plugin to check
|
||||
* @return true if the plugin is enabled, otherwise false
|
||||
|
||||
@@ -6,6 +6,7 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InaccessibleObjectException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
@@ -91,13 +92,20 @@ public final class PluginClassLoader extends URLClassLoader implements io.paperm
|
||||
try {
|
||||
pluginConstructor = pluginClass.getDeclaredConstructor();
|
||||
} catch (NoSuchMethodException ex) {
|
||||
throw new InvalidPluginException("main class `" + description.getMain() + "' must have a public no-args constructor", ex);
|
||||
throw new InvalidPluginException("main class `" + description.getMain() + "' must have a no-args constructor", ex);
|
||||
}
|
||||
|
||||
try {
|
||||
// Support non-public constructors
|
||||
pluginConstructor.setAccessible(true);
|
||||
} catch (InaccessibleObjectException | SecurityException ex) {
|
||||
throw new InvalidPluginException("main class `" + description.getMain() + "' constructor inaccessible", ex);
|
||||
}
|
||||
|
||||
try {
|
||||
plugin = pluginConstructor.newInstance();
|
||||
} catch (IllegalAccessException ex) {
|
||||
throw new InvalidPluginException("main class `" + description.getMain() + "' constructor must be public", ex);
|
||||
throw new InvalidPluginException("main class `" + description.getMain() + "' constructor inaccessible", ex);
|
||||
} catch (InstantiationException ex) {
|
||||
throw new InvalidPluginException("main class `" + description.getMain() + "' must not be abstract", ex);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
|
||||
@@ -134,7 +134,7 @@ dependencies {
|
||||
implementation("org.jline:jline-terminal-ffm:3.27.1") // use ffm on java 22+
|
||||
implementation("org.jline:jline-terminal-jni:3.27.1") // fall back to jni on java 21
|
||||
implementation("net.minecrell:terminalconsoleappender:1.3.0")
|
||||
implementation("net.kyori:adventure-text-serializer-ansi:4.21.0") // Keep in sync with adventureVersion from Paper-API build file
|
||||
implementation("net.kyori:adventure-text-serializer-ansi:4.23.0") // Keep in sync with adventureVersion from Paper-API build file
|
||||
runtimeConfiguration(sourceSets.main.map { it.runtimeClasspath })
|
||||
|
||||
/*
|
||||
@@ -381,7 +381,7 @@ fill {
|
||||
version(paperweight.minecraftVersion)
|
||||
|
||||
build {
|
||||
channel = BuildChannel.ALPHA
|
||||
channel = BuildChannel.STABLE
|
||||
|
||||
downloads {
|
||||
register("server:default") {
|
||||
|
||||
@@ -484,7 +484,7 @@ index c70a58f5f633fa8e255f74c42f5e87c96b7b013a..ec20a5a6d7c8f65abda528fec36bec7b
|
||||
public void tick() {
|
||||
super.tick();
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index 7c7b9872a2cb16d842901a86e7e4b0f723588ae0..c8ce2ccfd5e5e8bdc7dc10ed7b72ca8d43444b4e 100644
|
||||
index 75f81a6bc156a6455a616b8de0d7701fd2255a2d..b3d951670b3a097d04cfe347d7df496b1d0a0e09 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -409,6 +409,15 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
@@ -517,7 +517,7 @@ index 7c7b9872a2cb16d842901a86e7e4b0f723588ae0..c8ce2ccfd5e5e8bdc7dc10ed7b72ca8d
|
||||
SynchedEntityData.Builder builder = new SynchedEntityData.Builder(this);
|
||||
builder.define(DATA_SHARED_FLAGS_ID, (byte)0);
|
||||
builder.define(DATA_AIR_SUPPLY_ID, this.getMaxAirSupply());
|
||||
@@ -995,6 +1011,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
@@ -984,6 +1000,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
this.setPos(this.getX() + movement.x, this.getY() + movement.y, this.getZ() + movement.z);
|
||||
} else {
|
||||
if (type == MoverType.PISTON) {
|
||||
@@ -528,7 +528,7 @@ index 7c7b9872a2cb16d842901a86e7e4b0f723588ae0..c8ce2ccfd5e5e8bdc7dc10ed7b72ca8d
|
||||
movement = this.limitPistonMovement(movement);
|
||||
if (movement.equals(Vec3.ZERO)) {
|
||||
return;
|
||||
@@ -1008,6 +1028,13 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
@@ -997,6 +1017,13 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
this.stuckSpeedMultiplier = Vec3.ZERO;
|
||||
this.setDeltaMovement(Vec3.ZERO);
|
||||
}
|
||||
@@ -661,10 +661,10 @@ index 3f780276be6766ef253c50212e06fd93a96b0caa..7e70c7bee633c54497d1cd2854dd60f4
|
||||
}
|
||||
}
|
||||
diff --git a/net/minecraft/world/entity/item/ItemEntity.java b/net/minecraft/world/entity/item/ItemEntity.java
|
||||
index 548d7c8dc517da6c4db86b11f579ae63e6db56cf..a29860af4c37b2b45df49f9ba18f7e38921dfb02 100644
|
||||
index fca31bbab8e7830933ceffcf992ff56ccc84414c..51804b611f469f2ab53e455e8c633b867b00cc88 100644
|
||||
--- a/net/minecraft/world/entity/item/ItemEntity.java
|
||||
+++ b/net/minecraft/world/entity/item/ItemEntity.java
|
||||
@@ -121,6 +121,29 @@ public class ItemEntity extends Entity implements TraceableEntity {
|
||||
@@ -129,6 +129,29 @@ public class ItemEntity extends Entity implements TraceableEntity {
|
||||
return 0.04;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@ Subject: [PATCH] Anti-Xray
|
||||
|
||||
|
||||
diff --git a/io/papermc/paper/FeatureHooks.java b/io/papermc/paper/FeatureHooks.java
|
||||
index 55cc0dec4a88baea17f160e95d5d8316e0bb7a50..338dc0fb07cdba5f7350cca332fa3e942c622bfb 100644
|
||||
index 811a8e5141f2061a185b53b63d951646141c0c7d..33d3eb510c5844e72bbc382bd24641aae080962d 100644
|
||||
--- a/io/papermc/paper/FeatureHooks.java
|
||||
+++ b/io/papermc/paper/FeatureHooks.java
|
||||
@@ -40,20 +40,25 @@ public final class FeatureHooks {
|
||||
@@ -45,20 +45,25 @@ public final class FeatureHooks {
|
||||
}
|
||||
|
||||
public static LevelChunkSection createSection(final Registry<Biome> biomeRegistry, final Level level, final ChunkPos chunkPos, final int chunkSection) {
|
||||
|
||||
@@ -14,17 +14,17 @@ movement will load only the chunk the player enters anyways and avoids loading
|
||||
massive amounts of surrounding chunks due to large AABB lookups.
|
||||
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index 7453ddb09f349b7836f966573e4933646a75cba6..58eda0d6426f30cda604f4120f1ddb012316c108 100644
|
||||
index 23dfc87db1d5e90099270627197abc0f787a4393..27a01fd28ea565221768f31df02f0a2ddf242fce 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -229,6 +229,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
// Paper end - Share random for entities to make them more random
|
||||
@@ -230,6 +230,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
public @Nullable org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason; // Paper - Entity#getEntitySpawnReason
|
||||
|
||||
private volatile @Nullable org.bukkit.craftbukkit.entity.CraftEntity bukkitEntity; // Paper - Folia schedulers - volatile
|
||||
+ public boolean collisionLoadChunks = false; // Paper
|
||||
private @Nullable org.bukkit.craftbukkit.entity.CraftEntity bukkitEntity;
|
||||
|
||||
public org.bukkit.craftbukkit.entity.CraftEntity getBukkitEntity() {
|
||||
if (this.bukkitEntity == null) {
|
||||
diff --git a/net/minecraft/world/level/BlockCollisions.java b/net/minecraft/world/level/BlockCollisions.java
|
||||
index ed6e4f9fd0c7ad1219e66bc1cb4038191dd6edd8..45a20dbb935b12d429153463dba5d6fd3385dd7a 100644
|
||||
--- a/net/minecraft/world/level/BlockCollisions.java
|
||||
|
||||
@@ -32481,7 +32481,7 @@ index 0000000000000000000000000000000000000000..b028017b9c44821a8a313a04e0b10f5d
|
||||
+ }
|
||||
+}
|
||||
diff --git a/ca/spottedleaf/moonrise/paper/PaperHooks.java b/ca/spottedleaf/moonrise/paper/PaperHooks.java
|
||||
index 0c611598a6912b692a7639301811ee557e2304f1..6f65564f6a6e99d6549de9a23a031b1f4a8a9798 100644
|
||||
index e4f0653c575c33b1ef8160b6c88e29ee9fb44508..b6a34796d33f1593301c3a67a013fa7e812cb329 100644
|
||||
--- a/ca/spottedleaf/moonrise/paper/PaperHooks.java
|
||||
+++ b/ca/spottedleaf/moonrise/paper/PaperHooks.java
|
||||
@@ -211,6 +211,43 @@ public final class PaperHooks extends BaseChunkSystemHooks implements PlatformHo
|
||||
@@ -32542,7 +32542,7 @@ index 6536dc08c80170f5679acedd65cd2b9f6ad3fb3a..294cd15a796ad25823c8ccf98fbfae46
|
||||
return structureTemplate.save(new CompoundTag());
|
||||
}
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index 794a01ee70a2a30d91550f5265f774ba73828cf9..c716521fb1497dc8a22d827ddb50fc1cc21a05f4 100644
|
||||
index 2d597e50dcd957bd566c4da384fac5f36b5362f7..75aba65cbe1a943f21c7464ff9465e64f63e8e5b 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -305,6 +305,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -32585,10 +32585,10 @@ index 8c1417c659ea0e079e99b9bfa79e1cf6ba9b712b..a8a32edea080f32fd25c9e009d4efa41
|
||||
if (stopBelowZero) {
|
||||
chunkData.putString("Status", net.minecraft.core.registries.BuiltInRegistries.CHUNK_STATUS.getKey(net.minecraft.world.level.chunk.status.ChunkStatus.SPAWN).toString());
|
||||
diff --git a/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java b/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
|
||||
index 6be673172548c1382c7402ec4e1ec6ef51f702d3..18750f1ea3101b6c0ab0b8e33c304eb7fa1ed04d 100644
|
||||
index 6be673172548c1382c7402ec4e1ec6ef51f702d3..49be43ac896d60587511a97445c53c10c587a341 100644
|
||||
--- a/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
|
||||
@@ -32,13 +32,30 @@ public class SimpleRegionStorage implements AutoCloseable {
|
||||
@@ -32,13 +32,32 @@ public class SimpleRegionStorage implements AutoCloseable {
|
||||
return this.worker.store(chunkPos, data);
|
||||
}
|
||||
|
||||
@@ -32609,7 +32609,9 @@ index 6be673172548c1382c7402ec4e1ec6ef51f702d3..18750f1ea3101b6c0ab0b8e33c304eb7
|
||||
- return this.dataFixType.updateToCurrentVersion(this.fixerUpper, tag, dataVersion);
|
||||
+ // Paper start - rewrite data conversion system
|
||||
+ final int dataVer = NbtUtils.getDataVersion(tag, version);
|
||||
+ return ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(this.getDataConverterType(), tag, dataVer, ca.spottedleaf.dataconverter.minecraft.util.Version.getCurrentVersion());
|
||||
+ final CompoundTag ret = ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(this.getDataConverterType(), tag, dataVer, ca.spottedleaf.dataconverter.minecraft.util.Version.getCurrentVersion());
|
||||
+ NbtUtils.addCurrentDataVersion(ret); // Fix MC-299110
|
||||
+ return ret;
|
||||
+ // Paper end - rewrite data conversion system
|
||||
}
|
||||
|
||||
|
||||
@@ -23061,7 +23061,7 @@ index 0000000000000000000000000000000000000000..f1f72a051083b61273202cb4e67ecb11
|
||||
+ private SaveUtil() {}
|
||||
+}
|
||||
diff --git a/io/papermc/paper/FeatureHooks.java b/io/papermc/paper/FeatureHooks.java
|
||||
index 68e1e2da7a3291c6260898c90373bf21630f3351..b2b67a3b1b4620dc97a32df953d4ad47bbe5e481 100644
|
||||
index b1a7af2dd3a3e166b1e58125f13ce08b9ec6d9ff..df6fbb35e5023b42de0b97434712e04a6b3e66a3 100644
|
||||
--- a/io/papermc/paper/FeatureHooks.java
|
||||
+++ b/io/papermc/paper/FeatureHooks.java
|
||||
@@ -1,6 +1,9 @@
|
||||
@@ -23074,8 +23074,13 @@ index 68e1e2da7a3291c6260898c90373bf21630f3351..b2b67a3b1b4620dc97a32df953d4ad47
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongSets;
|
||||
@@ -31,12 +34,16 @@ import org.bukkit.World;
|
||||
public final class FeatureHooks {
|
||||
@@ -32,16 +35,20 @@ public final class FeatureHooks {
|
||||
|
||||
// this includes non-accessible entities
|
||||
public static Iterable<Entity> getAllEntities(final net.minecraft.server.level.ServerLevel world) {
|
||||
- return ((net.minecraft.world.level.entity.LevelEntityGetterAdapter<Entity>)world.getEntities()).sectionStorage.getAllEntities();
|
||||
+ return ((ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup)world.getEntities()).getAllMapped(); // Paper - rewrite chunk system
|
||||
}
|
||||
|
||||
public static void setPlayerChunkUnloadDelay(final long ticks) {
|
||||
+ ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.setUnloadDelay(ticks); // Paper - rewrite chunk system
|
||||
@@ -23091,7 +23096,7 @@ index 68e1e2da7a3291c6260898c90373bf21630f3351..b2b67a3b1b4620dc97a32df953d4ad47
|
||||
}
|
||||
|
||||
public static LevelChunkSection createSection(final Registry<Biome> biomeRegistry, final Level level, final ChunkPos chunkPos, final int chunkSection) {
|
||||
@@ -62,111 +69,58 @@ public final class FeatureHooks {
|
||||
@@ -67,111 +74,58 @@ public final class FeatureHooks {
|
||||
}
|
||||
|
||||
public static Set<Long> getSentChunkKeys(final ServerPlayer player) {
|
||||
@@ -23224,7 +23229,7 @@ index 68e1e2da7a3291c6260898c90373bf21630f3351..b2b67a3b1b4620dc97a32df953d4ad47
|
||||
|
||||
org.bukkit.Chunk chunk = null;
|
||||
for (net.minecraft.server.level.Ticket ticket : tickets) {
|
||||
@@ -186,15 +140,15 @@ public final class FeatureHooks {
|
||||
@@ -191,15 +145,15 @@ public final class FeatureHooks {
|
||||
}
|
||||
|
||||
public static int getViewDistance(net.minecraft.server.level.ServerLevel world) {
|
||||
@@ -23243,7 +23248,7 @@ index 68e1e2da7a3291c6260898c90373bf21630f3351..b2b67a3b1b4620dc97a32df953d4ad47
|
||||
}
|
||||
|
||||
public static void setViewDistance(net.minecraft.server.level.ServerLevel world, int distance) {
|
||||
@@ -212,35 +166,31 @@ public final class FeatureHooks {
|
||||
@@ -217,35 +171,31 @@ public final class FeatureHooks {
|
||||
}
|
||||
|
||||
public static void setSendViewDistance(net.minecraft.server.level.ServerLevel world, int distance) {
|
||||
@@ -23862,7 +23867,7 @@ index 46de98a6bbbae48c4837e1e588ba198a363d2dde..fd3553bdc1c3cdbf6aa3dc00e0a4987f
|
||||
thread1 -> {
|
||||
DedicatedServer dedicatedServer1 = new DedicatedServer(
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index f7362a979126c5c0a581c05c0b623cf40b8f0ebd..338ef549efe82c250c74365c1c1071986920c8c9 100644
|
||||
index 75aba65cbe1a943f21c7464ff9465e64f63e8e5b..32475c0958fd7e0f1f9b494b0cc78a4a718d12b8 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -173,7 +173,7 @@ import net.minecraft.world.phys.Vec2;
|
||||
@@ -24054,7 +24059,7 @@ index f7362a979126c5c0a581c05c0b623cf40b8f0ebd..338ef549efe82c250c74365c1c107198
|
||||
return true;
|
||||
} else {
|
||||
boolean ret = false; // Paper - force execution of all worlds, do not just bias the first
|
||||
@@ -2469,6 +2557,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -2478,6 +2566,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26910,11 +26915,11 @@ index 92a39927d7bd9af3ab67c2595d8ed435a2da6a69..7c9a2eed4441f816723562e0012f918d
|
||||
}
|
||||
}
|
||||
diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java
|
||||
index 5f8ce58e8af0590f0f79ff7122ad8a5ac96a5d9b..50bde815b917c14bc4fb9163bd9df45bfa63ef47 100644
|
||||
index 283f240511898e416b83ec4dcccd4901889b80bb..3d5a8163a7dd78a195b77c4aaebdee2dc3dee64b 100644
|
||||
--- a/net/minecraft/server/level/ServerEntity.java
|
||||
+++ b/net/minecraft/server/level/ServerEntity.java
|
||||
@@ -122,6 +122,11 @@ public class ServerEntity {
|
||||
// Paper end - Improve AbstractHurtingProjectile syncing
|
||||
@@ -104,6 +104,11 @@ public class ServerEntity {
|
||||
}
|
||||
|
||||
public void sendChanges() {
|
||||
+ // Paper start - optimise collisions
|
||||
@@ -27686,7 +27691,7 @@ index 49008b4cbaead8a66a93d2b0d4b50b335a6c3eed..f9c96bbdc54e68b9216b7f8662bfae03
|
||||
}
|
||||
|
||||
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
|
||||
index 69dbff3f62e3a9ba7090156380f842bb44deebf8..c0b74d408340101bc3aac4cb4b7232c5cc78b08a 100644
|
||||
index 6f7f92cc43c56a7453b289f205502d089474ef6d..b390ba657b8b880e431c84e9dd948ac9c84af2fd 100644
|
||||
--- a/net/minecraft/server/level/ServerPlayer.java
|
||||
+++ b/net/minecraft/server/level/ServerPlayer.java
|
||||
@@ -193,7 +193,7 @@ import net.minecraft.world.scores.Team;
|
||||
@@ -28723,7 +28728,7 @@ index 8cc5c0716392ba06501542ff5cbe71ee43979e5d..09fd99c9cbd23b5f3c899bfb00c9b896
|
||||
+ // Paper end - block counting
|
||||
}
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index f185440ef2d2340f1b7e8db85ea2287478edf6b8..c6bc9b1bf6780c0fa4819e7b773b487416c18ea6 100644
|
||||
index 06d07f93e42edcfdd7d69df0b52efe2a58e7dfc1..da880f52920b1101f23ef94f3fd0dbdea218c373 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -147,7 +147,7 @@ import net.minecraft.world.waypoints.WaypointTransmitter;
|
||||
@@ -28974,7 +28979,7 @@ index f185440ef2d2340f1b7e8db85ea2287478edf6b8..c6bc9b1bf6780c0fa4819e7b773b4874
|
||||
|
||||
public Entity(EntityType<?> entityType, Level level) {
|
||||
this.type = entityType;
|
||||
@@ -1389,35 +1495,77 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
@@ -1378,35 +1484,77 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
return distance;
|
||||
}
|
||||
|
||||
@@ -29076,7 +29081,7 @@ index f185440ef2d2340f1b7e8db85ea2287478edf6b8..c6bc9b1bf6780c0fa4819e7b773b4874
|
||||
}
|
||||
|
||||
private static float[] collectCandidateStepUpHeights(AABB box, List<VoxelShape> colliders, float deltaY, float maxUpStep) {
|
||||
@@ -2691,21 +2839,110 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
@@ -2680,21 +2828,110 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
}
|
||||
|
||||
public boolean isInWall() {
|
||||
@@ -29198,7 +29203,7 @@ index f185440ef2d2340f1b7e8db85ea2287478edf6b8..c6bc9b1bf6780c0fa4819e7b773b4874
|
||||
}
|
||||
|
||||
public InteractionResult interact(Player player, InteractionHand hand) {
|
||||
@@ -4302,15 +4539,17 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
@@ -4291,15 +4528,17 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
}
|
||||
|
||||
public Iterable<Entity> getIndirectPassengers() {
|
||||
@@ -29224,7 +29229,7 @@ index f185440ef2d2340f1b7e8db85ea2287478edf6b8..c6bc9b1bf6780c0fa4819e7b773b4874
|
||||
}
|
||||
|
||||
public int countPlayerPassengers() {
|
||||
@@ -4453,77 +4692,136 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
@@ -4442,77 +4681,136 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
return Mth.lerp(partialTick, this.yRotO, this.yRot);
|
||||
}
|
||||
|
||||
@@ -29415,7 +29420,7 @@ index f185440ef2d2340f1b7e8db85ea2287478edf6b8..c6bc9b1bf6780c0fa4819e7b773b4874
|
||||
|
||||
public boolean touchingUnloadedChunk() {
|
||||
AABB aabb = this.getBoundingBox().inflate(1.0);
|
||||
@@ -4678,6 +4976,15 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
@@ -4667,6 +4965,15 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
}
|
||||
|
||||
public final void setPosRaw(double x, double y, double z, boolean forceBoundingBoxUpdate) {
|
||||
@@ -29431,7 +29436,7 @@ index f185440ef2d2340f1b7e8db85ea2287478edf6b8..c6bc9b1bf6780c0fa4819e7b773b4874
|
||||
if (!checkPosition(this, x, y, z)) {
|
||||
return;
|
||||
}
|
||||
@@ -4839,6 +5146,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
@@ -4818,6 +5125,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
|
||||
@Override
|
||||
public final void setRemoved(Entity.RemovalReason removalReason, @Nullable org.bukkit.event.entity.EntityRemoveEvent.Cause cause) { // CraftBukkit - add Bukkit remove cause
|
||||
@@ -29444,7 +29449,7 @@ index f185440ef2d2340f1b7e8db85ea2287478edf6b8..c6bc9b1bf6780c0fa4819e7b773b4874
|
||||
org.bukkit.craftbukkit.event.CraftEventFactory.callEntityRemoveEvent(this, cause); // CraftBukkit
|
||||
final boolean alreadyRemoved = this.removalReason != null; // Paper - Folia schedulers
|
||||
if (this.removalReason == null) {
|
||||
@@ -4849,7 +5162,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
@@ -4828,7 +5141,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
this.stopRiding();
|
||||
}
|
||||
|
||||
@@ -29453,7 +29458,7 @@ index f185440ef2d2340f1b7e8db85ea2287478edf6b8..c6bc9b1bf6780c0fa4819e7b773b4874
|
||||
this.levelCallback.onRemove(removalReason);
|
||||
this.onRemoval(removalReason);
|
||||
// Paper start - Folia schedulers
|
||||
@@ -4883,7 +5196,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
@@ -4862,7 +5175,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
public boolean shouldBeSaved() {
|
||||
return (this.removalReason == null || this.removalReason.shouldSave())
|
||||
&& !this.isPassenger()
|
||||
@@ -31627,7 +31632,7 @@ index 96e8dfb1ff24954656470925a1fc6280fe5e09d9..be6f37f91569c659c609e5e8d38671ca
|
||||
|
||||
public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource random) {
|
||||
diff --git a/net/minecraft/world/level/block/state/BlockBehaviour.java b/net/minecraft/world/level/block/state/BlockBehaviour.java
|
||||
index c846d5d47c6488b11930b858da946e636e025294..d4c02b9bb9bfc10484a79ede35985ba35c99bada 100644
|
||||
index 1c81dc30aabb354d18290d42dfc419d9b1581fbd..834e27ef2f7b342b074ff9e1e390e02f3ca1c399 100644
|
||||
--- a/net/minecraft/world/level/block/state/BlockBehaviour.java
|
||||
+++ b/net/minecraft/world/level/block/state/BlockBehaviour.java
|
||||
@@ -413,7 +413,7 @@ public abstract class BlockBehaviour implements FeatureElement {
|
||||
@@ -34312,7 +34317,7 @@ index 15417fab103feec3c1f7d5bd5b332e89d3ace3f5..2e6263d8b466e0f61bc72eb818044734
|
||||
}
|
||||
}
|
||||
diff --git a/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java b/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
|
||||
index 18750f1ea3101b6c0ab0b8e33c304eb7fa1ed04d..bf708ff89ea1f2c7279e48c41c4f44abc77ceebb 100644
|
||||
index 49be43ac896d60587511a97445c53c10c587a341..1b070cf7e3612dbcb170cf5d954eba5f5b3c777c 100644
|
||||
--- a/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
|
||||
@@ -14,7 +14,7 @@ import net.minecraft.util.datafix.DataFixTypes;
|
||||
|
||||
+14
-11
@@ -60,21 +60,24 @@ index 61692c07dfb75ca0c19f603aafc96c0817861107..56a1d081a28e8b38384cfca732b10346
|
||||
} else if (this.seenBy.remove(player.connection)) {
|
||||
this.serverEntity.removePairing(player);
|
||||
diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java
|
||||
index 50bde815b917c14bc4fb9163bd9df45bfa63ef47..dec11820b80094656e89e7384c4b515948280121 100644
|
||||
index 3d5a8163a7dd78a195b77c4aaebdee2dc3dee64b..e96d4dee14c05f2fa329bfb1588ec795d4e3d730 100644
|
||||
--- a/net/minecraft/server/level/ServerEntity.java
|
||||
+++ b/net/minecraft/server/level/ServerEntity.java
|
||||
@@ -104,8 +104,9 @@ public class ServerEntity {
|
||||
@@ -103,6 +103,13 @@ public class ServerEntity {
|
||||
this.trackedDataValues = entity.getEntityData().getNonDefaultValues();
|
||||
}
|
||||
|
||||
// Paper start - fix desync when a player is added to the tracker
|
||||
+ // Paper start - fix desync when a player is added to the tracker
|
||||
+ private boolean forceStateResync;
|
||||
public void onPlayerAdd() {
|
||||
- // TODO - IMPLEMENT STUBBED METHOD FROM MOONRISE
|
||||
+ public void onPlayerAdd() {
|
||||
+ this.forceStateResync = true;
|
||||
}
|
||||
// Paper end - fix desync when a player is added to the tracker
|
||||
// Paper start - Improve AbstractHurtingProjectile syncing
|
||||
@@ -159,7 +160,7 @@ public class ServerEntity {
|
||||
+ }
|
||||
+ // Paper end - fix desync when a player is added to the tracker
|
||||
+
|
||||
public void sendChanges() {
|
||||
// Paper start - optimise collisions
|
||||
if (((ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity)this.entity).moonrise$isHardColliding()) {
|
||||
@@ -141,7 +148,7 @@ public class ServerEntity {
|
||||
this.sendDirtyEntityData();
|
||||
}
|
||||
|
||||
@@ -83,7 +86,7 @@ index 50bde815b917c14bc4fb9163bd9df45bfa63ef47..dec11820b80094656e89e7384c4b5159
|
||||
byte b = Mth.packDegrees(this.entity.getYRot());
|
||||
byte b1 = Mth.packDegrees(this.entity.getXRot());
|
||||
boolean flag = Math.abs(b - this.lastSentYRot) >= 1 || Math.abs(b1 - this.lastSentXRot) >= 1;
|
||||
@@ -194,7 +195,7 @@ public class ServerEntity {
|
||||
@@ -176,7 +183,7 @@ public class ServerEntity {
|
||||
long l1 = this.positionCodec.encodeY(vec3);
|
||||
long l2 = this.positionCodec.encodeZ(vec3);
|
||||
boolean flag5 = l < -32768L || l > 32767L || l1 < -32768L || l1 > 32767L || l2 < -32768L || l2 > 32767L;
|
||||
@@ -92,7 +95,7 @@ index 50bde815b917c14bc4fb9163bd9df45bfa63ef47..dec11820b80094656e89e7384c4b5159
|
||||
|| flag5
|
||||
|| this.teleportDelay > 400
|
||||
|| this.wasRiding
|
||||
@@ -263,6 +264,7 @@ public class ServerEntity {
|
||||
@@ -245,6 +252,7 @@ public class ServerEntity {
|
||||
}
|
||||
|
||||
this.entity.hasImpulse = false;
|
||||
|
||||
@@ -48,10 +48,10 @@ index 0000000000000000000000000000000000000000..24a2090e068ad3c0d08705050944abdf
|
||||
+ }
|
||||
+}
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index 39581095ccc69d113d954ed835bdfa32d25b5489..ce7d72e02797da1ae408d0d310420e30aaa0ce28 100644
|
||||
index cff9d761dfee8a90b19fb2f3e678f99a39fc000c..0a260fdf6b198a8ab52e60bf6db2fb5eab719c48 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -1709,6 +1709,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -1718,6 +1718,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
serverLevel.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - BlockPhysicsEvent
|
||||
serverLevel.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent
|
||||
serverLevel.updateLagCompensationTick(); // Paper - lag compensation
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Tue, 24 Jun 2025 03:41:38 -0700
|
||||
Subject: [PATCH] Improve keepalive ping system
|
||||
|
||||
Send more keepalives, record all transactions within the last minute.
|
||||
We send more keepalives so that the latency calculation is more
|
||||
accurate. Since we send more keepalives, we track all pending
|
||||
keepalives in case multiple end up in flight.
|
||||
|
||||
Additionally, replace the latency calculation with a true
|
||||
average over the last 5 seconds of keepalive transactions.
|
||||
|
||||
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
|
||||
index 53f038e1b5e7a13a08a0c925c8bd3f8a40868195..f3eca351021c37b64315872d075bd0a84aeee267 100644
|
||||
--- a/net/minecraft/server/level/ServerPlayer.java
|
||||
+++ b/net/minecraft/server/level/ServerPlayer.java
|
||||
@@ -461,6 +461,70 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
||||
return this.viewDistanceHolder;
|
||||
}
|
||||
// Paper end - rewrite chunk system
|
||||
+ // Paper start - improve keepalives
|
||||
+ public long lastKeepAliveTx = System.nanoTime();
|
||||
+ public static final record KeepAliveResponse(long txTimeNS, long rxTimeNS) {
|
||||
+ public long latencyNS() {
|
||||
+ return this.rxTimeNS - this.txTimeNS;
|
||||
+ }
|
||||
+ }
|
||||
+ public static final record PendingKeepAlive(long txTimeNS, long challengeId) {}
|
||||
+
|
||||
+ public final ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue<PendingKeepAlive> pendingKeepAlives = new ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue<>();
|
||||
+
|
||||
+ 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 static final class PingCalculator {
|
||||
+
|
||||
+ private final long intervalNS;
|
||||
+ private final ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue<KeepAliveResponse> responses = new ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue<>();
|
||||
+
|
||||
+ private long timeSumNS;
|
||||
+ private int timeSumCount;
|
||||
+ private volatile long lastAverageNS;
|
||||
+
|
||||
+ public PingCalculator(long intervalNS) {
|
||||
+ this.intervalNS = intervalNS;
|
||||
+ }
|
||||
+
|
||||
+ public void update(KeepAliveResponse response) {
|
||||
+ long currTime = response.txTimeNS;
|
||||
+
|
||||
+ this.responses.add(response);
|
||||
+
|
||||
+ ++this.timeSumCount;
|
||||
+ this.timeSumNS += response.latencyNS();
|
||||
+
|
||||
+ // remove out-of-window times
|
||||
+ KeepAliveResponse removed;
|
||||
+ while ((removed = this.responses.pollIf((ka) -> (currTime - ka.txTimeNS) > this.intervalNS)) != null) {
|
||||
+ --this.timeSumCount;
|
||||
+ this.timeSumNS -= removed.latencyNS();
|
||||
+ }
|
||||
+
|
||||
+ this.lastAverageNS = this.timeSumNS / (long)this.timeSumCount;
|
||||
+ }
|
||||
+
|
||||
+ public int getAvgLatencyMS() {
|
||||
+ return (int)java.util.concurrent.TimeUnit.NANOSECONDS.toMillis(this.getAvgLatencyNS());
|
||||
+ }
|
||||
+
|
||||
+ public long getAvgLatencyNS() {
|
||||
+ return this.lastAverageNS;
|
||||
+ }
|
||||
+
|
||||
+ public it.unimi.dsi.fastutil.longs.LongArrayList getAllNS() {
|
||||
+ it.unimi.dsi.fastutil.longs.LongArrayList ret = new it.unimi.dsi.fastutil.longs.LongArrayList();
|
||||
+
|
||||
+ for (KeepAliveResponse response : this.responses) {
|
||||
+ ret.add(response.latencyNS());
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - improve keepalives
|
||||
|
||||
public ServerPlayer(MinecraftServer server, ServerLevel level, GameProfile gameProfile, ClientInformation clientInformation) {
|
||||
super(level, gameProfile);
|
||||
diff --git a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
|
||||
index 85e01c3a1536b41a0301a5a6506e058ff9633a4a..43f70a5561d6cc62aaeba6d1e39598ecb382e369 100644
|
||||
--- a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
|
||||
+++ b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
|
||||
@@ -38,12 +38,12 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
|
||||
protected final MinecraftServer server;
|
||||
public final Connection connection; // Paper
|
||||
private final boolean transferred;
|
||||
- private long keepAliveTime;
|
||||
- private boolean keepAlivePending;
|
||||
- private long keepAliveChallenge;
|
||||
+ //private long keepAliveTime; // Paper - improve keepalives
|
||||
+ //private boolean keepAlivePending; // Paper - improve keepalives
|
||||
+ //private long keepAliveChallenge; // Paper - improve keepalives
|
||||
private long closedListenerTime;
|
||||
private boolean closed = false;
|
||||
- private int latency;
|
||||
+ private volatile int latency; // Paper - improve keepalives - make volatile
|
||||
private volatile boolean suspendFlushingOnServerThread = false;
|
||||
// CraftBukkit start
|
||||
protected final net.minecraft.server.level.ServerPlayer player;
|
||||
@@ -57,7 +57,7 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
|
||||
public ServerCommonPacketListenerImpl(MinecraftServer server, Connection connection, CommonListenerCookie cookie, net.minecraft.server.level.ServerPlayer player) { // CraftBukkit
|
||||
this.server = server;
|
||||
this.connection = connection;
|
||||
- this.keepAliveTime = Util.getMillis();
|
||||
+ //this.keepAliveTime = Util.getMillis(); // Paper - improve keepalives
|
||||
this.latency = cookie.latency();
|
||||
this.transferred = cookie.transferred();
|
||||
// CraftBukkit start - add fields and methods
|
||||
@@ -120,13 +120,41 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
|
||||
|
||||
@Override
|
||||
public void handleKeepAlive(ServerboundKeepAlivePacket packet) {
|
||||
- if (this.keepAlivePending && packet.getId() == this.keepAliveChallenge) {
|
||||
- int i = (int)(Util.getMillis() - this.keepAliveTime);
|
||||
- this.latency = (this.latency * 3 + i) / 4;
|
||||
- this.keepAlivePending = false;
|
||||
- } else if (!this.isSingleplayerOwner()) {
|
||||
- this.disconnectAsync(TIMEOUT_DISCONNECTION_MESSAGE, org.bukkit.event.player.PlayerKickEvent.Cause.TIMEOUT); // Paper - add proper async disconnect
|
||||
+ // Paper start - improve keepalives
|
||||
+ long now = System.nanoTime();
|
||||
+ net.minecraft.server.level.ServerPlayer.PendingKeepAlive pending = this.player.pendingKeepAlives.peek();
|
||||
+ if (pending != null && pending.challengeId() == packet.getId()) {
|
||||
+ this.player.pendingKeepAlives.remove(pending);
|
||||
+
|
||||
+ net.minecraft.server.level.ServerPlayer.KeepAliveResponse response = new net.minecraft.server.level.ServerPlayer.KeepAliveResponse(pending.txTimeNS(), now);
|
||||
+
|
||||
+ this.player.pingCalculator1m.update(response);
|
||||
+ this.player.pingCalculator5s.update(response);
|
||||
+
|
||||
+ this.latency = this.player.pingCalculator5s.getAvgLatencyMS();
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ for (java.util.Iterator<net.minecraft.server.level.ServerPlayer.PendingKeepAlive> itr = this.player.pendingKeepAlives.iterator(); itr.hasNext();) {
|
||||
+ net.minecraft.server.level.ServerPlayer.PendingKeepAlive ka = itr.next();
|
||||
+ if (ka.challengeId() == packet.getId()) {
|
||||
+ itr.remove();
|
||||
+
|
||||
+ if (!this.processedDisconnect) {
|
||||
+ LOGGER.info("Disconnecting " + this.player.getScoreboardName() + " for sending keepalive response (" + packet.getId() + ") out-of-order!");
|
||||
+ this.disconnectAsync(TIMEOUT_DISCONNECTION_MESSAGE, org.bukkit.event.player.PlayerKickEvent.Cause.TIMEOUT);
|
||||
+ return;
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
}
|
||||
+
|
||||
+ if (!this.processedDisconnect) {
|
||||
+ LOGGER.info("Disconnecting " + this.player.getScoreboardName() + " for sending keepalive response (" + packet.getId() + ") without matching challenge!");
|
||||
+ this.disconnectAsync(TIMEOUT_DISCONNECTION_MESSAGE, org.bukkit.event.player.PlayerKickEvent.Cause.TIMEOUT);
|
||||
+ return;
|
||||
+ }
|
||||
+ // Paper end - improve keepalives
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -247,20 +275,23 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
|
||||
protected void keepConnectionAlive() {
|
||||
Profiler.get().push("keepAlive");
|
||||
long millis = Util.getMillis();
|
||||
- // Paper start - give clients a longer time to respond to pings as per pre 1.12.2 timings
|
||||
- // This should effectively place the keepalive handling back to "as it was" before 1.12.2
|
||||
- final long elapsedTime = millis - this.keepAliveTime;
|
||||
- if (!this.isSingleplayerOwner() && elapsedTime >= 15000L) { // use vanilla's 15000L between keep alive packets
|
||||
- if (this.keepAlivePending) {
|
||||
- if (!this.processedDisconnect && elapsedTime >= KEEPALIVE_LIMIT) { // check keepalive limit, don't fire if already disconnected
|
||||
- this.disconnect(TIMEOUT_DISCONNECTION_MESSAGE, org.bukkit.event.player.PlayerKickEvent.Cause.TIMEOUT); // Paper - kick event cause
|
||||
- }
|
||||
- // Paper end - give clients a longer time to respond to pings as per pre 1.12.2 timings
|
||||
- } else if (this.checkIfClosed(millis)) {
|
||||
- this.keepAlivePending = true;
|
||||
- this.keepAliveTime = millis;
|
||||
- this.keepAliveChallenge = millis;
|
||||
- this.send(new ClientboundKeepAlivePacket(this.keepAliveChallenge));
|
||||
+ // Paper start - improve keepalives
|
||||
+ if (this.checkIfClosed(millis) && !this.processedDisconnect) {
|
||||
+ long currTime = System.nanoTime();
|
||||
+
|
||||
+ if ((currTime - this.player.lastKeepAliveTx) >= java.util.concurrent.TimeUnit.SECONDS.toNanos(1L)) {
|
||||
+ this.player.lastKeepAliveTx = currTime;
|
||||
+
|
||||
+ net.minecraft.server.level.ServerPlayer.PendingKeepAlive pka = new net.minecraft.server.level.ServerPlayer.PendingKeepAlive(currTime, millis);
|
||||
+ this.player.pendingKeepAlives.add(pka);
|
||||
+ this.send(new ClientboundKeepAlivePacket(pka.challengeId()));
|
||||
+ }
|
||||
+
|
||||
+ net.minecraft.server.level.ServerPlayer.PendingKeepAlive oldest = this.player.pendingKeepAlives.peek();
|
||||
+ if (oldest != null && (currTime - oldest.txTimeNS()) > java.util.concurrent.TimeUnit.MILLISECONDS.toNanos(KEEPALIVE_LIMIT)) {
|
||||
+ LOGGER.warn(this.player.getScoreboardName() + " was kicked due to keepalive timeout!");
|
||||
+ this.disconnect(TIMEOUT_DISCONNECTION_MESSAGE, org.bukkit.event.player.PlayerKickEvent.Cause.TIMEOUT);
|
||||
+ // Paper end - improve keepalives
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Tue, 24 Jun 2025 07:05:51 -0700
|
||||
Subject: [PATCH] Optimise EntityScheduler ticking
|
||||
|
||||
The vast majority of the time, there are no tasks scheduled to
|
||||
the EntityScheduler. We can avoid iterating the entire entity list
|
||||
by tracking which schedulers have any tasks scheduled.
|
||||
|
||||
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java
|
||||
index 5f2deeb5cc01d8bbeb7449bd4e59c466b3dfdf57..82824ae7ffbced513a8bcace684af94916135e84 100644
|
||||
--- a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java
|
||||
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java
|
||||
@@ -96,6 +96,7 @@ public final class ServerEntityLookup extends EntityLookup {
|
||||
if (entity instanceof ThrownEnderpearl enderpearl) {
|
||||
this.addEnderPearl(CoordinateUtils.getChunkKey(enderpearl.chunkPosition()));
|
||||
}
|
||||
+ entity.registerScheduler(); // Paper - optimise Folia entity scheduler
|
||||
}
|
||||
|
||||
@Override
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index 0a260fdf6b198a8ab52e60bf6db2fb5eab719c48..52fa5112cd90ba766c94512a02401dd3aee82cc9 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -1654,33 +1654,22 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
}
|
||||
}
|
||||
|
||||
+ public final io.papermc.paper.threadedregions.EntityScheduler.EntitySchedulerTickList entitySchedulerTickList = new io.papermc.paper.threadedregions.EntityScheduler.EntitySchedulerTickList(); // Paper - optimise Folia entity scheduler
|
||||
+
|
||||
protected void tickChildren(BooleanSupplier hasTimeLeft) {
|
||||
ProfilerFiller profilerFiller = Profiler.get();
|
||||
this.getPlayerList().getPlayers().forEach(serverPlayer1 -> serverPlayer1.connection.suspendFlushing());
|
||||
this.server.getScheduler().mainThreadHeartbeat(); // CraftBukkit
|
||||
- // Paper start - Folia scheduler API
|
||||
- ((io.papermc.paper.threadedregions.scheduler.FoliaGlobalRegionScheduler) org.bukkit.Bukkit.getGlobalRegionScheduler()).tick();
|
||||
- for (ServerPlayer player : this.playerList.players) {
|
||||
- if (!this.playerList.players.contains(player)) {
|
||||
+ // Paper start - optimise Folia entity scheduler
|
||||
+ ((io.papermc.paper.threadedregions.scheduler.FoliaGlobalRegionScheduler)org.bukkit.Bukkit.getGlobalRegionScheduler()).tick();
|
||||
+ for (io.papermc.paper.threadedregions.EntityScheduler scheduler : this.entitySchedulerTickList.getAllSchedulers()) {
|
||||
+ if (scheduler.isRetired()) {
|
||||
continue;
|
||||
}
|
||||
- final org.bukkit.craftbukkit.entity.CraftEntity bukkit = player.getBukkitEntityRaw();
|
||||
- if (bukkit != null) {
|
||||
- bukkit.taskScheduler.executeTick();
|
||||
- }
|
||||
+
|
||||
+ scheduler.executeTick();
|
||||
}
|
||||
- getAllLevels().forEach(level -> {
|
||||
- for (final net.minecraft.world.entity.Entity entity : io.papermc.paper.FeatureHooks.getAllEntities(level)) {
|
||||
- if (entity.isRemoved() || entity instanceof ServerPlayer) {
|
||||
- continue;
|
||||
- }
|
||||
- final org.bukkit.craftbukkit.entity.CraftEntity bukkit = entity.getBukkitEntityRaw();
|
||||
- if (bukkit != null) {
|
||||
- bukkit.taskScheduler.executeTick();
|
||||
- }
|
||||
- }
|
||||
- });
|
||||
- // Paper end - Folia scheduler API
|
||||
+ // Paper end - optimise Folia entity scheduler
|
||||
io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.CALLBACK_MANAGER.handleQueue(this.tickCount); // Paper
|
||||
profilerFiller.push("commandFunctions");
|
||||
this.getFunctions().tick();
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index da880f52920b1101f23ef94f3fd0dbdea218c373..3d2c0a4d3a1f9d3e5cc6cd0cdb988ae1205de821 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -5165,6 +5165,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
this.getBukkitEntity().taskScheduler.retire();
|
||||
}
|
||||
// Paper end - Folia schedulers
|
||||
+ // Paper start - optimise Folia entity scheduler
|
||||
+ public final void registerScheduler() {
|
||||
+ this.getBukkitEntity().taskScheduler.registerTo(net.minecraft.server.MinecraftServer.getServer().entitySchedulerTickList);
|
||||
+ }
|
||||
+ // Paper end - optimise Folia entity scheduler
|
||||
|
||||
@Override
|
||||
public void setLevelCallback(EntityInLevelCallback levelCallback) {
|
||||
@@ -1,6 +1,6 @@
|
||||
--- /dev/null
|
||||
+++ b/io/papermc/paper/FeatureHooks.java
|
||||
@@ -1,0 +_,241 @@
|
||||
@@ -1,0 +_,246 @@
|
||||
+package io.papermc.paper;
|
||||
+
|
||||
+import io.papermc.paper.command.PaperSubcommand;
|
||||
@@ -33,6 +33,11 @@
|
||||
+
|
||||
+public final class FeatureHooks {
|
||||
+
|
||||
+ // this includes non-accessible entities
|
||||
+ public static Iterable<Entity> getAllEntities(final net.minecraft.server.level.ServerLevel world) {
|
||||
+ return ((net.minecraft.world.level.entity.LevelEntityGetterAdapter<Entity>)world.getEntities()).sectionStorage.getAllEntities();
|
||||
+ }
|
||||
+
|
||||
+ public static void setPlayerChunkUnloadDelay(final long ticks) {
|
||||
+ }
|
||||
+
|
||||
|
||||
@@ -76,11 +76,15 @@
|
||||
CommandSourceStack commandSourceStack = parseResults.getContext().getSource();
|
||||
Profiler.get().push(() -> "/" + command);
|
||||
ContextChain<CommandSourceStack> contextChain = finishParsing(parseResults, command, commandSourceStack);
|
||||
@@ -313,9 +_,10 @@
|
||||
@@ -312,10 +_,13 @@
|
||||
)
|
||||
);
|
||||
}
|
||||
} catch (Exception var12) {
|
||||
+ if (throwCommandError) throw var12; // Paper
|
||||
- } catch (Exception var12) {
|
||||
+ // Paper start
|
||||
+ } catch (Throwable var12) { // always gracefully handle it, no matter how bad:tm:
|
||||
+ if (throwCommandError) throw var12; // rethrow directly if requested
|
||||
+ // Paper end
|
||||
MutableComponent mutableComponent = Component.literal(var12.getMessage() == null ? var12.getClass().getName() : var12.getMessage());
|
||||
- if (LOGGER.isDebugEnabled()) {
|
||||
- LOGGER.error("Command exception: /{}", command, var12);
|
||||
|
||||
+1
-12
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/network/protocol/game/VecDeltaCodec.java
|
||||
+++ b/net/minecraft/network/protocol/game/VecDeltaCodec.java
|
||||
@@ -5,16 +_,16 @@
|
||||
@@ -5,7 +_,7 @@
|
||||
|
||||
public class VecDeltaCodec {
|
||||
private static final double TRUNCATION_STEPS = 4096.0;
|
||||
@@ -9,14 +9,3 @@
|
||||
|
||||
@VisibleForTesting
|
||||
static long encode(double value) {
|
||||
- return Math.round(value * 4096.0);
|
||||
+ return Math.round(value * 4096.0); // Paper - Fix MC-4; diff on change
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static double decode(long value) {
|
||||
- return value / 4096.0;
|
||||
+ return value / 4096.0; // Paper - Fix MC-4; diff on change
|
||||
}
|
||||
|
||||
public Vec3 decode(long x, long y, long z) {
|
||||
@@ -984,16 +984,25 @@
|
||||
ObjectArrayList<GameProfile> list = new ObjectArrayList<>(min);
|
||||
int randomInt = Mth.nextInt(this.random, 0, players.size() - min);
|
||||
|
||||
@@ -1040,17 +_,66 @@
|
||||
@@ -1040,17 +_,75 @@
|
||||
protected void tickChildren(BooleanSupplier hasTimeLeft) {
|
||||
ProfilerFiller profilerFiller = Profiler.get();
|
||||
this.getPlayerList().getPlayers().forEach(serverPlayer1 -> serverPlayer1.connection.suspendFlushing());
|
||||
+ this.server.getScheduler().mainThreadHeartbeat(); // CraftBukkit
|
||||
+ // Paper start - Folia scheduler API
|
||||
+ ((io.papermc.paper.threadedregions.scheduler.FoliaGlobalRegionScheduler) org.bukkit.Bukkit.getGlobalRegionScheduler()).tick();
|
||||
+ for (ServerPlayer player : this.playerList.players) {
|
||||
+ if (!this.playerList.players.contains(player)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ final org.bukkit.craftbukkit.entity.CraftEntity bukkit = player.getBukkitEntityRaw();
|
||||
+ if (bukkit != null) {
|
||||
+ bukkit.taskScheduler.executeTick();
|
||||
+ }
|
||||
+ }
|
||||
+ getAllLevels().forEach(level -> {
|
||||
+ for (final net.minecraft.world.entity.Entity entity : level.getEntities().getAll()) {
|
||||
+ if (entity.isRemoved()) {
|
||||
+ for (final net.minecraft.world.entity.Entity entity : io.papermc.paper.FeatureHooks.getAllEntities(level)) {
|
||||
+ if (entity.isRemoved() || entity instanceof ServerPlayer) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ final org.bukkit.craftbukkit.entity.CraftEntity bukkit = entity.getBukkitEntityRaw();
|
||||
|
||||
@@ -28,30 +28,7 @@
|
||||
this.level = level;
|
||||
this.broadcast = broadcast;
|
||||
this.entity = entity;
|
||||
@@ -98,21 +_,45 @@
|
||||
this.trackedDataValues = entity.getEntityData().getNonDefaultValues();
|
||||
}
|
||||
|
||||
+ // Paper start - fix desync when a player is added to the tracker
|
||||
+ public void onPlayerAdd() {
|
||||
+ // TODO - IMPLEMENT STUBBED METHOD FROM MOONRISE
|
||||
+ }
|
||||
+ // Paper end - fix desync when a player is added to the tracker
|
||||
+ // Paper start - Improve AbstractHurtingProjectile syncing
|
||||
+ // This can be removed when velocity is no longer downsized when sending it over the protocol.
|
||||
+ // We create a packet that only updates the velocity on the client.
|
||||
+ private static final Set<net.minecraft.world.entity.Relative> RELATIVES = net.minecraft.world.entity.Relative.union(Set.of(net.minecraft.world.entity.Relative.X, net.minecraft.world.entity.Relative.Y, net.minecraft.world.entity.Relative.Z), net.minecraft.world.entity.Relative.ROTATION);
|
||||
+ public Packet<ClientGamePacketListener> getAccurateVelocityPacket() {
|
||||
+ return new net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket(this.entity.getId(),
|
||||
+ new net.minecraft.world.entity.PositionMoveRotation(net.minecraft.world.phys.Vec3.ZERO, this.lastSentMovement, 0, 0),
|
||||
+ RELATIVES,
|
||||
+ this.wasOnGround
|
||||
+ );
|
||||
+ }
|
||||
+ // Paper end - Improve AbstractHurtingProjectile syncing
|
||||
+
|
||||
public void sendChanges() {
|
||||
List<Entity> passengers = this.entity.getPassengers();
|
||||
@@ -103,16 +_,22 @@
|
||||
if (!passengers.equals(this.lastPassengers)) {
|
||||
List<UUID> list = this.mountedOrDismounted(passengers).map(Entity::getUUID).toList();
|
||||
this.broadcastWithIgnore.accept(new ClientboundSetPassengersPacket(this.entity), list);
|
||||
@@ -93,15 +70,6 @@
|
||||
Packet<?> packet = null;
|
||||
boolean flag2 = flag1 || this.tickCount % 60 == 0;
|
||||
boolean flag3 = false;
|
||||
@@ -188,7 +_,7 @@
|
||||
.accept(
|
||||
new ClientboundBundlePacket(
|
||||
List.of(
|
||||
- new ClientboundSetEntityMotionPacket(this.entity.getId(), this.lastSentMovement),
|
||||
+ this.getAccurateVelocityPacket(), // Paper - Improve AbstractHurtingProjectile syncing
|
||||
new ClientboundProjectilePowerPacket(abstractHurtingProjectile.getId(), abstractHurtingProjectile.accelerationPower)
|
||||
)
|
||||
)
|
||||
@@ -227,6 +_,25 @@
|
||||
|
||||
this.tickCount++;
|
||||
@@ -163,20 +131,6 @@
|
||||
}
|
||||
|
||||
if (!this.entity.getPassengers().isEmpty()) {
|
||||
@@ -326,6 +_,13 @@
|
||||
if (this.entity instanceof Leashable leashable && leashable.isLeashed()) {
|
||||
consumer.accept(new ClientboundSetEntityLinkPacket(this.entity, leashable.getLeashHolder()));
|
||||
}
|
||||
+ // Paper start - Improve AbstractHurtingProjectile syncing
|
||||
+ // We need to send our more accurate velocity and our acceleration power
|
||||
+ if (this.entity instanceof AbstractHurtingProjectile abstractHurtingProjectile) {
|
||||
+ consumer.accept(new ClientboundProjectilePowerPacket(abstractHurtingProjectile.getId(), abstractHurtingProjectile.accelerationPower));
|
||||
+ consumer.accept(this.getAccurateVelocityPacket());
|
||||
+ }
|
||||
+ // Paper end - Improve AbstractHurtingProjectile syncing
|
||||
}
|
||||
|
||||
public Vec3 getPositionBase() {
|
||||
@@ -359,6 +_,11 @@
|
||||
if (this.entity instanceof LivingEntity) {
|
||||
Set<AttributeInstance> attributesToSync = ((LivingEntity)this.entity).getAttributes().getAttributesToSync();
|
||||
|
||||
@@ -1334,7 +1334,8 @@
|
||||
if (traceItem) {
|
||||
- ItemStack itemStack = itemEntity != null ? itemEntity.getItem() : ItemStack.EMPTY;
|
||||
if (!itemStack.isEmpty()) {
|
||||
this.awardStat(Stats.ITEM_DROPPED.get(itemStack.getItem()), droppedItem.getCount());
|
||||
- this.awardStat(Stats.ITEM_DROPPED.get(itemStack.getItem()), droppedItem.getCount());
|
||||
+ this.awardStat(Stats.ITEM_DROPPED.get(itemStack.getItem()), itemStack.getCount()); // Paper - use size from dropped item
|
||||
this.awardStat(Stats.DROP);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-5
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
|
||||
+++ b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
|
||||
@@ -29,30 +_,67 @@
|
||||
@@ -29,14 +_,14 @@
|
||||
import net.minecraft.util.profiling.Profiler;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
@@ -15,11 +15,9 @@
|
||||
- protected final Connection connection;
|
||||
+ public final Connection connection; // Paper
|
||||
private final boolean transferred;
|
||||
- private long keepAliveTime;
|
||||
+ private long keepAliveTime = Util.getMillis(); // Paper
|
||||
private long keepAliveTime;
|
||||
private boolean keepAlivePending;
|
||||
private long keepAliveChallenge;
|
||||
private long closedListenerTime;
|
||||
@@ -45,14 +_,51 @@
|
||||
private boolean closed = false;
|
||||
private int latency;
|
||||
private volatile boolean suspendFlushingOnServerThread = false;
|
||||
|
||||
+15
-4
@@ -295,7 +295,7 @@
|
||||
|
||||
rootVehicle.absSnapTo(d, d1, d2, f, f1);
|
||||
+ // CraftBukkit start - fire PlayerMoveEvent TODO: this should be removed.
|
||||
+ this.player.absSnapTo(x, y, z, this.player.getYRot(), this.player.getXRot()); // Paper - TODO: This breaks alot of stuff
|
||||
+ this.player.absSnapTo(d, d1, d2, this.player.getYRot(), this.player.getXRot()); // Paper - TODO: This breaks alot of stuff
|
||||
+ org.bukkit.entity.Player player = this.getCraftPlayer();
|
||||
+ if (!this.hasMoved) {
|
||||
+ this.lastPosX = prevX;
|
||||
@@ -1146,8 +1146,7 @@
|
||||
|
||||
return;
|
||||
case RELEASE_USE_ITEM:
|
||||
- this.player.releaseUsingItem();
|
||||
+ if (this.player.getUseItem() == this.player.getItemInHand(this.player.getUsedItemHand())) this.player.releaseUsingItem(); // Paper - validate use item before processing release
|
||||
this.player.releaseUsingItem();
|
||||
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.updateEquipmentOnPlayerActions) this.player.detectEquipmentUpdates(); // Paper - Force update attributes.
|
||||
return;
|
||||
case START_DESTROY_BLOCK:
|
||||
@@ -2610,7 +2609,7 @@
|
||||
if (!this.receivedMovementThisTick) {
|
||||
this.player.setKnownMovement(Vec3.ZERO);
|
||||
}
|
||||
@@ -2078,4 +_,17 @@
|
||||
@@ -2078,4 +_,29 @@
|
||||
interface EntityInteraction {
|
||||
InteractionResult run(ServerPlayer player, Entity entity, InteractionHand hand);
|
||||
}
|
||||
@@ -2627,4 +2626,16 @@
|
||||
+ return event;
|
||||
+ }
|
||||
+ // Paper end - Add fail move event
|
||||
+
|
||||
+ // Paper start - Implement click callbacks with custom click action
|
||||
+ @Override
|
||||
+ public void handleCustomClickAction(final net.minecraft.network.protocol.common.ServerboundCustomClickActionPacket packet) {
|
||||
+ super.handleCustomClickAction(packet);
|
||||
+ if (packet.id().equals(io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.CLICK_CALLBACK_RESOURCE_LOCATION)) {
|
||||
+ packet.payload().ifPresent(tag ->
|
||||
+ io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.CALLBACK_MANAGER.tryRunCallback(this.player.getBukkitEntity(), tag)
|
||||
+ );
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - Implement click callbacks with custom click action
|
||||
}
|
||||
@@ -85,7 +85,7 @@
|
||||
+ // Paper end - Share random for entities to make them more random
|
||||
+ public @Nullable org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason; // Paper - Entity#getEntitySpawnReason
|
||||
+
|
||||
+ private @Nullable org.bukkit.craftbukkit.entity.CraftEntity bukkitEntity;
|
||||
+ private volatile @Nullable org.bukkit.craftbukkit.entity.CraftEntity bukkitEntity; // Paper - Folia schedulers - volatile
|
||||
+
|
||||
+ public org.bukkit.craftbukkit.entity.CraftEntity getBukkitEntity() {
|
||||
+ if (this.bukkitEntity == null) {
|
||||
@@ -227,7 +227,7 @@
|
||||
}
|
||||
|
||||
protected abstract void defineSynchedData(SynchedEntityData.Builder builder);
|
||||
@@ -384,6 +_,59 @@
|
||||
@@ -384,6 +_,48 @@
|
||||
return this.entityData;
|
||||
}
|
||||
|
||||
@@ -271,17 +271,6 @@
|
||||
+
|
||||
+ to.connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket(this.id, values));
|
||||
+ }
|
||||
+
|
||||
+ // This method forces the entity position to be resynced the next tick, using the exact position.
|
||||
+ public void resendPossiblyDesyncedPosition() {
|
||||
+ ServerLevel world = (net.minecraft.server.level.ServerLevel)this.level();
|
||||
+ net.minecraft.server.level.ChunkMap.TrackedEntity tracker = world == null ? null : world.getChunkSource().chunkMap.entityMap.get(this.getId());
|
||||
+ if (tracker == null) {
|
||||
+ return;
|
||||
+ }
|
||||
+ final net.minecraft.server.level.ServerEntity serverEntity = tracker.serverEntity;
|
||||
+ serverEntity.onPlayerAdd();
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
@Override
|
||||
@@ -1797,7 +1786,7 @@
|
||||
}
|
||||
|
||||
public void addDeltaMovement(Vec3 addend) {
|
||||
@@ -3661,9 +_,45 @@
|
||||
@@ -3661,9 +_,35 @@
|
||||
return this.getZ((2.0 * this.random.nextDouble() - 1.0) * scale);
|
||||
}
|
||||
|
||||
@@ -1826,16 +1815,6 @@
|
||||
+ return;
|
||||
+ }
|
||||
+ // Paper end - Block invalid positions and bounding box
|
||||
+ // Paper start - Fix MC-4
|
||||
+ if (this instanceof ItemEntity) {
|
||||
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().misc.fixEntityPositionDesync) {
|
||||
+ // encode/decode from VecDeltaCodec todo computation changed?
|
||||
+ x = Mth.lfloor(x * 4096.0) * (1 / 4096.0);
|
||||
+ y = Mth.lfloor(y * 4096.0) * (1 / 4096.0);
|
||||
+ z = Mth.lfloor(z * 4096.0) * (1 / 4096.0);
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - Fix MC-4
|
||||
if (this.position.x != x || this.position.y != y || this.position.z != z) {
|
||||
+ synchronized (this.posLock) { // Paper - detailed watchdog information
|
||||
this.position = new Vec3(x, y, z);
|
||||
|
||||
-8
@@ -8,14 +8,6 @@
|
||||
|
||||
public ItemFrame(EntityType<? extends ItemFrame> entityType, Level level) {
|
||||
super(entityType, level);
|
||||
@@ -92,6 +_,7 @@
|
||||
this.xRotO = this.getXRot();
|
||||
this.yRotO = this.getYRot();
|
||||
this.recalculateBoundingBox();
|
||||
+ this.resendPossiblyDesyncedPosition(); // Paper - Fix MC-297817
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -102,6 +_,12 @@
|
||||
|
||||
@Override
|
||||
|
||||
+22
@@ -27,3 +27,25 @@
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +_,12 @@
|
||||
return this.level().getBlockState(this.pos).is(BlockTags.FENCES);
|
||||
}
|
||||
|
||||
+ // Paper start - Track if a knot was created
|
||||
public static LeashFenceKnotEntity getOrCreateKnot(Level level, BlockPos pos) {
|
||||
+ return getOrCreateKnot(level, pos, null);
|
||||
+ }
|
||||
+ public static LeashFenceKnotEntity getOrCreateKnot(Level level, BlockPos pos, @Nullable org.apache.commons.lang3.mutable.MutableBoolean created) {
|
||||
+ // Paper end - Track if a knot was created
|
||||
int x = pos.getX();
|
||||
int y = pos.getY();
|
||||
int z = pos.getZ();
|
||||
@@ -134,7 +_,7 @@
|
||||
}
|
||||
|
||||
LeashFenceKnotEntity leashFenceKnotEntity1 = new LeashFenceKnotEntity(level, pos);
|
||||
- level.addFreshEntity(leashFenceKnotEntity1);
|
||||
+ if (level.addFreshEntity(leashFenceKnotEntity1) && created != null) { created.setTrue(); } // Paper - Track if a knot was created
|
||||
return leashFenceKnotEntity1;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,21 @@
|
||||
}
|
||||
|
||||
public ItemEntity(Level level, double posX, double posY, double posZ, ItemStack itemStack, double deltaX, double deltaY, double deltaZ) {
|
||||
@@ -79,6 +_,14 @@
|
||||
this.bobOffs = other.bobOffs;
|
||||
}
|
||||
|
||||
+ // Paper start - Require item entities to send their location precisely (Fixes MC-4)
|
||||
+ {
|
||||
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().misc.sendFullPosForItemEntities) {
|
||||
+ this.setRequiresPrecisePosition(true);
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - Require item entities to send their location precisely (Fixes MC-4)
|
||||
+
|
||||
@Override
|
||||
public boolean dampensVibrations() {
|
||||
return this.getItem().is(ItemTags.DAMPENS_VIBRATIONS);
|
||||
@@ -116,7 +_,7 @@
|
||||
@Override
|
||||
public void tick() {
|
||||
|
||||
+21
-8
@@ -43,23 +43,36 @@
|
||||
if (this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) {
|
||||
LocalDate localDate = LocalDate.now();
|
||||
int i = localDate.get(ChronoField.DAY_OF_MONTH);
|
||||
@@ -196,9 +_,19 @@
|
||||
@@ -188,7 +_,8 @@
|
||||
|
||||
@Override
|
||||
public void performRangedAttack(LivingEntity target, float distanceFactor) {
|
||||
- ItemStack itemInHand = this.getItemInHand(ProjectileUtil.getWeaponHoldingHand(this, Items.BOW));
|
||||
+ net.minecraft.world.InteractionHand hand = ProjectileUtil.getWeaponHoldingHand(this, Items.BOW); // Paper - call EntityShootBowEvent
|
||||
+ ItemStack itemInHand = this.getItemInHand(hand); // Paper - call EntityShootBowEvent
|
||||
ItemStack projectile = this.getProjectile(itemInHand);
|
||||
AbstractArrow arrow = this.getArrow(projectile, distanceFactor, itemInHand);
|
||||
double d = target.getX() - this.getX();
|
||||
@@ -196,9 +_,21 @@
|
||||
double d2 = target.getZ() - this.getZ();
|
||||
double squareRoot = Math.sqrt(d * d + d2 * d2);
|
||||
if (this.level() instanceof ServerLevel serverLevel) {
|
||||
+ // CraftBukkit start
|
||||
+ org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(this, this.getMainHandItem(), arrow.getPickupItem(), arrow, net.minecraft.world.InteractionHand.MAIN_HAND, distanceFactor, true); // Paper - improve entity shoot bow event, add arrow stack to event
|
||||
- Projectile.spawnProjectileUsingShoot(
|
||||
+ Projectile.Delayed<AbstractArrow> delayedEntity = Projectile.spawnProjectileUsingShootDelayed( // Paper - delayed
|
||||
arrow, serverLevel, projectile, d, d1 + squareRoot * 0.2F, d2, 1.6F, 14 - serverLevel.getDifficulty().getId() * 4
|
||||
);
|
||||
+
|
||||
+ // Paper start - call EntityShootBowEvent
|
||||
+ org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(this, itemInHand, arrow.getPickupItem(), arrow, hand, distanceFactor, true);
|
||||
+ if (event.isCancelled()) {
|
||||
+ event.getProjectile().remove();
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (event.getProjectile() == arrow.getBukkitEntity()) {
|
||||
+ // CraftBukkit end
|
||||
Projectile.spawnProjectileUsingShoot(
|
||||
arrow, serverLevel, projectile, d, d1 + squareRoot * 0.2F, d2, 1.6F, 14 - serverLevel.getDifficulty().getId() * 4
|
||||
);
|
||||
+ } // CraftBukkit
|
||||
+ delayedEntity.spawn();
|
||||
+ }
|
||||
+ // Paper end - call EntityShootBowEvent
|
||||
}
|
||||
|
||||
this.playSound(SoundEvents.SKELETON_SHOOT, 1.0F, 1.0F / (this.getRandom().nextFloat() * 0.4F + 0.8F));
|
||||
|
||||
+20
-7
@@ -1,22 +1,35 @@
|
||||
--- a/net/minecraft/world/entity/monster/Illusioner.java
|
||||
+++ b/net/minecraft/world/entity/monster/Illusioner.java
|
||||
@@ -180,9 +_,19 @@
|
||||
@@ -172,7 +_,8 @@
|
||||
|
||||
@Override
|
||||
public void performRangedAttack(LivingEntity target, float distanceFactor) {
|
||||
- ItemStack itemInHand = this.getItemInHand(ProjectileUtil.getWeaponHoldingHand(this, Items.BOW));
|
||||
+ net.minecraft.world.InteractionHand hand = ProjectileUtil.getWeaponHoldingHand(this, Items.BOW); // Paper - call EntityShootBowEvent
|
||||
+ ItemStack itemInHand = this.getItemInHand(hand); // Paper - call EntityShootBowEvent
|
||||
ItemStack projectile = this.getProjectile(itemInHand);
|
||||
AbstractArrow mobArrow = ProjectileUtil.getMobArrow(this, projectile, distanceFactor, itemInHand);
|
||||
double d = target.getX() - this.getX();
|
||||
@@ -180,9 +_,21 @@
|
||||
double d2 = target.getZ() - this.getZ();
|
||||
double squareRoot = Math.sqrt(d * d + d2 * d2);
|
||||
if (this.level() instanceof ServerLevel serverLevel) {
|
||||
+ // Paper start - EntityShootBowEvent
|
||||
+ org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(this, this.getMainHandItem(), mobArrow.getPickupItem(), mobArrow, target.getUsedItemHand(), distanceFactor, true);
|
||||
- Projectile.spawnProjectileUsingShoot(
|
||||
+ Projectile.Delayed<AbstractArrow> delayedEntity = Projectile.spawnProjectileUsingShootDelayed( // Paper - delayed
|
||||
mobArrow, serverLevel, projectile, d, d1 + squareRoot * 0.2F, d2, 1.6F, 14 - serverLevel.getDifficulty().getId() * 4
|
||||
);
|
||||
+
|
||||
+ // Paper start - call EntityShootBowEvent
|
||||
+ org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(this, itemInHand, mobArrow.getPickupItem(), mobArrow, hand, distanceFactor, true);
|
||||
+ if (event.isCancelled()) {
|
||||
+ event.getProjectile().remove();
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (event.getProjectile() == mobArrow.getBukkitEntity()) {
|
||||
Projectile.spawnProjectileUsingShoot(
|
||||
mobArrow, serverLevel, projectile, d, d1 + squareRoot * 0.2F, d2, 1.6F, 14 - serverLevel.getDifficulty().getId() * 4
|
||||
);
|
||||
+ delayedEntity.spawn();
|
||||
+ }
|
||||
+ // Paper end - EntityShootBowEvent
|
||||
+ // Paper end - call EntityShootBowEvent
|
||||
}
|
||||
|
||||
this.playSound(SoundEvents.SKELETON_SHOOT, 1.0F, 1.0F / (this.getRandom().nextFloat() * 0.4F + 0.8F));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/world/item/LeadItem.java
|
||||
+++ b/net/minecraft/world/item/LeadItem.java
|
||||
@@ -26,24 +_,43 @@
|
||||
@@ -26,24 +_,46 @@
|
||||
if (blockState.is(BlockTags.FENCES)) {
|
||||
Player player = context.getPlayer();
|
||||
if (!level.isClientSide && player != null) {
|
||||
@@ -22,15 +22,19 @@
|
||||
+ for (java.util.Iterator<Leashable> iterator = list.iterator(); iterator.hasNext();) { // Paper - use iterator to remove
|
||||
+ Leashable leashable = iterator.next(); // Paper - use iterator to remove
|
||||
if (leashFenceKnotEntity == null) {
|
||||
leashFenceKnotEntity = LeashFenceKnotEntity.getOrCreateKnot(level, pos);
|
||||
- leashFenceKnotEntity = LeashFenceKnotEntity.getOrCreateKnot(level, pos);
|
||||
+ // CraftBukkit start - fire HangingPlaceEvent
|
||||
+ org.bukkit.inventory.EquipmentSlot hand = org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(interactionHand);
|
||||
+ org.bukkit.event.hanging.HangingPlaceEvent event = new org.bukkit.event.hanging.HangingPlaceEvent((org.bukkit.entity.Hanging) leashFenceKnotEntity.getBukkitEntity(), player != null ? (org.bukkit.entity.Player) player.getBukkitEntity() : null, org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), org.bukkit.block.BlockFace.SELF, hand);
|
||||
+ level.getCraftServer().getPluginManager().callEvent(event);
|
||||
+ org.apache.commons.lang3.mutable.MutableBoolean created = new org.apache.commons.lang3.mutable.MutableBoolean(false);
|
||||
+ leashFenceKnotEntity = LeashFenceKnotEntity.getOrCreateKnot(level, pos, created);
|
||||
+ if (created.booleanValue()) {
|
||||
+ org.bukkit.inventory.EquipmentSlot hand = org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(interactionHand);
|
||||
+ org.bukkit.event.hanging.HangingPlaceEvent event = new org.bukkit.event.hanging.HangingPlaceEvent((org.bukkit.entity.Hanging) leashFenceKnotEntity.getBukkitEntity(), player != null ? (org.bukkit.entity.Player) player.getBukkitEntity() : null, org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), org.bukkit.block.BlockFace.SELF, hand);
|
||||
+ level.getCraftServer().getPluginManager().callEvent(event);
|
||||
+
|
||||
+ if (event.isCancelled()) {
|
||||
+ leashFenceKnotEntity.discard(null); // CraftBukkit - add Bukkit remove cause
|
||||
+ return InteractionResult.PASS;
|
||||
+ if (event.isCancelled()) {
|
||||
+ leashFenceKnotEntity.discard(null); // CraftBukkit - add Bukkit remove cause
|
||||
+ return InteractionResult.PASS;
|
||||
+ }
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
leashFenceKnotEntity.playPlacementSound();
|
||||
|
||||
+3
-4
@@ -63,12 +63,11 @@
|
||||
);
|
||||
}
|
||||
|
||||
@@ -124,7 +_,7 @@
|
||||
}
|
||||
@@ -125,6 +_,7 @@
|
||||
|
||||
public void updateDemand() {
|
||||
- this.demand = this.demand + this.uses - (this.maxUses - this.uses);
|
||||
+ this.demand = Math.max(0, this.demand + this.uses - (this.maxUses - this.uses)); // Paper - Fix MC-163962
|
||||
this.demand = this.demand + this.uses - (this.maxUses - this.uses);
|
||||
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().misc.preventNegativeVillagerDemand) this.demand = Math.max(0, this.demand); // Paper - Fix MC-163962
|
||||
}
|
||||
|
||||
public ItemStack assemble() {
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@
|
||||
- builder.withParameter(LootContextParams.EXPLOSION_RADIUS, explosion.radius());
|
||||
+ // CraftBukkit start - add yield
|
||||
+ if (explosion instanceof net.minecraft.world.level.ServerExplosion serverExplosion && serverExplosion.yield < 1.0F) {
|
||||
+ builder.withParameter(LootContextParams.EXPLOSION_RADIUS, serverExplosion.yield == 0 ? 0 : 1.0F / serverExplosion.yield);
|
||||
+ builder.withParameter(LootContextParams.EXPLOSION_RADIUS, 1.0F / serverExplosion.yield);
|
||||
+ // CraftBukkit end
|
||||
}
|
||||
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
--- a/net/minecraft/world/level/entity/EntitySection.java
|
||||
+++ b/net/minecraft/world/level/entity/EntitySection.java
|
||||
@@ -19,6 +_,12 @@
|
||||
this.storage = new ClassInstanceMultiMap<>(entityClazz);
|
||||
}
|
||||
|
||||
+ // Paper start - support retrieving all entities, regardless of whether they are accessible
|
||||
+ public void getEntities(java.util.List<T> into) {
|
||||
+ into.addAll(this.storage);
|
||||
+ }
|
||||
+ // Paper end - support retrieving all entities, regardless of whether they are accessible
|
||||
+
|
||||
public void add(T entity) {
|
||||
this.storage.add(entity);
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
--- a/net/minecraft/world/level/entity/EntitySectionStorage.java
|
||||
+++ b/net/minecraft/world/level/entity/EntitySectionStorage.java
|
||||
@@ -34,6 +_,16 @@
|
||||
this.intialSectionVisibility = initialSectionVisibility;
|
||||
}
|
||||
|
||||
+ // Paper start - support retrieving all entities, regardless of whether they are accessible
|
||||
+ public Iterable<T> getAllEntities() {
|
||||
+ java.util.List<T> ret = new java.util.ArrayList<>();
|
||||
+ for (EntitySection<T> section : this.sections.values()) {
|
||||
+ section.getEntities(ret);
|
||||
+ }
|
||||
+ return ret;
|
||||
+ }
|
||||
+ // Paper end - support retrieving all entities, regardless of whether they are accessible
|
||||
+
|
||||
public void forEachAccessibleNonEmptySection(AABB boundingBox, AbortableIterationConsumer<EntitySection<T>> consumer) {
|
||||
int sectionPosCoord = SectionPos.posToSectionCoord(boundingBox.minX - 2.0);
|
||||
int sectionPosCoord1 = SectionPos.posToSectionCoord(boundingBox.minY - 4.0);
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/world/level/entity/LevelEntityGetterAdapter.java
|
||||
+++ b/net/minecraft/world/level/entity/LevelEntityGetterAdapter.java
|
||||
@@ -8,7 +_,7 @@
|
||||
|
||||
public class LevelEntityGetterAdapter<T extends EntityAccess> implements LevelEntityGetter<T> {
|
||||
private final EntityLookup<T> visibleEntities;
|
||||
- private final EntitySectionStorage<T> sectionStorage;
|
||||
+ public final EntitySectionStorage<T> sectionStorage; // Paper - public
|
||||
|
||||
public LevelEntityGetterAdapter(EntityLookup<T> visibleEntities, EntitySectionStorage<T> sectionStorage) {
|
||||
this.visibleEntities = visibleEntities;
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.papermc.paper.adventure;
|
||||
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.DataResult;
|
||||
@@ -14,6 +15,7 @@ import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.nbt.api.BinaryTagHolder;
|
||||
import net.kyori.adventure.text.BlockNBTComponent;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.EntityNBTComponent;
|
||||
@@ -37,6 +39,9 @@ import net.kyori.adventure.text.format.TextDecoration;
|
||||
import net.minecraft.commands.arguments.selector.SelectorPattern;
|
||||
import net.minecraft.core.UUIDUtil;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.nbt.TagParser;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.chat.ComponentSerialization;
|
||||
import net.minecraft.network.chat.contents.KeybindContents;
|
||||
@@ -44,6 +49,7 @@ import net.minecraft.network.chat.contents.ScoreContents;
|
||||
import net.minecraft.network.chat.contents.TranslatableContents;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.ExtraCodecs;
|
||||
import net.minecraft.util.StringRepresentable;
|
||||
import net.minecraft.world.item.Item;
|
||||
@@ -63,7 +69,14 @@ import static net.kyori.adventure.text.TranslationArgument.numeric;
|
||||
|
||||
@DefaultQualifier(NonNull.class)
|
||||
public final class AdventureCodecs {
|
||||
|
||||
public static final Codec<BinaryTagHolder> BINARY_TAG_HOLDER_CODEC = ExtraCodecs.NBT.flatComapMap(tag -> BinaryTagHolder.encode(tag, PaperAdventure.NBT_CODEC), api -> {
|
||||
try {
|
||||
final Tag tag = api.get(PaperAdventure.NBT_CODEC);
|
||||
return DataResult.success(tag);
|
||||
} catch (CommandSyntaxException e) {
|
||||
return DataResult.error(e::getMessage);
|
||||
}
|
||||
});
|
||||
public static final Codec<Component> COMPONENT_CODEC = recursive("adventure Component", AdventureCodecs::createCodec);
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, Component> STREAM_COMPONENT_CODEC = ByteBufCodecs.fromCodecWithRegistriesTrusted(COMPONENT_CODEC);
|
||||
|
||||
@@ -89,27 +102,37 @@ public final class AdventureCodecs {
|
||||
return Key.parseable(s) ? DataResult.success(Key.key(s)) : DataResult.error(() -> "Cannot convert " + s + " to adventure Key");
|
||||
}, Key::asString);
|
||||
|
||||
static final Function<ClickEvent, String> TEXT_PAYLOAD_EXTRACTOR = a -> ((ClickEvent.Payload.Text) a.payload()).value();
|
||||
|
||||
/*
|
||||
* Click
|
||||
*/
|
||||
static final MapCodec<ClickEvent> OPEN_URL_CODEC = mapCodec((instance) -> instance.group(
|
||||
ExtraCodecs.UNTRUSTED_URI.fieldOf("url").forGetter(a -> URI.create(!a.value().contains("://") ? "https://" + a.value() : a.value()))
|
||||
ExtraCodecs.UNTRUSTED_URI.fieldOf("url").forGetter(a -> {
|
||||
final String url = ((ClickEvent.Payload.Text) a.payload()).value();
|
||||
return URI.create(!url.contains("://") ? "https://" + url : url);
|
||||
}
|
||||
)
|
||||
).apply(instance, (url) -> ClickEvent.openUrl(url.toString())));
|
||||
static final MapCodec<ClickEvent> OPEN_FILE_CODEC = mapCodec((instance) -> instance.group(
|
||||
Codec.STRING.fieldOf("path").forGetter(ClickEvent::value)
|
||||
Codec.STRING.fieldOf("path").forGetter(TEXT_PAYLOAD_EXTRACTOR)
|
||||
).apply(instance, ClickEvent::openFile));
|
||||
static final MapCodec<ClickEvent> RUN_COMMAND_CODEC = mapCodec((instance) -> instance.group(
|
||||
ExtraCodecs.CHAT_STRING.fieldOf("command").forGetter(ClickEvent::value)
|
||||
ExtraCodecs.CHAT_STRING.fieldOf("command").forGetter(TEXT_PAYLOAD_EXTRACTOR)
|
||||
).apply(instance, ClickEvent::runCommand));
|
||||
static final MapCodec<ClickEvent> SUGGEST_COMMAND_CODEC = mapCodec((instance) -> instance.group(
|
||||
ExtraCodecs.CHAT_STRING.fieldOf("command").forGetter(ClickEvent::value)
|
||||
ExtraCodecs.CHAT_STRING.fieldOf("command").forGetter(TEXT_PAYLOAD_EXTRACTOR)
|
||||
).apply(instance, ClickEvent::suggestCommand));
|
||||
static final MapCodec<ClickEvent> CHANGE_PAGE_CODEC = mapCodec((instance) -> instance.group(
|
||||
ExtraCodecs.POSITIVE_INT.fieldOf("page").forGetter(a -> Integer.parseInt(a.value()))
|
||||
ExtraCodecs.POSITIVE_INT.fieldOf("page").forGetter(a -> ((ClickEvent.Payload.Int) a.payload()).integer())
|
||||
).apply(instance, ClickEvent::changePage));
|
||||
static final MapCodec<ClickEvent> COPY_TO_CLIPBOARD_CODEC = mapCodec((instance) -> instance.group(
|
||||
Codec.STRING.fieldOf("value").forGetter(ClickEvent::value)
|
||||
Codec.STRING.fieldOf("value").forGetter(TEXT_PAYLOAD_EXTRACTOR)
|
||||
).apply(instance, ClickEvent::copyToClipboard));
|
||||
static final MapCodec<ClickEvent> CUSTOM_CODEC = mapCodec((instance) -> instance.group(
|
||||
KEY_CODEC.fieldOf("id").forGetter(a -> ((ClickEvent.Payload.Custom) a.payload()).key()),
|
||||
BINARY_TAG_HOLDER_CODEC.fieldOf("payload").forGetter(a -> ((ClickEvent.Payload.Custom) a.payload()).nbt())
|
||||
).apply(instance, ClickEvent::custom));
|
||||
|
||||
static final ClickEventType OPEN_URL_CLICK_EVENT_TYPE = new ClickEventType(OPEN_URL_CODEC, "open_url");
|
||||
static final ClickEventType OPEN_FILE_CLICK_EVENT_TYPE = new ClickEventType(OPEN_FILE_CODEC, "open_file");
|
||||
@@ -117,7 +140,8 @@ public final class AdventureCodecs {
|
||||
static final ClickEventType SUGGEST_COMMAND_CLICK_EVENT_TYPE = new ClickEventType(SUGGEST_COMMAND_CODEC, "suggest_command");
|
||||
static final ClickEventType CHANGE_PAGE_CLICK_EVENT_TYPE = new ClickEventType(CHANGE_PAGE_CODEC, "change_page");
|
||||
static final ClickEventType COPY_TO_CLIPBOARD_CLICK_EVENT_TYPE = new ClickEventType(COPY_TO_CLIPBOARD_CODEC, "copy_to_clipboard");
|
||||
static final Codec<ClickEventType> CLICK_EVENT_TYPE_CODEC = StringRepresentable.fromValues(() -> new ClickEventType[]{OPEN_URL_CLICK_EVENT_TYPE, OPEN_FILE_CLICK_EVENT_TYPE, RUN_COMMAND_CLICK_EVENT_TYPE, SUGGEST_COMMAND_CLICK_EVENT_TYPE, CHANGE_PAGE_CLICK_EVENT_TYPE, COPY_TO_CLIPBOARD_CLICK_EVENT_TYPE});
|
||||
static final ClickEventType CUSTOM_CLICK_EVENT_TYPE = new ClickEventType(CUSTOM_CODEC, "custom");
|
||||
static final Codec<ClickEventType> CLICK_EVENT_TYPE_CODEC = StringRepresentable.fromValues(() -> new ClickEventType[]{OPEN_URL_CLICK_EVENT_TYPE, OPEN_FILE_CLICK_EVENT_TYPE, RUN_COMMAND_CLICK_EVENT_TYPE, SUGGEST_COMMAND_CLICK_EVENT_TYPE, CHANGE_PAGE_CLICK_EVENT_TYPE, COPY_TO_CLIPBOARD_CLICK_EVENT_TYPE, CUSTOM_CLICK_EVENT_TYPE});
|
||||
|
||||
record ClickEventType(MapCodec<ClickEvent> codec, String id) implements StringRepresentable {
|
||||
@Override
|
||||
@@ -126,23 +150,17 @@ public final class AdventureCodecs {
|
||||
}
|
||||
}
|
||||
|
||||
private static final Function<ClickEvent, ClickEventType> GET_CLICK_EVENT_TYPE = he -> {
|
||||
if (he.action() == ClickEvent.Action.OPEN_URL) {
|
||||
return OPEN_URL_CLICK_EVENT_TYPE;
|
||||
} else if (he.action() == ClickEvent.Action.OPEN_FILE) {
|
||||
return OPEN_FILE_CLICK_EVENT_TYPE;
|
||||
} else if (he.action() == ClickEvent.Action.RUN_COMMAND) {
|
||||
return RUN_COMMAND_CLICK_EVENT_TYPE;
|
||||
} else if (he.action() == ClickEvent.Action.SUGGEST_COMMAND) {
|
||||
return SUGGEST_COMMAND_CLICK_EVENT_TYPE;
|
||||
} else if (he.action() == ClickEvent.Action.CHANGE_PAGE) {
|
||||
return CHANGE_PAGE_CLICK_EVENT_TYPE;
|
||||
} else if (he.action() == ClickEvent.Action.COPY_TO_CLIPBOARD) {
|
||||
return COPY_TO_CLIPBOARD_CLICK_EVENT_TYPE;
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
};
|
||||
private static final Function<ClickEvent, ClickEventType> GET_CLICK_EVENT_TYPE =
|
||||
he -> switch (he.action()) {
|
||||
case OPEN_URL -> OPEN_URL_CLICK_EVENT_TYPE;
|
||||
case OPEN_FILE -> OPEN_FILE_CLICK_EVENT_TYPE;
|
||||
case RUN_COMMAND -> RUN_COMMAND_CLICK_EVENT_TYPE;
|
||||
case SUGGEST_COMMAND -> SUGGEST_COMMAND_CLICK_EVENT_TYPE;
|
||||
case CHANGE_PAGE -> CHANGE_PAGE_CLICK_EVENT_TYPE;
|
||||
case COPY_TO_CLIPBOARD -> COPY_TO_CLIPBOARD_CLICK_EVENT_TYPE;
|
||||
case SHOW_DIALOG -> throw new UnsupportedOperationException(); // todo: dialog codec with dialog "api"
|
||||
case CUSTOM -> CUSTOM_CLICK_EVENT_TYPE;
|
||||
};
|
||||
|
||||
static final Codec<ClickEvent> CLICK_EVENT_CODEC = CLICK_EVENT_TYPE_CODEC.dispatch("action", GET_CLICK_EVENT_TYPE, ClickEventType::codec);
|
||||
|
||||
|
||||
@@ -35,8 +35,6 @@ import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import net.kyori.adventure.translation.GlobalTranslator;
|
||||
import net.kyori.adventure.translation.TranslationRegistry;
|
||||
import net.kyori.adventure.translation.Translator;
|
||||
import net.kyori.adventure.util.Codec;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
@@ -78,16 +76,16 @@ import static java.util.Objects.requireNonNull;
|
||||
public final class PaperAdventure {
|
||||
private static final Pattern LOCALIZATION_PATTERN = Pattern.compile("%(?:(\\d+)\\$)?s");
|
||||
public static final ComponentFlattener FLATTENER = ComponentFlattener.basic().toBuilder()
|
||||
.nestingLimit(30) // todo: should this be configurable? a system property or config value?
|
||||
.complexMapper(TranslatableComponent.class, (translatable, consumer) -> {
|
||||
if (!Language.getInstance().has(translatable.key())) {
|
||||
for (final Translator source : GlobalTranslator.translator().sources()) {
|
||||
if (source instanceof TranslationRegistry registry && registry.contains(translatable.key())) {
|
||||
consumer.accept(GlobalTranslator.render(translatable, Locale.US));
|
||||
return;
|
||||
}
|
||||
final Language language = Language.getInstance();
|
||||
final @Nullable String fallback = translatable.fallback();
|
||||
if (!language.has(translatable.key()) && (fallback == null || !language.has(fallback))) {
|
||||
if (GlobalTranslator.translator().canTranslate(translatable.key(), Locale.US)) {
|
||||
consumer.accept(GlobalTranslator.render(translatable, Locale.US));
|
||||
return;
|
||||
}
|
||||
}
|
||||
final @Nullable String fallback = translatable.fallback();
|
||||
final @NotNull String translated = Language.getInstance().getOrDefault(translatable.key(), fallback != null ? fallback : translatable.key());
|
||||
|
||||
final Matcher matcher = LOCALIZATION_PATTERN.matcher(translated);
|
||||
@@ -130,10 +128,11 @@ public final class PaperAdventure {
|
||||
@Deprecated
|
||||
public static final PlainComponentSerializer PLAIN = PlainComponentSerializer.builder().flattener(FLATTENER).build();
|
||||
public static final ANSIComponentSerializer ANSI_SERIALIZER = ANSIComponentSerializer.builder().flattener(FLATTENER).build();
|
||||
private static final TagParser<Tag> NBT_PARSER = TagParser.create(NbtOps.INSTANCE);
|
||||
public static final Codec<Tag, String, CommandSyntaxException, RuntimeException> NBT_CODEC = new Codec<>() {
|
||||
@Override
|
||||
public @NotNull Tag decode(final @NotNull String encoded) throws CommandSyntaxException {
|
||||
return TagParser.parseCompoundFully(encoded);
|
||||
return NBT_PARSER.parseFully(encoded);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -379,6 +378,7 @@ public final class PaperAdventure {
|
||||
case PLAYER -> SoundSource.PLAYERS;
|
||||
case AMBIENT -> SoundSource.AMBIENT;
|
||||
case VOICE -> SoundSource.VOICE;
|
||||
case UI -> SoundSource.UI;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+28
-10
@@ -1,24 +1,33 @@
|
||||
package io.papermc.paper.adventure.providers;
|
||||
|
||||
import io.papermc.paper.adventure.PaperAdventure;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.text.event.ClickCallback;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage") // permitted provider
|
||||
public class ClickCallbackProviderImpl implements ClickCallback.Provider {
|
||||
private static final Key CLICK_CALLBACK_KEY = Key.key("paper", "click_callback");
|
||||
private static final String ID_KEY = "id";
|
||||
|
||||
public static final ResourceLocation CLICK_CALLBACK_RESOURCE_LOCATION = PaperAdventure.asVanilla(CLICK_CALLBACK_KEY);
|
||||
public static final CallbackManager CALLBACK_MANAGER = new CallbackManager();
|
||||
|
||||
@Override
|
||||
public @NotNull ClickEvent create(final @NotNull ClickCallback<Audience> callback, final ClickCallback.@NotNull Options options) {
|
||||
return ClickEvent.runCommand("/paper:callback " + CALLBACK_MANAGER.addCallback(callback, options));
|
||||
final CompoundTag tag = new CompoundTag();
|
||||
tag.putString(ID_KEY, CALLBACK_MANAGER.addCallback(callback, options).toString());
|
||||
return ClickEvent.custom(CLICK_CALLBACK_KEY, PaperAdventure.asBinaryTagHolder(tag));
|
||||
}
|
||||
|
||||
public static final class CallbackManager {
|
||||
@@ -48,12 +57,21 @@ public class ClickCallbackProviderImpl implements ClickCallback.Provider {
|
||||
}
|
||||
}
|
||||
|
||||
public void runCallback(final @NotNull Audience audience, final UUID id) {
|
||||
final StoredCallback callback = this.callbacks.get(id);
|
||||
if (callback != null && callback.valid()) { //TODO Message if expired/invalid?
|
||||
callback.takeUse();
|
||||
callback.callback.accept(audience);
|
||||
}
|
||||
public void tryRunCallback(final @NotNull Audience audience, final Tag tag) {
|
||||
tag.asCompound().flatMap(t -> t.getString(ID_KEY)).ifPresent(s -> {
|
||||
final UUID id;
|
||||
try {
|
||||
id = UUID.fromString(s);
|
||||
} catch (final IllegalArgumentException ignored) {
|
||||
return;
|
||||
}
|
||||
|
||||
final StoredCallback callback = this.callbacks.get(id);
|
||||
if (callback != null && callback.valid()) {
|
||||
callback.takeUse();
|
||||
callback.callback.accept(audience);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
package io.papermc.paper.command;
|
||||
|
||||
import io.papermc.paper.adventure.providers.ClickCallbackProviderImpl;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
import java.util.UUID;
|
||||
|
||||
@DefaultQualifier(NonNull.class)
|
||||
public class CallbackCommand extends Command {
|
||||
|
||||
protected CallbackCommand(final String name) {
|
||||
super(name);
|
||||
this.description = "ClickEvent callback";
|
||||
this.usageMessage = "/callback <uuid>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(final CommandSender sender, final String commandLabel, final String[] args) {
|
||||
if (args.length != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final UUID id;
|
||||
try {
|
||||
id = UUID.fromString(args[0]);
|
||||
} catch (final IllegalArgumentException ignored) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ClickCallbackProviderImpl.CALLBACK_MANAGER.runCallback(sender, id);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@ public final class PaperCommands {
|
||||
|
||||
public static void registerCommands(final MinecraftServer server) {
|
||||
COMMANDS.put("paper", new PaperCommand("paper"));
|
||||
COMMANDS.put("callback", new CallbackCommand("callback"));
|
||||
COMMANDS.put("mspt", new MSPTCommand("mspt"));
|
||||
|
||||
COMMANDS.forEach((s, command) -> {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.papermc.paper.command.subcommands;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import io.papermc.paper.FeatureHooks;
|
||||
import io.papermc.paper.command.CommandUtil;
|
||||
import io.papermc.paper.command.PaperSubcommand;
|
||||
import java.util.Collections;
|
||||
@@ -9,7 +10,6 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
@@ -102,7 +102,7 @@ public final class EntityCommand implements PaperSubcommand {
|
||||
ServerLevel world = ((CraftWorld) bukkitWorld).getHandle();
|
||||
Map<ResourceLocation, Integer> nonEntityTicking = Maps.newHashMap();
|
||||
ServerChunkCache chunkProviderServer = world.getChunkSource();
|
||||
world.getAllEntities().forEach(e -> {
|
||||
FeatureHooks.getAllEntities(world).forEach(e -> {
|
||||
ResourceLocation key = EntityType.getKey(e.getType());
|
||||
|
||||
MutablePair<Integer, Map<ChunkPos, Integer>> info = list.computeIfAbsent(key, k -> MutablePair.of(0, Maps.newHashMap()));
|
||||
|
||||
@@ -343,7 +343,7 @@ public class GlobalConfiguration extends ConfigurationPart {
|
||||
}
|
||||
}
|
||||
public int maxJoinsPerTick = 5;
|
||||
public boolean fixEntityPositionDesync = true;
|
||||
public boolean sendFullPosForItemEntities = false;
|
||||
public boolean loadPermissionsYmlBeforePlugins = true;
|
||||
@Constraints.Min(4)
|
||||
public int regionFileCacheSize = 256;
|
||||
@@ -357,6 +357,8 @@ public class GlobalConfiguration extends ConfigurationPart {
|
||||
@Comment("Defines how many orbs groups can exist in an area.")
|
||||
@Constraints.Min(1)
|
||||
public IntOr.Default xpOrbGroupsPerArea = IntOr.Default.USE_DEFAULT;
|
||||
@Comment("See Fix MC-163962; prevent villager demand from going negative.")
|
||||
public boolean preventNegativeVillagerDemand = false;
|
||||
}
|
||||
|
||||
public BlockUpdates blockUpdates;
|
||||
|
||||
@@ -82,6 +82,7 @@ interface RemovedConfigurations {
|
||||
path("unsupported-settings", "allow-grindstone-overstacking"),
|
||||
path("unsupported-settings", "allow-tripwire-disarming-exploits"),
|
||||
path("commands", "fix-target-selector-tag-completion"),
|
||||
path("misc", "fix-entity-position-desync")
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.papermc.paper.threadedregions;
|
||||
|
||||
import ca.spottedleaf.concurrentutil.util.Validate;
|
||||
import ca.spottedleaf.moonrise.common.list.ReferenceList;
|
||||
import ca.spottedleaf.moonrise.common.util.TickThread;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
@@ -9,6 +10,8 @@ import org.bukkit.craftbukkit.entity.CraftEntity;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.locks.StampedLock;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
@@ -43,6 +46,8 @@ public final class EntityScheduler {
|
||||
private static final long RETIRED_TICK_COUNT = -1L;
|
||||
private final Object stateLock = new Object();
|
||||
private final Long2ObjectOpenHashMap<List<ScheduledTask>> oneTimeDelayed = new Long2ObjectOpenHashMap<>();
|
||||
private EntitySchedulerTickList scheduledList;
|
||||
private boolean insideScheduledList;
|
||||
|
||||
private final ArrayDeque<ScheduledTask> currentlyExecuting = new ArrayDeque<>();
|
||||
|
||||
@@ -50,6 +55,51 @@ public final class EntityScheduler {
|
||||
this.entity = Validate.notNull(entity);
|
||||
}
|
||||
|
||||
// must own state lock
|
||||
private boolean hasTasks() {
|
||||
return !this.currentlyExecuting.isEmpty() || !this.oneTimeDelayed.isEmpty();
|
||||
}
|
||||
|
||||
public void registerTo(final EntitySchedulerTickList newTickList) {
|
||||
synchronized (this.stateLock) {
|
||||
final EntitySchedulerTickList prevList = this.scheduledList;
|
||||
if (prevList == newTickList) {
|
||||
return;
|
||||
}
|
||||
this.scheduledList = newTickList;
|
||||
|
||||
// make sure tasks scheduled before registration can be ticked
|
||||
if (prevList == null && this.hasTasks()) {
|
||||
this.insideScheduledList = true;
|
||||
}
|
||||
|
||||
// transfer to new list
|
||||
if (this.insideScheduledList) {
|
||||
if (prevList != null) {
|
||||
prevList.remove(this);
|
||||
}
|
||||
if (newTickList != null) {
|
||||
newTickList.add(this);
|
||||
} else {
|
||||
// retired
|
||||
this.insideScheduledList = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this scheduler is retired.
|
||||
*
|
||||
* <p>
|
||||
* Note: This should only be invoked on the owning thread for the entity.
|
||||
* </p>
|
||||
* @return whether this scheduler is retired.
|
||||
*/
|
||||
public boolean isRetired() {
|
||||
return this.tickCount == RETIRED_TICK_COUNT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retires the scheduler, preventing new tasks from being scheduled and invoking the retired callback
|
||||
* on all currently scheduled tasks.
|
||||
@@ -66,6 +116,7 @@ public final class EntityScheduler {
|
||||
throw new IllegalStateException("Already retired");
|
||||
}
|
||||
this.tickCount = RETIRED_TICK_COUNT;
|
||||
this.registerTo(null);
|
||||
}
|
||||
|
||||
final Entity thisEntity = this.entity.getHandleRaw();
|
||||
@@ -127,6 +178,11 @@ public final class EntityScheduler {
|
||||
this.oneTimeDelayed.computeIfAbsent(this.tickCount + Math.max(1L, delay), (final long keyInMap) -> {
|
||||
return new ArrayList<>();
|
||||
}).add(task);
|
||||
|
||||
if (!this.insideScheduledList && this.scheduledList != null) {
|
||||
this.scheduledList.add(this);
|
||||
this.insideScheduledList = true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -147,6 +203,12 @@ public final class EntityScheduler {
|
||||
throw new IllegalStateException("Ticking retired scheduler");
|
||||
}
|
||||
++this.tickCount;
|
||||
|
||||
if (this.scheduledList != null && !this.hasTasks()) {
|
||||
this.scheduledList.remove(this);
|
||||
this.insideScheduledList = false;
|
||||
}
|
||||
|
||||
if (this.oneTimeDelayed.isEmpty()) {
|
||||
toRun = null;
|
||||
} else {
|
||||
@@ -178,4 +240,34 @@ public final class EntityScheduler {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static final class EntitySchedulerTickList {
|
||||
|
||||
private static final EntityScheduler[] ENTITY_SCHEDULER_ARRAY = new EntityScheduler[0];
|
||||
|
||||
private final ReferenceList<EntityScheduler> entitySchedulers = new ReferenceList<>(ENTITY_SCHEDULER_ARRAY);
|
||||
|
||||
public boolean add(final EntityScheduler scheduler) {
|
||||
synchronized (this) {
|
||||
return this.entitySchedulers.add(scheduler);
|
||||
}
|
||||
}
|
||||
|
||||
public void remove(final EntityScheduler scheduler) {
|
||||
synchronized (this) {
|
||||
this.entitySchedulers.remove(scheduler);
|
||||
}
|
||||
}
|
||||
|
||||
public EntityScheduler[] getAllSchedulers() {
|
||||
EntityScheduler[] ret = new EntityScheduler[this.entitySchedulers.size()];
|
||||
synchronized (this) {
|
||||
if (ret.length != this.entitySchedulers.size()) {
|
||||
ret = new EntityScheduler[this.entitySchedulers.size()];
|
||||
}
|
||||
System.arraycopy(this.entitySchedulers.getRawDataUnchecked(), 0, ret, 0, this.entitySchedulers.size());
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import net.kyori.adventure.pointer.PointersSupplier;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.HolderSet;
|
||||
@@ -157,6 +158,10 @@ import org.jetbrains.annotations.Nullable;
|
||||
public class CraftWorld extends CraftRegionAccessor implements World {
|
||||
public static final int CUSTOM_DIMENSION_OFFSET = 10;
|
||||
private static final CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new CraftPersistentDataTypeRegistry();
|
||||
private static final PointersSupplier<World> POINTERS_SUPPLIER = PointersSupplier.<World>builder()
|
||||
.resolving(net.kyori.adventure.identity.Identity.NAME, World::getName)
|
||||
.resolving(net.kyori.adventure.identity.Identity.UUID, World::getUID)
|
||||
.build();
|
||||
|
||||
private final ServerLevel world;
|
||||
private WorldBorder worldBorder;
|
||||
@@ -168,7 +173,6 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
||||
private final BlockMetadataStore blockMetadata = new BlockMetadataStore(this);
|
||||
private final Object2IntOpenHashMap<SpawnCategory> spawnCategoryLimit = new Object2IntOpenHashMap<>();
|
||||
private final CraftPersistentDataContainer persistentDataContainer = new CraftPersistentDataContainer(CraftWorld.DATA_TYPE_REGISTRY);
|
||||
private net.kyori.adventure.pointer.Pointers adventure$pointers; // Paper - implement pointers
|
||||
// Paper start - void damage configuration
|
||||
private boolean voidDamageEnabled;
|
||||
private float voidDamageAmount;
|
||||
@@ -2481,14 +2485,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
||||
// Paper start - implement pointers
|
||||
@Override
|
||||
public net.kyori.adventure.pointer.Pointers pointers() {
|
||||
if (this.adventure$pointers == null) {
|
||||
this.adventure$pointers = net.kyori.adventure.pointer.Pointers.builder()
|
||||
.withDynamic(net.kyori.adventure.identity.Identity.NAME, this::getName)
|
||||
.withDynamic(net.kyori.adventure.identity.Identity.UUID, this::getUID)
|
||||
.build();
|
||||
}
|
||||
|
||||
return this.adventure$pointers;
|
||||
return POINTERS_SUPPLIER.view(this);
|
||||
}
|
||||
// Paper end
|
||||
}
|
||||
@@ -74,7 +74,7 @@ public abstract class CraftBlockEntityState<T extends BlockEntity> extends Craft
|
||||
this.loadData(state.getSnapshotNBT());
|
||||
}
|
||||
|
||||
private RegistryAccess getRegistryAccess() {
|
||||
public RegistryAccess getRegistryAccess() {
|
||||
LevelAccessor worldHandle = this.getWorldHandle();
|
||||
return (worldHandle != null) ? worldHandle.registryAccess() : CraftRegistry.getMinecraftRegistry();
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ public class CraftCreatureSpawner extends CraftBlockEntityState<SpawnerBlockEnti
|
||||
}
|
||||
|
||||
try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(() -> "spawner@" + getLocation(), LOGGER)) {
|
||||
ValueInput valueInput = TagValueInput.create(scopedCollector, this.getInternalWorld().registryAccess(), spawnData.entityToSpawn());
|
||||
ValueInput valueInput = TagValueInput.create(scopedCollector, this.getRegistryAccess(), spawnData.entityToSpawn());
|
||||
Optional<net.minecraft.world.entity.EntityType<?>> type = net.minecraft.world.entity.EntityType.by(valueInput);
|
||||
return type.map(CraftEntityType::minecraftToBukkit).orElse(null);
|
||||
}
|
||||
@@ -182,7 +182,7 @@ public class CraftCreatureSpawner extends CraftBlockEntityState<SpawnerBlockEnti
|
||||
return null;
|
||||
}
|
||||
try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(() -> "spawner@" + getLocation(), LOGGER)) {
|
||||
ValueInput valueInput = TagValueInput.create(scopedCollector, this.getInternalWorld().registryAccess(), spawnData.getEntityToSpawn());
|
||||
ValueInput valueInput = TagValueInput.create(scopedCollector, this.getRegistryAccess(), spawnData.getEntityToSpawn());
|
||||
Optional<net.minecraft.world.entity.EntityType<?>> type = net.minecraft.world.entity.EntityType.by(valueInput);
|
||||
|
||||
return type.map(CraftEntityType::minecraftToBukkit).map(CraftEntityType::bukkitToString).orElse(null);
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.bukkit.craftbukkit.command;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import net.kyori.adventure.pointer.PointersSupplier;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.command.CommandSender;
|
||||
@@ -12,8 +13,12 @@ import org.bukkit.permissions.PermissionAttachmentInfo;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public abstract class ServerCommandSender implements CommandSender {
|
||||
private static final PointersSupplier<ServerCommandSender> POINTERS_SUPPLIER = PointersSupplier.<ServerCommandSender>builder()
|
||||
.resolving(net.kyori.adventure.identity.Identity.DISPLAY_NAME, ServerCommandSender::name)
|
||||
.resolving(net.kyori.adventure.permission.PermissionChecker.POINTER, serverCommandSender -> serverCommandSender::permissionValue)
|
||||
.build();
|
||||
|
||||
public final PermissibleBase perm;
|
||||
private net.kyori.adventure.pointer.Pointers adventure$pointers;
|
||||
|
||||
protected ServerCommandSender() {
|
||||
this.perm = new PermissibleBase(this);
|
||||
@@ -126,13 +131,6 @@ public abstract class ServerCommandSender implements CommandSender {
|
||||
|
||||
@Override
|
||||
public net.kyori.adventure.pointer.Pointers pointers() {
|
||||
if (this.adventure$pointers == null) {
|
||||
this.adventure$pointers = net.kyori.adventure.pointer.Pointers.builder()
|
||||
.withDynamic(net.kyori.adventure.identity.Identity.DISPLAY_NAME, this::name)
|
||||
.withStatic(net.kyori.adventure.permission.PermissionChecker.POINTER, this::permissionValue)
|
||||
.build();
|
||||
}
|
||||
|
||||
return this.adventure$pointers;
|
||||
return POINTERS_SUPPLIER.view(this);
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import io.papermc.paper.entity.LookAnchor;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import net.kyori.adventure.pointer.PointersSupplier;
|
||||
import net.kyori.adventure.util.TriState;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
@@ -85,13 +86,17 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
||||
|
||||
private static PermissibleBase perm;
|
||||
private static final CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new CraftPersistentDataTypeRegistry();
|
||||
static final PointersSupplier<org.bukkit.entity.Entity> POINTERS_SUPPLIER = PointersSupplier.<org.bukkit.entity.Entity>builder()
|
||||
.resolving(net.kyori.adventure.identity.Identity.DISPLAY_NAME, org.bukkit.entity.Entity::name)
|
||||
.resolving(net.kyori.adventure.identity.Identity.UUID, org.bukkit.entity.Entity::getUniqueId)
|
||||
.resolving(net.kyori.adventure.permission.PermissionChecker.POINTER, entity1 -> entity1::permissionValue)
|
||||
.build();
|
||||
|
||||
protected final CraftServer server;
|
||||
protected Entity entity;
|
||||
private final EntityType entityType;
|
||||
private EntityDamageEvent lastDamageEvent;
|
||||
private final CraftPersistentDataContainer persistentDataContainer = new CraftPersistentDataContainer(CraftEntity.DATA_TYPE_REGISTRY);
|
||||
protected net.kyori.adventure.pointer.Pointers adventure$pointers; // Paper - implement pointers
|
||||
// Paper start - Folia shedulers
|
||||
public final io.papermc.paper.threadedregions.EntityScheduler taskScheduler = new io.papermc.paper.threadedregions.EntityScheduler(this);
|
||||
private final io.papermc.paper.threadedregions.scheduler.FoliaEntityScheduler apiScheduler = new io.papermc.paper.threadedregions.scheduler.FoliaEntityScheduler(this);
|
||||
@@ -200,7 +205,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
||||
}
|
||||
// Paper end
|
||||
this.entity.setDeltaMovement(CraftVector.toVec3(velocity));
|
||||
this.entity.hasImpulse = true;
|
||||
this.entity.hurtMarked = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -670,15 +675,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
||||
|
||||
@Override
|
||||
public net.kyori.adventure.pointer.Pointers pointers() {
|
||||
if (this.adventure$pointers == null) {
|
||||
this.adventure$pointers = net.kyori.adventure.pointer.Pointers.builder()
|
||||
.withDynamic(net.kyori.adventure.identity.Identity.DISPLAY_NAME, this::name)
|
||||
.withDynamic(net.kyori.adventure.identity.Identity.UUID, this::getUniqueId)
|
||||
.withStatic(net.kyori.adventure.permission.PermissionChecker.POINTER, this::permissionValue)
|
||||
.build();
|
||||
}
|
||||
|
||||
return this.adventure$pointers;
|
||||
return POINTERS_SUPPLIER.view(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -62,10 +62,9 @@ public class CraftFireball extends AbstractProjectile implements Fireball {
|
||||
public void setAcceleration(@NotNull Vector acceleration) {
|
||||
Preconditions.checkArgument(acceleration != null, "Vector acceleration cannot be null");
|
||||
// SPIGOT-6993: AbstractHurtingProjectile#assignDirectionalMovement will normalize the given values
|
||||
// Set the acceleration power in order to properly sync the movement to the client
|
||||
double length = acceleration.length();
|
||||
this.getHandle().accelerationPower = length;
|
||||
this.getHandle().assignDirectionalMovement(CraftVector.toVec3(acceleration), length);
|
||||
// Note: Because of MC-80142 the fireball will stutter on the client when setting the power to something other than 0 or the normalized vector * 0.1
|
||||
this.getHandle().assignDirectionalMovement(CraftVector.toVec3(acceleration), acceleration.length());
|
||||
this.update(); // SPIGOT-6579
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -22,6 +22,35 @@ public class CraftItemFrame extends CraftHanging implements ItemFrame {
|
||||
return (net.minecraft.world.entity.decoration.ItemFrame) this.entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setFacingDirection(BlockFace face, boolean force) {
|
||||
HangingEntity hanging = this.getHandle();
|
||||
Direction oldDir = hanging.getDirection();
|
||||
Direction newDir = CraftBlock.blockFaceToNotch(face);
|
||||
|
||||
Preconditions.checkArgument(newDir != null, "%s is not a valid facing direction", face);
|
||||
|
||||
this.getHandle().setDirection(newDir);
|
||||
if (!force && !this.getHandle().generation && !hanging.survives()) {
|
||||
hanging.setDirection(oldDir);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.update();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void update() {
|
||||
super.update();
|
||||
|
||||
// update redstone
|
||||
if (!this.getHandle().generation) {
|
||||
this.getHandle().level().updateNeighbourForOutputSignal(this.getHandle().getPos(), Blocks.AIR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(org.bukkit.inventory.ItemStack item) {
|
||||
this.setItem(item, true);
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.google.common.base.Preconditions;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.util.ProblemReporter;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.util.random.WeightedList;
|
||||
@@ -186,6 +187,11 @@ public class CraftMinecartMobSpawner extends CraftMinecart implements SpawnerMin
|
||||
return this.getHandle().level();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegistryAccess getRegistryAccess() {
|
||||
return this.getHandle().registryAccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public net.minecraft.core.BlockPos getInternalPosition() {
|
||||
return this.getHandle().blockPosition();
|
||||
|
||||
@@ -44,4 +44,14 @@ public class CraftPainting extends CraftHanging implements Painting {
|
||||
//this.update(); Paper - Don't resent entity on art update
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setFacingDirection(BlockFace face, boolean force) {
|
||||
if (super.setFacingDirection(face, force)) {
|
||||
this.update();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,8 @@ import java.util.concurrent.CompletableFuture;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
import net.kyori.adventure.identity.Identity;
|
||||
import net.kyori.adventure.pointer.PointersSupplier;
|
||||
import net.kyori.adventure.util.TriState;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.minecraft.advancements.AdvancementProgress;
|
||||
@@ -213,6 +215,12 @@ import org.jspecify.annotations.Nullable;
|
||||
|
||||
@DelegateDeserialization(CraftOfflinePlayer.class)
|
||||
public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
private static final PointersSupplier<Player> POINTERS_SUPPLIER = PointersSupplier.<Player>builder()
|
||||
.parent(CraftEntity.POINTERS_SUPPLIER)
|
||||
.resolving(Identity.NAME, Player::getName)
|
||||
.resolving(Identity.DISPLAY_NAME, Player::displayName)
|
||||
.resolving(Identity.LOCALE, Player::locale)
|
||||
.build();
|
||||
|
||||
private long firstPlayed = 0;
|
||||
private long lastPlayed = 0;
|
||||
@@ -3283,17 +3291,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
|
||||
@Override
|
||||
public net.kyori.adventure.pointer.Pointers pointers() {
|
||||
if (this.adventure$pointers == null) {
|
||||
this.adventure$pointers = net.kyori.adventure.pointer.Pointers.builder()
|
||||
.withDynamic(net.kyori.adventure.identity.Identity.DISPLAY_NAME, this::displayName)
|
||||
.withDynamic(net.kyori.adventure.identity.Identity.NAME, this::getName)
|
||||
.withDynamic(net.kyori.adventure.identity.Identity.UUID, this::getUniqueId)
|
||||
.withStatic(net.kyori.adventure.permission.PermissionChecker.POINTER, this::permissionValue)
|
||||
.withDynamic(net.kyori.adventure.identity.Identity.LOCALE, this::locale)
|
||||
.build();
|
||||
}
|
||||
|
||||
return this.adventure$pointers;
|
||||
return POINTERS_SUPPLIER.view(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -34,6 +34,8 @@ public interface PaperSharedSpawnerLogic extends Spawner {
|
||||
|
||||
Level getInternalWorld();
|
||||
|
||||
RegistryAccess getRegistryAccess();
|
||||
|
||||
BlockPos getInternalPosition();
|
||||
default boolean isActivated() {
|
||||
return this.getSpawner().isNearPlayer(this.getInternalWorld(), this.getInternalPosition());
|
||||
|
||||
@@ -11,11 +11,14 @@ import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.nbt.api.BinaryTagHolder;
|
||||
import net.kyori.adventure.text.BlockNBTComponent;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
@@ -106,23 +109,35 @@ class AdventureCodecsTest {
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = PARAMETERIZED_NAME)
|
||||
@EnumSource(value = ClickEvent.Action.class, mode = EnumSource.Mode.EXCLUDE, names = {"OPEN_FILE"})
|
||||
@EnumSource(value = ClickEvent.Action.class, mode = EnumSource.Mode.EXCLUDE, names = {"OPEN_FILE", "SHOW_DIALOG", "CUSTOM"})
|
||||
void testClickEvent(final ClickEvent.Action action) {
|
||||
final ClickEvent event = ClickEvent.clickEvent(action, action.name().equals("OPEN_URL") ? "https://google.com" : "1337");
|
||||
final Tag result = CLICK_EVENT_CODEC.encodeStart(NbtOps.INSTANCE, event).result().orElseThrow();
|
||||
final ClickEvent event = switch (action) {
|
||||
case OPEN_URL -> openUrl("https://google.com");
|
||||
case RUN_COMMAND -> ClickEvent.runCommand("/say hello");
|
||||
case SUGGEST_COMMAND -> suggestCommand("/suggest hello");
|
||||
case CHANGE_PAGE -> ClickEvent.changePage(2);
|
||||
case COPY_TO_CLIPBOARD -> ClickEvent.copyToClipboard("clipboard content");
|
||||
case CUSTOM -> ClickEvent.custom(key("test"), BinaryTagHolder.binaryTagHolder("3"));
|
||||
case SHOW_DIALOG, OPEN_FILE -> throw new IllegalArgumentException();
|
||||
};
|
||||
final Tag result = CLICK_EVENT_CODEC.encodeStart(NbtOps.INSTANCE, event).result().orElseThrow(() -> new RuntimeException("Failed to encode ClickEvent: " + event));
|
||||
final net.minecraft.network.chat.ClickEvent nms = net.minecraft.network.chat.ClickEvent.CODEC.decode(NbtOps.INSTANCE, result).result().orElseThrow().getFirst();
|
||||
assertEquals(event.action().toString(), nms.action().getSerializedName());
|
||||
switch (nms) {
|
||||
case net.minecraft.network.chat.ClickEvent.OpenUrl(java.net.URI uri) ->
|
||||
assertEquals(event.value(), uri.toString());
|
||||
case net.minecraft.network.chat.ClickEvent.OpenUrl(URI uri) ->
|
||||
assertEquals(((ClickEvent.Payload.Text) event.payload()).value(), uri.toString());
|
||||
case net.minecraft.network.chat.ClickEvent.SuggestCommand(String command) ->
|
||||
assertEquals(event.value(), command);
|
||||
assertEquals(((ClickEvent.Payload.Text) event.payload()).value(), command);
|
||||
case net.minecraft.network.chat.ClickEvent.RunCommand(String command) ->
|
||||
assertEquals(event.value(), command);
|
||||
assertEquals(((ClickEvent.Payload.Text) event.payload()).value(), command);
|
||||
case net.minecraft.network.chat.ClickEvent.CopyToClipboard(String value) ->
|
||||
assertEquals(event.value(), value);
|
||||
assertEquals(((ClickEvent.Payload.Text) event.payload()).value(), value);
|
||||
case net.minecraft.network.chat.ClickEvent.ChangePage(int page) ->
|
||||
assertEquals(event.value(), String.valueOf(page));
|
||||
assertEquals(((ClickEvent.Payload.Int) event.payload()).integer(), page);
|
||||
case net.minecraft.network.chat.ClickEvent.Custom(ResourceLocation id, Optional<Tag> payload) -> {
|
||||
assertEquals(((ClickEvent.Payload.Custom) event.payload()).key().toString(), id.toString());
|
||||
assertEquals(((ClickEvent.Payload.Custom) event.payload()).nbt(), payload.orElseThrow().asString());
|
||||
}
|
||||
default -> throw new AssertionError("Unexpected ClickEvent type: " + nms.getClass());
|
||||
}
|
||||
}
|
||||
@@ -294,10 +309,10 @@ class AdventureCodecsTest {
|
||||
.clickEvent(openUrl("https://github.com"))
|
||||
.build(),
|
||||
style()
|
||||
.hoverEvent(HoverEvent.showEntity(HoverEvent.ShowEntity.showEntity(
|
||||
Key.key(Key.MINECRAFT_NAMESPACE, "pig"),
|
||||
.hoverEvent(showEntity(HoverEvent.ShowEntity.showEntity(
|
||||
key(Key.MINECRAFT_NAMESPACE, "pig"),
|
||||
UUID.randomUUID(),
|
||||
Component.text("Dolores", TextColor.color(0x0a1ab9))
|
||||
text("Dolores", color(0x0a1ab9))
|
||||
)))
|
||||
.build()
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user