Merge remote-tracking branch 'upstream/main'
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import io.papermc.fill.model.BuildChannel
|
||||
import io.papermc.paperweight.attribute.DevBundleOutput
|
||||
import io.papermc.paperweight.util.*
|
||||
import io.papermc.paperweight.util.data.FileEntry
|
||||
@@ -10,6 +11,7 @@ plugins {
|
||||
`maven-publish`
|
||||
idea
|
||||
id("io.papermc.paperweight.core")
|
||||
id("io.papermc.fill.gradle") version "1.0.3"
|
||||
}
|
||||
|
||||
val paperMavenPublicUrl = "https://repo.papermc.io/repository/maven-public/"
|
||||
@@ -132,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 })
|
||||
|
||||
/*
|
||||
@@ -372,3 +374,20 @@ tasks.registerRunTask("runReobfPaperclip") {
|
||||
classpath(tasks.createReobfPaperclipJar.flatMap { it.outputZip })
|
||||
mainClass.set(null as String?)
|
||||
}
|
||||
|
||||
fill {
|
||||
project("paper")
|
||||
versionFamily(paperweight.minecraftVersion.map { it.split(".", "-").takeWhile { part -> part.toIntOrNull() != null }.take(2).joinToString(".") })
|
||||
version(paperweight.minecraftVersion)
|
||||
|
||||
build {
|
||||
channel = BuildChannel.STABLE
|
||||
|
||||
downloads {
|
||||
register("server:default") {
|
||||
file = tasks.createMojmapPaperclipJar.flatMap { it.outputZip }
|
||||
nameResolver.set { project, _, version, build -> "$project-$version-$build.jar" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,7 +354,7 @@ index 0000000000000000000000000000000000000000..ae2bb9a73106febfe5f0d090abd4252b
|
||||
+ }
|
||||
+}
|
||||
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
|
||||
index 7b5ed00c9b2f22af5cbc44171192d674936dc7d7..5fe3c9a159908785e08fa874982bc1a82283bb1d 100644
|
||||
index d51645b115780dac9ff6010806e8bd62dedc8e9f..3faf1b5556c55f3468182f75b2dbff4aaa3aae3a 100644
|
||||
--- a/net/minecraft/server/level/ChunkMap.java
|
||||
+++ b/net/minecraft/server/level/ChunkMap.java
|
||||
@@ -4,7 +4,6 @@ import com.google.common.collect.ImmutableList;
|
||||
@@ -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 f961540a00bfb5e1c8eb0e739f8ae535e9eee8f3..7453ddb09f349b7836f966573e4933646a75cba6 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -695,7 +695,7 @@ index 548d7c8dc517da6c4db86b11f579ae63e6db56cf..a29860af4c37b2b45df49f9ba18f7e38
|
||||
public void tick() {
|
||||
if (this.getItem().isEmpty()) {
|
||||
diff --git a/net/minecraft/world/entity/npc/Villager.java b/net/minecraft/world/entity/npc/Villager.java
|
||||
index b0607f4a9b35570b319423c7876bb904d5154e8e..98c8653647dc52059d8becfe38a74d4e62edf08f 100644
|
||||
index ef8347329b440833b45a54be2b6e4204ac0a425e..43f16df230f87a43e249a58fc10ef2da517f22ee 100644
|
||||
--- a/net/minecraft/world/entity/npc/Villager.java
|
||||
+++ b/net/minecraft/world/entity/npc/Villager.java
|
||||
@@ -269,11 +269,35 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
||||
@@ -838,7 +838,7 @@ index 52acc72841f0c6980f5f3f8ef21d0b29dd472ce3..41a6ec508a10a49a37539d2f10171d15
|
||||
+
|
||||
}
|
||||
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
|
||||
index d26e4d85d8fd8bd4f0c7de30b50a2ce370b37bf5..bab28e7afb7b1249d40631aabff16fc18cf95ea0 100644
|
||||
index 06069d3ac598f5f12feab038de4f1199794298f6..980eaba27ce2616c1573a4760cf4acc2dd251190 100644
|
||||
--- a/net/minecraft/world/level/Level.java
|
||||
+++ b/net/minecraft/world/level/Level.java
|
||||
@@ -143,6 +143,12 @@ public abstract class Level implements LevelAccessor, UUIDLookup<Entity>, AutoCl
|
||||
|
||||
@@ -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 338dc0fb07cdba5f7350cca332fa3e942c622bfb..e1fe49e4bf014e2405708270efd81bab4e1512da 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 338dc0fb07cdba5f7350cca332fa3e942c622bfb..e1fe49e4bf014e2405708270efd81bab
|
||||
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 338dc0fb07cdba5f7350cca332fa3e942c622bfb..e1fe49e4bf014e2405708270efd81bab
|
||||
}
|
||||
|
||||
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 338dc0fb07cdba5f7350cca332fa3e942c622bfb..e1fe49e4bf014e2405708270efd81bab
|
||||
|
||||
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 338dc0fb07cdba5f7350cca332fa3e942c622bfb..e1fe49e4bf014e2405708270efd81bab
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -23286,6 +23291,7 @@ index 338dc0fb07cdba5f7350cca332fa3e942c622bfb..e1fe49e4bf014e2405708270efd81bab
|
||||
}
|
||||
|
||||
}
|
||||
\ No newline at end of file
|
||||
diff --git a/io/papermc/paper/command/subcommands/ChunkDebugCommand.java b/io/papermc/paper/command/subcommands/ChunkDebugCommand.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..2dca7afbd93cfbb8686f336fcd3b45dd01fba0fc
|
||||
@@ -23861,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 c716521fb1497dc8a22d827ddb50fc1cc21a05f4..80442494db670fec34df310390ea787fb963eef4 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;
|
||||
@@ -23960,7 +23966,7 @@ index c716521fb1497dc8a22d827ddb50fc1cc21a05f4..80442494db670fec34df310390ea787f
|
||||
this.server.getPluginManager().callEvent(new org.bukkit.event.world.WorldLoadEvent(serverLevel.getWorld()));
|
||||
}
|
||||
|
||||
@@ -844,6 +915,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -845,6 +916,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
public abstract boolean shouldRconBroadcast();
|
||||
|
||||
public boolean saveAllChunks(boolean suppressLog, boolean flush, boolean forced) {
|
||||
@@ -23972,7 +23978,7 @@ index c716521fb1497dc8a22d827ddb50fc1cc21a05f4..80442494db670fec34df310390ea787f
|
||||
boolean flag = false;
|
||||
|
||||
for (ServerLevel serverLevel : this.getAllLevels()) {
|
||||
@@ -851,7 +927,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -852,7 +928,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
LOGGER.info("Saving chunks for level '{}'/{}", serverLevel, serverLevel.dimension().location());
|
||||
}
|
||||
|
||||
@@ -23981,7 +23987,7 @@ index c716521fb1497dc8a22d827ddb50fc1cc21a05f4..80442494db670fec34df310390ea787f
|
||||
flag = true;
|
||||
}
|
||||
|
||||
@@ -944,7 +1020,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -945,7 +1021,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23990,7 +23996,7 @@ index c716521fb1497dc8a22d827ddb50fc1cc21a05f4..80442494db670fec34df310390ea787f
|
||||
this.nextTickTimeNanos = Util.getNanos() + TimeUtil.NANOSECONDS_PER_MILLISECOND;
|
||||
|
||||
for (ServerLevel serverLevelx : this.getAllLevels()) {
|
||||
@@ -954,18 +1030,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -955,18 +1031,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
|
||||
this.waitUntilNextTick();
|
||||
}
|
||||
@@ -24016,7 +24022,7 @@ index c716521fb1497dc8a22d827ddb50fc1cc21a05f4..80442494db670fec34df310390ea787f
|
||||
|
||||
this.isSaving = false;
|
||||
this.resources.close();
|
||||
@@ -985,6 +1057,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -986,6 +1058,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
this.getProfileCache().save(false); // Paper - Perf: Async GameProfileCache saving
|
||||
}
|
||||
// Spigot end
|
||||
@@ -24031,7 +24037,7 @@ index c716521fb1497dc8a22d827ddb50fc1cc21a05f4..80442494db670fec34df310390ea787f
|
||||
// Paper start - Improved watchdog support - move final shutdown items here
|
||||
Util.shutdownExecutors();
|
||||
try {
|
||||
@@ -1169,6 +1249,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -1170,6 +1250,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
profilerFiller.push("tick");
|
||||
this.tickFrame.start();
|
||||
this.tickServer(flag ? () -> false : this::haveTime);
|
||||
@@ -24045,7 +24051,7 @@ index c716521fb1497dc8a22d827ddb50fc1cc21a05f4..80442494db670fec34df310390ea787f
|
||||
this.tickFrame.end();
|
||||
profilerFiller.popPush("nextTickWait");
|
||||
this.mayHaveDelayedTasks = true;
|
||||
@@ -1339,6 +1426,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -1340,6 +1427,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
|
||||
private boolean pollTaskInternal() {
|
||||
if (super.pollTask()) {
|
||||
@@ -24053,7 +24059,7 @@ index c716521fb1497dc8a22d827ddb50fc1cc21a05f4..80442494db670fec34df310390ea787f
|
||||
return true;
|
||||
} else {
|
||||
boolean ret = false; // Paper - force execution of all worlds, do not just bias the first
|
||||
@@ -2468,6 +2556,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -2478,6 +2566,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24067,7 +24073,7 @@ index c716521fb1497dc8a22d827ddb50fc1cc21a05f4..80442494db670fec34df310390ea787f
|
||||
// CraftBukkit start
|
||||
public boolean isDebugging() {
|
||||
diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
index 5db176be3bd31eb886a541eeaee922ee30ee9908..5fea5e2e9fc10d348fa3e65cd354ef6a4a717a4d 100644
|
||||
index 4488d0a2f05ef07afab0f9a1483f54b21757b29e..98927d4a5fba2a0dcdb147ac10b82c3286ccdc6b 100644
|
||||
--- a/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
+++ b/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
@@ -391,7 +391,33 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
||||
@@ -27685,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;
|
||||
@@ -28722,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 fa2ffc44336c3ddfe576b202154f14ef91a301b9..7546ff4c5ffc62d93a3f874519db8fef1e3bfbcb 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;
|
||||
@@ -29430,7 +29436,7 @@ index fa2ffc44336c3ddfe576b202154f14ef91a301b9..7546ff4c5ffc62d93a3f874519db8fef
|
||||
if (!checkPosition(this, x, y, z)) {
|
||||
return;
|
||||
}
|
||||
@@ -4828,6 +5135,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
|
||||
@@ -29443,7 +29449,7 @@ index fa2ffc44336c3ddfe576b202154f14ef91a301b9..7546ff4c5ffc62d93a3f874519db8fef
|
||||
org.bukkit.craftbukkit.event.CraftEventFactory.callEntityRemoveEvent(this, cause); // CraftBukkit
|
||||
final boolean alreadyRemoved = this.removalReason != null; // Paper - Folia schedulers
|
||||
if (this.removalReason == null) {
|
||||
@@ -4838,7 +5151,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
@@ -4828,7 +5141,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
this.stopRiding();
|
||||
}
|
||||
|
||||
@@ -29452,7 +29458,7 @@ index fa2ffc44336c3ddfe576b202154f14ef91a301b9..7546ff4c5ffc62d93a3f874519db8fef
|
||||
this.levelCallback.onRemove(removalReason);
|
||||
this.onRemoval(removalReason);
|
||||
// Paper start - Folia schedulers
|
||||
@@ -4872,7 +5185,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()
|
||||
@@ -34311,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;
|
||||
@@ -36675,10 +36681,10 @@ index c634d795644be86ad85395ffa39fbac33bf7418b..66d0a6390febe929ef774b0a78133290
|
||||
|
||||
for (SavedTick<T> savedTick : this.pendingTicks) {
|
||||
diff --git a/net/minecraft/world/waypoints/WaypointTransmitter.java b/net/minecraft/world/waypoints/WaypointTransmitter.java
|
||||
index b579839c03b371d408e3750ec09af7da1d7bc9a0..9b41c62afc861847571ad739d1dd848b8276230c 100644
|
||||
index 47382efcd50f29601a6623876be50c4d047336c5..5d1c933dfa862d0733777d305563a89ea7827f07 100644
|
||||
--- a/net/minecraft/world/waypoints/WaypointTransmitter.java
|
||||
+++ b/net/minecraft/world/waypoints/WaypointTransmitter.java
|
||||
@@ -31,7 +31,10 @@ public interface WaypointTransmitter extends Waypoint {
|
||||
@@ -32,7 +32,10 @@ public interface WaypointTransmitter extends Waypoint {
|
||||
}
|
||||
|
||||
static boolean isChunkVisible(ChunkPos pos, ServerPlayer player) {
|
||||
|
||||
@@ -5,10 +5,10 @@ Subject: [PATCH] Incremental chunk and player saving
|
||||
|
||||
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index 80442494db670fec34df310390ea787fb963eef4..2dd512565ab901bf853f34b384155902b0fe8120 100644
|
||||
index 338ef549efe82c250c74365c1c1071986920c8c9..39581095ccc69d113d954ed835bdfa32d25b5489 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -954,7 +954,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -955,7 +955,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
boolean var4;
|
||||
try {
|
||||
this.isSaving = true;
|
||||
@@ -17,7 +17,7 @@ index 80442494db670fec34df310390ea787fb963eef4..2dd512565ab901bf853f34b384155902
|
||||
var4 = this.saveAllChunks(suppressLog, flush, forced);
|
||||
} finally {
|
||||
this.isSaving = false;
|
||||
@@ -1533,9 +1533,29 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -1534,9 +1534,29 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
}
|
||||
|
||||
this.ticksUntilAutosave--;
|
||||
|
||||
@@ -48,10 +48,10 @@ index 0000000000000000000000000000000000000000..24a2090e068ad3c0d08705050944abdf
|
||||
+ }
|
||||
+}
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index 2dd512565ab901bf853f34b384155902b0fe8120..d6dcb6d146d89a8fb96e7c669e5deb802223abd6 100644
|
||||
index cff9d761dfee8a90b19fb2f3e678f99a39fc000c..0a260fdf6b198a8ab52e60bf6db2fb5eab719c48 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -1708,6 +1708,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) {
|
||||
+ }
|
||||
+
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
--- a/net/minecraft/commands/Commands.java
|
||||
+++ b/net/minecraft/commands/Commands.java
|
||||
@@ -176,6 +_,11 @@
|
||||
@@ -170,12 +_,18 @@
|
||||
|
||||
@Override
|
||||
public boolean isRestricted(CommandNode<CommandSourceStack> node) {
|
||||
+ if (node.getRequirement() instanceof PermissionSource.RestrictedMarker) return true; // Paper - restricted api
|
||||
return node.getRequirement() instanceof PermissionCheck<?> permissionCheck && permissionCheck.requiredLevel() > 0;
|
||||
}
|
||||
};
|
||||
private final CommandDispatcher<CommandSourceStack> dispatcher = new CommandDispatcher<>();
|
||||
|
||||
public Commands(Commands.CommandSelection selection, CommandBuildContext context) {
|
||||
@@ -55,76 +62,29 @@
|
||||
this.dispatcher.setConsumer(ExecutionCommandSource.resultConsumer());
|
||||
}
|
||||
|
||||
@@ -289,9 +_,41 @@
|
||||
return new ParseResults<>(commandContextBuilder, parseResults.getReader(), parseResults.getExceptions());
|
||||
}
|
||||
|
||||
+ // CraftBukkit start
|
||||
+ public void dispatchServerCommand(CommandSourceStack sender, String command) {
|
||||
+ com.google.common.base.Joiner joiner = com.google.common.base.Joiner.on(" ");
|
||||
+ if (command.startsWith("/")) {
|
||||
+ command = command.substring(1);
|
||||
+ }
|
||||
+
|
||||
+ org.bukkit.event.server.ServerCommandEvent event = new org.bukkit.event.server.ServerCommandEvent(sender.getBukkitSender(), command);
|
||||
+ org.bukkit.Bukkit.getPluginManager().callEvent(event);
|
||||
+ if (event.isCancelled()) {
|
||||
+ return;
|
||||
+ }
|
||||
+ command = event.getCommand();
|
||||
+
|
||||
+ String[] args = command.split(" ");
|
||||
+ if (args.length == 0) return; // Paper - empty commands shall not be dispatched
|
||||
+
|
||||
+ // Paper - Fix permission levels for command blocks
|
||||
+
|
||||
+ // Handle vanilla commands; // Paper - handled in CommandNode/CommandDispatcher
|
||||
+
|
||||
+ String newCommand = joiner.join(args);
|
||||
+ this.performPrefixedCommand(sender, newCommand, newCommand);
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
public void performPrefixedCommand(CommandSourceStack source, String command) {
|
||||
+ // CraftBukkit start
|
||||
+ this.performPrefixedCommand(source, command, command);
|
||||
+ }
|
||||
+
|
||||
+ public void performPrefixedCommand(CommandSourceStack source, String command, String label) {
|
||||
command = trimOptionalPrefix(command);
|
||||
- this.performCommand(this.dispatcher.parse(command, source), command);
|
||||
+ this.performCommand(this.dispatcher.parse(command, source), command, label);
|
||||
+ // CraftBukkit end
|
||||
}
|
||||
|
||||
public static String trimOptionalPrefix(String command) {
|
||||
@@ -299,9 +_,20 @@
|
||||
@@ -299,6 +_,13 @@
|
||||
}
|
||||
|
||||
public void performCommand(ParseResults<CommandSourceStack> parseResults, String command) {
|
||||
+ // CraftBukkit start
|
||||
+ this.performCommand(parseResults, command, command);
|
||||
+ // Paper start
|
||||
+ this.performCommand(parseResults, command, false);
|
||||
+ }
|
||||
+
|
||||
+ public void performCommand(ParseResults<CommandSourceStack> parseResults, String command, String label) {
|
||||
+ // CraftBukkit end
|
||||
+ // Paper start
|
||||
+ this.performCommand(parseResults, command, label, false);
|
||||
+ }
|
||||
+ public void performCommand(ParseResults<CommandSourceStack> parseResults, String command, String label, boolean throwCommandError) {
|
||||
+ public void performCommand(ParseResults<CommandSourceStack> parseResults, String command, boolean throwCommandError) {
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("Cannot perform command async");
|
||||
+ // Paper end
|
||||
CommandSourceStack commandSourceStack = parseResults.getContext().getSource();
|
||||
Profiler.get().push(() -> "/" + command);
|
||||
- ContextChain<CommandSourceStack> contextChain = finishParsing(parseResults, command, commandSourceStack);
|
||||
+ ContextChain contextChain = this.finishParsing(parseResults, command, commandSourceStack, label); // CraftBukkit // Paper - Add UnknownCommandEvent
|
||||
|
||||
try {
|
||||
if (contextChain != null) {
|
||||
@@ -313,9 +_,10 @@
|
||||
ContextChain<CommandSourceStack> contextChain = finishParsing(parseResults, command, commandSourceStack);
|
||||
@@ -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);
|
||||
@@ -133,12 +93,12 @@
|
||||
StackTraceElement[] stackTrace = var12.getStackTrace();
|
||||
|
||||
for (int i = 0; i < Math.min(stackTrace.length, 3); i++) {
|
||||
@@ -341,18 +_,22 @@
|
||||
@@ -341,13 +_,17 @@
|
||||
}
|
||||
|
||||
@Nullable
|
||||
- private static ContextChain<CommandSourceStack> finishParsing(ParseResults<CommandSourceStack> parseResults, String command, CommandSourceStack source) {
|
||||
+ private ContextChain<CommandSourceStack> finishParsing(ParseResults<CommandSourceStack> parseResults, String command, CommandSourceStack source, String label) { // CraftBukkit // Paper - Add UnknownCommandEvent
|
||||
+ private ContextChain<CommandSourceStack> finishParsing(ParseResults<CommandSourceStack> parseResults, String command, CommandSourceStack source) {
|
||||
try {
|
||||
validateParseResults(parseResults);
|
||||
return ContextChain.tryFlatten(parseResults.getContext().build(command))
|
||||
@@ -153,12 +113,6 @@
|
||||
if (var7.getInput() != null && var7.getCursor() >= 0) {
|
||||
int min = Math.min(var7.getInput().length(), var7.getCursor());
|
||||
MutableComponent mutableComponent = Component.empty()
|
||||
.withStyle(ChatFormatting.GRAY)
|
||||
- .withStyle(style -> style.withClickEvent(new ClickEvent.SuggestCommand("/" + command)));
|
||||
+ .withStyle(style -> style.withClickEvent(new ClickEvent.SuggestCommand("/" + label))); // CraftBukkit // Paper
|
||||
if (min > 10) {
|
||||
mutableComponent.append(CommonComponents.ELLIPSIS);
|
||||
}
|
||||
@@ -364,7 +_,17 @@
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
--- a/net/minecraft/commands/PermissionSource.java
|
||||
+++ b/net/minecraft/commands/PermissionSource.java
|
||||
@@ -9,9 +_,20 @@
|
||||
@@ -9,9 +_,22 @@
|
||||
return this.hasPermission(2);
|
||||
}
|
||||
|
||||
- public record Check<T extends PermissionSource>(@Override int requiredLevel) implements PermissionCheck<T> {
|
||||
+ public record Check<T extends PermissionSource>(@Override int requiredLevel, java.util.concurrent.atomic.AtomicReference<com.mojang.brigadier.tree.CommandNode<CommandSourceStack>> vanillaNode) implements PermissionCheck<T> { // Paper
|
||||
+ // Paper start - Vanilla Command permission checking
|
||||
+ // Paper start - Vanilla Command permission checking & expose restricted API
|
||||
+ interface RestrictedMarker { }
|
||||
+
|
||||
+ public record Check<T extends PermissionSource>(@Override int requiredLevel, java.util.concurrent.atomic.AtomicReference<com.mojang.brigadier.tree.CommandNode<CommandSourceStack>> vanillaNode) implements PermissionCheck<T> {
|
||||
+ public Check(int requiredLevel) {
|
||||
+ this(requiredLevel, new java.util.concurrent.atomic.AtomicReference<>());
|
||||
+ }
|
||||
+ // Paper end - Vanilla Command permission checking
|
||||
+ // Paper end - Vanilla Command permission checking & expose restricted API
|
||||
@Override
|
||||
public boolean test(T source) {
|
||||
+ // Paper start - Vanilla Command permission checking
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -172,7 +172,7 @@
|
||||
if (profiledDuration != null) {
|
||||
profiledDuration.finish(true);
|
||||
}
|
||||
@@ -364,25 +_,265 @@
|
||||
@@ -364,25 +_,266 @@
|
||||
protected void forceDifficulty() {
|
||||
}
|
||||
|
||||
@@ -439,6 +439,7 @@
|
||||
+ if (io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper != null) io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper.pluginsEnabled(); // Paper - Remap plugins
|
||||
+ io.papermc.paper.command.brigadier.PaperCommands.INSTANCE.setValid(); // Paper - reset invalid state for event fire below
|
||||
+ io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner.INSTANCE.callReloadableRegistrarEvent(io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents.COMMANDS, io.papermc.paper.command.brigadier.PaperCommands.INSTANCE, org.bukkit.plugin.Plugin.class, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.INITIAL); // Paper - call commands event for regular plugins
|
||||
+ this.server.getCommandMap().registerServerAliases(); // Paper - relocate initial CommandMap#registerServerAliases() call
|
||||
+ ((org.bukkit.craftbukkit.help.SimpleHelpMap) this.server.getHelpMap()).initializeCommands();
|
||||
+ this.server.getPluginManager().callEvent(new org.bukkit.event.server.ServerLoadEvent(org.bukkit.event.server.ServerLoadEvent.LoadType.STARTUP));
|
||||
+ this.connection.acceptConnections();
|
||||
@@ -983,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();
|
||||
|
||||
@@ -273,7 +273,7 @@
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -291,13 +_,23 @@
|
||||
@@ -291,12 +_,20 @@
|
||||
}
|
||||
|
||||
public void handleConsoleInput(String msg, CommandSourceStack source) {
|
||||
@@ -284,23 +284,19 @@
|
||||
public void handleConsoleInputs() {
|
||||
- while (!this.consoleInput.isEmpty()) {
|
||||
- ConsoleInput consoleInput = this.consoleInput.remove(0);
|
||||
- this.getCommands().performPrefixedCommand(consoleInput.source, consoleInput.msg);
|
||||
+ // Paper start - Perf: use proper queue
|
||||
+ ConsoleInput servercommand;
|
||||
+ while ((servercommand = this.serverCommandQueue.poll()) != null) {
|
||||
+ ConsoleInput consoleInput;
|
||||
+ while ((consoleInput = this.serverCommandQueue.poll()) != null) {
|
||||
+ // Paper end - Perf: use proper queue
|
||||
+ // CraftBukkit start - ServerCommand for preprocessing
|
||||
+ org.bukkit.event.server.ServerCommandEvent event = new org.bukkit.event.server.ServerCommandEvent(this.console, servercommand.msg);
|
||||
+ org.bukkit.event.server.ServerCommandEvent event = new org.bukkit.event.server.ServerCommandEvent(this.console, consoleInput.msg);
|
||||
+ this.server.getPluginManager().callEvent(event);
|
||||
+ if (event.isCancelled()) continue;
|
||||
+ servercommand = new ConsoleInput(event.getCommand(), servercommand.source);
|
||||
+
|
||||
+ // this.getCommands().performPrefixedCommand(servercommand.source, servercommand.msg); // Called in dispatchServerCommand
|
||||
+ this.server.dispatchServerCommand(this.console, servercommand);
|
||||
+ consoleInput = new ConsoleInput(event.getCommand(), consoleInput.source);
|
||||
+ // CraftBukkit end
|
||||
this.getCommands().performPrefixedCommand(consoleInput.source, consoleInput.msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,7 +_,11 @@
|
||||
@Override
|
||||
public boolean enforceSecureProfile() {
|
||||
@@ -314,7 +310,7 @@
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -515,14 +_,54 @@
|
||||
@@ -515,14 +_,53 @@
|
||||
|
||||
@Override
|
||||
public String getPluginNames() {
|
||||
@@ -365,8 +361,7 @@
|
||||
+ if (event.isCancelled()) {
|
||||
+ return;
|
||||
+ }
|
||||
+ ConsoleInput serverCommand = new ConsoleInput(event.getCommand(), wrapper);
|
||||
+ this.server.dispatchServerCommand(event.getSender(), serverCommand);
|
||||
+ this.getCommands().performPrefixedCommand(wrapper, event.getCommand());
|
||||
+ });
|
||||
+ return rconConsoleSource.getCommandResponse();
|
||||
+ // CraftBukkit end
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
@@ -1786,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);
|
||||
}
|
||||
|
||||
@@ -1815,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);
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -12,12 +12,18 @@
|
||||
|
||||
public int getSuccessCount() {
|
||||
return this.successCount;
|
||||
@@ -108,7 +_,7 @@
|
||||
@@ -108,7 +_,13 @@
|
||||
this.successCount++;
|
||||
}
|
||||
});
|
||||
- server.getCommands().performPrefixedCommand(commandSourceStack, this.command);
|
||||
+ server.getCommands().dispatchServerCommand(commandSourceStack, this.command); // CraftBukkit
|
||||
+ // Paper start - ServerCommandEvent
|
||||
+ org.bukkit.event.server.ServerCommandEvent event = new org.bukkit.event.server.ServerCommandEvent(commandSourceStack.getBukkitSender(), net.minecraft.commands.Commands.trimOptionalPrefix(this.command));
|
||||
+ if (!event.callEvent()) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ server.getCommands().performPrefixedCommand(commandSourceStack, event.getCommand());
|
||||
+ // Paper end - ServerCommandEvent
|
||||
} catch (Throwable var6) {
|
||||
CrashReport crashReport = CrashReport.forThrowable(var6, "Executing command block");
|
||||
CrashReportCategory crashReportCategory = crashReport.addCategory("Command to be executed");
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
@@ -0,0 +1,10 @@
|
||||
--- a/net/minecraft/world/waypoints/WaypointTransmitter.java
|
||||
+++ b/net/minecraft/world/waypoints/WaypointTransmitter.java
|
||||
@@ -20,6 +_,7 @@
|
||||
Waypoint.Icon waypointIcon();
|
||||
|
||||
static boolean doesSourceIgnoreReceiver(LivingEntity entity, ServerPlayer player) {
|
||||
+ if (!player.getBukkitEntity().canSee(entity.getBukkitEntity())) return true; // Paper - ignore if entity is hidden from player
|
||||
if (player.isSpectator()) {
|
||||
return false;
|
||||
} else if (!entity.isSpectator() && !entity.hasIndirectPassenger(player)) {
|
||||
@@ -1,10 +1,12 @@
|
||||
package io.papermc.paper;
|
||||
|
||||
import io.papermc.paper.command.brigadier.CommandSourceStack;
|
||||
import io.papermc.paper.world.damagesource.CombatEntry;
|
||||
import io.papermc.paper.world.damagesource.FallLocationType;
|
||||
import io.papermc.paper.world.damagesource.PaperCombatEntryWrapper;
|
||||
import io.papermc.paper.world.damagesource.PaperCombatTrackerWrapper;
|
||||
import net.minecraft.Optionull;
|
||||
import net.minecraft.commands.PermissionSource;
|
||||
import net.minecraft.world.damagesource.FallLocation;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.craftbukkit.block.CraftBiome;
|
||||
@@ -16,6 +18,7 @@ import org.bukkit.damage.DamageSource;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@NullMarked
|
||||
public class PaperServerInternalAPIBridge implements InternalAPIBridge {
|
||||
@@ -71,4 +74,16 @@ public class PaperServerInternalAPIBridge implements InternalAPIBridge {
|
||||
damageSource, damage, fallLocation, fallDistance
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<CommandSourceStack> restricted(final Predicate<CommandSourceStack> predicate) {
|
||||
record RestrictedPredicate(Predicate<CommandSourceStack> predicate) implements Predicate<CommandSourceStack>, PermissionSource.RestrictedMarker {
|
||||
@Override
|
||||
public boolean test(final CommandSourceStack commandSourceStack) {
|
||||
return this.predicate.test(commandSourceStack);
|
||||
}
|
||||
}
|
||||
|
||||
return new RestrictedPredicate(predicate);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -379,6 +377,7 @@ public final class PaperAdventure {
|
||||
case PLAYER -> SoundSource.PLAYERS;
|
||||
case AMBIENT -> SoundSource.AMBIENT;
|
||||
case VOICE -> SoundSource.VOICE;
|
||||
case UI -> SoundSource.UI;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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) -> {
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.util.Spliterator;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.craftbukkit.command.VanillaCommandWrapper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -96,7 +97,7 @@ public class BukkitBrigForwardingMap extends HashMap<String, Command> {
|
||||
public Command put(String key, Command value) {
|
||||
Command old = this.get(key);
|
||||
this.getDispatcher().getRoot().removeCommand(key); // Override previous command
|
||||
if (value instanceof PluginVanillaCommandWrapper wrapper && wrapper.getName().equals(key)) {
|
||||
if (value instanceof VanillaCommandWrapper wrapper && wrapper.getName().equals(key)) {
|
||||
// Don't break when some plugin tries to remove and add back a plugin command registered with modern API...
|
||||
this.getDispatcher().getRoot().addChild((CommandNode) wrapper.vanillaCommand);
|
||||
} else {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,7 +302,6 @@ public final class CraftServer implements Server {
|
||||
public CraftDataPackManager dataPackManager;
|
||||
private final CraftServerTickManager serverTickManager;
|
||||
private final CraftServerLinks serverLinks;
|
||||
public boolean playerCommandState;
|
||||
private boolean printSaveWarning;
|
||||
private CraftIconCache icon;
|
||||
private boolean overrideAllCommandBlockCommands = false;
|
||||
@@ -613,7 +612,6 @@ public final class CraftServer implements Server {
|
||||
// Spigot start - Allow vanilla commands to be forced to be the main command
|
||||
this.commandMap.setFallbackCommands();
|
||||
// Spigot end
|
||||
this.commandMap.registerServerAliases();
|
||||
DefaultPermissions.registerCorePermissions();
|
||||
CraftDefaultPermissions.registerCorePermissions();
|
||||
if (!io.papermc.paper.configuration.GlobalConfiguration.get().misc.loadPermissionsYmlBeforePlugins) this.loadCustomPermissions(); // Paper
|
||||
@@ -974,41 +972,13 @@ public final class CraftServer implements Server {
|
||||
return this.playerList;
|
||||
}
|
||||
|
||||
// NOTE: Should only be called from DedicatedServer.ah()
|
||||
public boolean dispatchServerCommand(CommandSender sender, ConsoleInput serverCommand) {
|
||||
if (sender instanceof Conversable) {
|
||||
Conversable conversable = (Conversable) sender;
|
||||
|
||||
if (conversable.isConversing()) {
|
||||
conversable.acceptConversationInput(serverCommand.msg);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
try {
|
||||
this.playerCommandState = true;
|
||||
return this.dispatchCommand(sender, serverCommand.msg);
|
||||
} catch (Exception ex) {
|
||||
this.getLogger().log(Level.WARNING, "Unexpected exception while parsing console command \"" + serverCommand.msg + '"', ex);
|
||||
return false;
|
||||
} finally {
|
||||
this.playerCommandState = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchCommand(CommandSender sender, String commandLine) {
|
||||
Preconditions.checkArgument(sender != null, "sender cannot be null");
|
||||
public boolean dispatchCommand(CommandSender rawSender, String commandLine) {
|
||||
Preconditions.checkArgument(rawSender != null, "sender cannot be null");
|
||||
Preconditions.checkArgument(commandLine != null, "commandLine cannot be null");
|
||||
org.spigotmc.AsyncCatcher.catchOp("Command Dispatched Async: " + commandLine); // Spigot // Paper - Include command in error message
|
||||
CommandSourceStack sourceStack = VanillaCommandWrapper.getListener(rawSender);
|
||||
|
||||
if (this.commandMap.dispatch(sender, commandLine)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.dispatchCommand(VanillaCommandWrapper.getListener(sender), commandLine);
|
||||
}
|
||||
|
||||
public boolean dispatchCommand(CommandSourceStack sourceStack, String commandLine) {
|
||||
net.minecraft.commands.Commands commands = this.getHandle().getServer().getCommands();
|
||||
com.mojang.brigadier.CommandDispatcher<CommandSourceStack> dispatcher = commands.getDispatcher();
|
||||
com.mojang.brigadier.ParseResults<CommandSourceStack> results = dispatcher.parse(commandLine, sourceStack);
|
||||
@@ -1018,7 +988,12 @@ public final class CraftServer implements Server {
|
||||
Command target = this.commandMap.getCommand(args[0].toLowerCase(java.util.Locale.ENGLISH));
|
||||
|
||||
try {
|
||||
commands.performCommand(results, commandLine, commandLine, true);
|
||||
if (results.getContext().getNodes().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
Commands.validateParseResults(results);
|
||||
commands.performCommand(results, commandLine, true);
|
||||
return true;
|
||||
} catch (CommandException ex) {
|
||||
new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerCommandException(ex, target, sender, args)).callEvent(); // Paper
|
||||
throw ex;
|
||||
@@ -1027,9 +1002,6 @@ public final class CraftServer implements Server {
|
||||
new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerCommandException(ex, target, sender, args)).callEvent(); // Paper
|
||||
throw new CommandException(msg, ex);
|
||||
}
|
||||
// Paper end
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2609,7 +2581,7 @@ public final class CraftServer implements Server {
|
||||
}
|
||||
|
||||
public void checkSaveState() {
|
||||
if (this.playerCommandState || this.printSaveWarning || this.console.autosavePeriod <= 0) {
|
||||
if (this.printSaveWarning || this.console.autosavePeriod <= 0) {
|
||||
return;
|
||||
}
|
||||
this.printSaveWarning = true;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.util.InclusiveRange;
|
||||
import net.minecraft.util.ProblemReporter;
|
||||
@@ -51,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);
|
||||
}
|
||||
@@ -183,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ public class VanillaCommandWrapper extends BukkitCommand { // Paper
|
||||
if (!this.testPermission(sender)) return true;
|
||||
|
||||
CommandSourceStack source = VanillaCommandWrapper.getListener(sender);
|
||||
this.commands().performPrefixedCommand(source, this.toDispatcher(args, this.getName()), this.toDispatcher(args, commandLabel)); // Paper
|
||||
this.commands().performPrefixedCommand(source, this.toDispatcher(args, this.getName()));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -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
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -185,11 +185,6 @@ public class CraftItemType<M extends ItemMeta> extends HolderableBase<Item> impl
|
||||
return expectedItem.isEmpty() ? null : CraftItemType.minecraftToBukkitNew(expectedItem.getItem());
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public EquipmentSlot getEquipmentSlot() {
|
||||
// return CraftEquipmentSlot.getSlot(EntityInsentient.getEquipmentSlotForItem(CraftItemStack.asNMSCopy(ItemStack.of(this))));
|
||||
// }
|
||||
|
||||
@Override
|
||||
public Multimap<Attribute, AttributeModifier> getDefaultAttributeModifiers() {
|
||||
return this.getDefaultAttributeModifiers(sg -> true);
|
||||
|
||||
@@ -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