mirror of
https://github.com/PaperMC/Paper.git
synced 2025-08-01 04:32:11 -07:00
Apply some feature patches
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Thu, 3 Mar 2016 02:07:55 -0600
|
||||
Subject: [PATCH] Optimize isInWorldBounds and getBlockState for inlining
|
||||
|
||||
Hot methods, so reduce # of instructions for the method.
|
||||
|
||||
Move is valid location test to the BlockPosition class so that it can access local variables.
|
||||
|
||||
Replace all calls to the new place to the unnecessary forward.
|
||||
|
||||
Optimize getType and getBlockData to manually inline and optimize the calls
|
||||
|
||||
diff --git a/net/minecraft/core/Vec3i.java b/net/minecraft/core/Vec3i.java
|
||||
index 03e2178430849d26c9826517e34ad069c94fc00a..11555ce7159ca6c8ddfe9691f86d3720c07cb086 100644
|
||||
--- a/net/minecraft/core/Vec3i.java
|
||||
+++ b/net/minecraft/core/Vec3i.java
|
||||
@@ -28,6 +28,12 @@ public class Vec3i implements Comparable<Vec3i> {
|
||||
);
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ public final boolean isInsideBuildHeightAndWorldBoundsHorizontal(net.minecraft.world.level.LevelHeightAccessor levelHeightAccessor) {
|
||||
+ return getX() >= -30000000 && getZ() >= -30000000 && getX() < 30000000 && getZ() < 30000000 && !levelHeightAccessor.isOutsideBuildHeight(getY());
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
public Vec3i(int x, int y, int z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
|
||||
index e0239091729f6be138c037951fd5c138497ee358..691fee2e2097244126f4fac0f5d00bf6916b9766 100644
|
||||
--- a/net/minecraft/world/level/Level.java
|
||||
+++ b/net/minecraft/world/level/Level.java
|
||||
@@ -350,7 +350,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
||||
// Paper end
|
||||
|
||||
public boolean isInWorldBounds(BlockPos pos) {
|
||||
- return !this.isOutsideBuildHeight(pos) && isInWorldBoundsHorizontal(pos);
|
||||
+ return pos.isInsideBuildHeightAndWorldBoundsHorizontal(this); // Paper - use better/optimized check
|
||||
}
|
||||
|
||||
public static boolean isInSpawnableBounds(BlockPos pos) {
|
||||
diff --git a/net/minecraft/world/level/chunk/ChunkAccess.java b/net/minecraft/world/level/chunk/ChunkAccess.java
|
||||
index f68f3f5e8ef39a0dc371e75110227a39791c04c8..12d9b532e466ec4e46920d409b5f1b3ae60b80f8 100644
|
||||
--- a/net/minecraft/world/level/chunk/ChunkAccess.java
|
||||
+++ b/net/minecraft/world/level/chunk/ChunkAccess.java
|
||||
@@ -130,6 +130,7 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh
|
||||
return GameEventListenerRegistry.NOOP;
|
||||
}
|
||||
|
||||
+ public abstract BlockState getBlockState(final int x, final int y, final int z); // Paper
|
||||
@Nullable
|
||||
public abstract BlockState setBlockState(BlockPos pos, BlockState state, boolean isMoving);
|
||||
|
||||
diff --git a/net/minecraft/world/level/chunk/ImposterProtoChunk.java b/net/minecraft/world/level/chunk/ImposterProtoChunk.java
|
||||
index 6c43e5e685871289968db8171342fd84189edcba..e7c0f4da8508fbca467326f475668d66454d7b77 100644
|
||||
--- a/net/minecraft/world/level/chunk/ImposterProtoChunk.java
|
||||
+++ b/net/minecraft/world/level/chunk/ImposterProtoChunk.java
|
||||
@@ -56,6 +56,12 @@ public class ImposterProtoChunk extends ProtoChunk {
|
||||
public BlockState getBlockState(BlockPos pos) {
|
||||
return this.wrapped.getBlockState(pos);
|
||||
}
|
||||
+ // Paper start
|
||||
+ @Override
|
||||
+ public final BlockState getBlockState(final int x, final int y, final int z) {
|
||||
+ return this.wrapped.getBlockStateFinal(x, y, z);
|
||||
+ }
|
||||
+ // Paper end
|
||||
|
||||
@Override
|
||||
public FluidState getFluidState(BlockPos pos) {
|
||||
diff --git a/net/minecraft/world/level/chunk/ProtoChunk.java b/net/minecraft/world/level/chunk/ProtoChunk.java
|
||||
index e359b5f694210f05e5675a995dbfc1a95cec76db..8c333d7f390d823a7c7f303e2f444f52ec16f799 100644
|
||||
--- a/net/minecraft/world/level/chunk/ProtoChunk.java
|
||||
+++ b/net/minecraft/world/level/chunk/ProtoChunk.java
|
||||
@@ -99,12 +99,18 @@ public class ProtoChunk extends ChunkAccess {
|
||||
|
||||
@Override
|
||||
public BlockState getBlockState(BlockPos pos) {
|
||||
- int y = pos.getY();
|
||||
+ // Paper start
|
||||
+ return getBlockState(pos.getX(), pos.getY(), pos.getZ());
|
||||
+ }
|
||||
+ public BlockState getBlockState(final int x, final int y, final int z) {
|
||||
+ // Paper end
|
||||
if (this.isOutsideBuildHeight(y)) {
|
||||
return Blocks.VOID_AIR.defaultBlockState();
|
||||
} else {
|
||||
- LevelChunkSection section = this.getSection(this.getSectionIndex(y));
|
||||
- return section.hasOnlyAir() ? Blocks.AIR.defaultBlockState() : section.getBlockState(pos.getX() & 15, y & 15, pos.getZ() & 15);
|
||||
+ // Paper start
|
||||
+ LevelChunkSection section = this.getSections()[this.getSectionIndex(y)];
|
||||
+ return section.hasOnlyAir() ? Blocks.AIR.defaultBlockState() : section.getBlockState(x & 15, y & 15, z & 15);
|
||||
+ // Paper end
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,135 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Fri, 29 Apr 2016 20:02:00 -0400
|
||||
Subject: [PATCH] Improve Maps (in item frames) performance and bug fixes
|
||||
|
||||
Maps used a modified version of rendering to support plugin controlled
|
||||
imaging on maps. The Craft Map Renderer is much slower than Vanilla,
|
||||
causing maps in item frames to cause a noticeable hit on server performance.
|
||||
|
||||
This updates the map system to not use the Craft system if we detect that no
|
||||
custom renderers are in use, defaulting to the much simpler Vanilla system.
|
||||
|
||||
Additionally, numerous issues to player position tracking on maps has been fixed.
|
||||
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index 678b3027e8c53e6021ea49afa69cdbe5f60dcf25..cce78d73e8adafd66d0f3ffb3fabb5e6c025c7df 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -2282,7 +2282,9 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
}
|
||||
|
||||
map.carriedByPlayers.remove(player);
|
||||
- map.carriedBy.removeIf(holdingPlayer -> holdingPlayer.player == player);
|
||||
+ if (map.carriedBy.removeIf(holdingPlayer -> holdingPlayer.player == player)) {
|
||||
+ map.decorations.remove(player.getName().getString()); // Paper
|
||||
+ }
|
||||
}
|
||||
}
|
||||
}
|
||||
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
|
||||
index e3a4cf9cd03670705391f4dc68f193d7cee656e0..f9c9485a051f5fa6d508b8a194c9f17657e1a0f4 100644
|
||||
--- a/net/minecraft/server/level/ServerPlayer.java
|
||||
+++ b/net/minecraft/server/level/ServerPlayer.java
|
||||
@@ -2654,6 +2654,14 @@ public class ServerPlayer extends Player {
|
||||
this.awardStat(Stats.DROP);
|
||||
}
|
||||
|
||||
+ // Paper start - remove player from map on drop
|
||||
+ if (item.getItem() == net.minecraft.world.item.Items.FILLED_MAP) {
|
||||
+ final MapItemSavedData mapData = MapItem.getSavedData(item, this.level());
|
||||
+ if (mapData != null) {
|
||||
+ mapData.tickCarriedBy(this, item);
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - remove player from map on drop
|
||||
return itemEntity;
|
||||
}
|
||||
}
|
||||
diff --git a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java b/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
|
||||
index fd50bd77e4dc7b18240afe5505c6e6f0f869c127..f60c2f3a3dfc69f50225b6ee7333ada5e9dfd090 100644
|
||||
--- a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
|
||||
+++ b/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
|
||||
@@ -67,6 +67,7 @@ public class MapItemSavedData extends SavedData {
|
||||
public final Map<String, MapDecoration> decorations = Maps.newLinkedHashMap();
|
||||
private final Map<String, MapFrame> frameMarkers = Maps.newHashMap();
|
||||
private int trackedDecorationCount;
|
||||
+ private org.bukkit.craftbukkit.map.RenderData vanillaRender = new org.bukkit.craftbukkit.map.RenderData(); // Paper
|
||||
|
||||
// CraftBukkit start
|
||||
public final org.bukkit.craftbukkit.map.CraftMapView mapView;
|
||||
@@ -92,6 +93,7 @@ public class MapItemSavedData extends SavedData {
|
||||
// CraftBukkit start
|
||||
this.mapView = new org.bukkit.craftbukkit.map.CraftMapView(this);
|
||||
this.server = (org.bukkit.craftbukkit.CraftServer) org.bukkit.Bukkit.getServer();
|
||||
+ this.vanillaRender.buffer = colors; // Paper
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
@@ -163,6 +165,7 @@ public class MapItemSavedData extends SavedData {
|
||||
if (byteArray.length == 16384) {
|
||||
mapItemSavedData.colors = byteArray;
|
||||
}
|
||||
+ mapItemSavedData.vanillaRender.buffer = byteArray; // Paper
|
||||
|
||||
RegistryOps<Tag> registryOps = levelRegistry.createSerializationContext(NbtOps.INSTANCE);
|
||||
|
||||
@@ -337,7 +340,7 @@ public class MapItemSavedData extends SavedData {
|
||||
this.trackedDecorationCount--;
|
||||
}
|
||||
|
||||
- this.setDecorationsDirty();
|
||||
+ if (mapDecoration != null) this.setDecorationsDirty(); // Paper - only mark dirty if a change occurs
|
||||
}
|
||||
|
||||
public static void addTargetDecoration(ItemStack stack, BlockPos pos, String type, Holder<MapDecorationType> mapDecorationType) {
|
||||
@@ -610,7 +613,16 @@ public class MapItemSavedData extends SavedData {
|
||||
@Nullable
|
||||
Packet<?> nextUpdatePacket(MapId mapId) {
|
||||
MapItemSavedData.MapPatch mapPatch;
|
||||
- org.bukkit.craftbukkit.map.RenderData render = MapItemSavedData.this.mapView.render((org.bukkit.craftbukkit.entity.CraftPlayer) this.player.getBukkitEntity()); // CraftBukkit
|
||||
+ // Paper start
|
||||
+ if (!this.dirtyData && this.tick % 5 != 0) {
|
||||
+ // this won't end up sending, so don't render it!
|
||||
+ this.tick++;
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ final boolean vanillaMaps = this.shouldUseVanillaMap();
|
||||
+ org.bukkit.craftbukkit.map.RenderData render = !vanillaMaps ? MapItemSavedData.this.mapView.render((org.bukkit.craftbukkit.entity.CraftPlayer) this.player.getBukkitEntity()) : MapItemSavedData.this.vanillaRender; // CraftBukkit // Paper
|
||||
+ // Paper end
|
||||
if (this.dirtyData) {
|
||||
this.dirtyData = false;
|
||||
mapPatch = this.createPatch(render.buffer); // CraftBukkit
|
||||
@@ -623,6 +635,7 @@ public class MapItemSavedData extends SavedData {
|
||||
this.dirtyDecorations = false;
|
||||
// CraftBukkit start
|
||||
java.util.Collection<MapDecoration> icons = new java.util.ArrayList<MapDecoration>();
|
||||
+ if (vanillaMaps) this.addSeenPlayers(icons); // Paper
|
||||
|
||||
for (org.bukkit.map.MapCursor cursor : render.cursors) {
|
||||
if (cursor.isVisible()) {
|
||||
@@ -658,6 +671,23 @@ public class MapItemSavedData extends SavedData {
|
||||
private void markDecorationsDirty() {
|
||||
this.dirtyDecorations = true;
|
||||
}
|
||||
+
|
||||
+ // Paper start
|
||||
+ private void addSeenPlayers(java.util.Collection<MapDecoration> icons) {
|
||||
+ org.bukkit.entity.Player player = (org.bukkit.entity.Player) this.player.getBukkitEntity();
|
||||
+ MapItemSavedData.this.decorations.forEach((name, mapIcon) -> {
|
||||
+ // If this cursor is for a player check visibility with vanish system
|
||||
+ org.bukkit.entity.Player other = org.bukkit.Bukkit.getPlayerExact(name); // Spigot
|
||||
+ if (other == null || player.canSee(other)) {
|
||||
+ icons.add(mapIcon);
|
||||
+ }
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ private boolean shouldUseVanillaMap() {
|
||||
+ return mapView.getRenderers().size() == 1 && mapView.getRenderers().getFirst().getClass() == org.bukkit.craftbukkit.map.CraftMapRenderer.class;
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
record MapDecorationLocation(Holder<MapDecorationType> type, byte x, byte y, byte rot) {
|
@@ -0,0 +1,391 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Wed, 6 May 2020 04:53:35 -0400
|
||||
Subject: [PATCH] Optimize Network Manager and add advanced packet support
|
||||
|
||||
Adds ability for 1 packet to bundle other packets to follow it
|
||||
Adds ability for a packet to delay sending more packets until a state is ready.
|
||||
|
||||
Removes synchronization from sending packets
|
||||
Removes processing packet queue off of main thread
|
||||
- for the few cases where it is allowed, order is not necessary nor
|
||||
should it even be happening concurrently in first place (handshaking/login/status)
|
||||
|
||||
Ensures packets sent asynchronously are dispatched on main thread
|
||||
|
||||
This helps ensure safety for ProtocolLib as packet listeners
|
||||
are commonly accessing world state. This will allow you to schedule
|
||||
a packet to be sent async, but itll be dispatched sync for packet
|
||||
listeners to process.
|
||||
|
||||
This should solve some deadlock risks
|
||||
|
||||
Also adds Netty Channel Flush Consolidation to reduce the amount of flushing
|
||||
|
||||
Also avoids spamming closed channel exception by rechecking closed state in dispatch
|
||||
and then catch exceptions and close if they fire.
|
||||
|
||||
Part of this commit was authored by: Spottedleaf, sandtechnology
|
||||
|
||||
diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java
|
||||
index b624f001ba9d98c4dc68fcd66c0bc2de0a12308c..c4bb28857ee11dccc9924666634488044c666fd1 100644
|
||||
--- a/net/minecraft/network/Connection.java
|
||||
+++ b/net/minecraft/network/Connection.java
|
||||
@@ -85,7 +85,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
private static final ProtocolInfo<ServerHandshakePacketListener> INITIAL_PROTOCOL = HandshakeProtocols.SERVERBOUND;
|
||||
private final PacketFlow receiving;
|
||||
private volatile boolean sendLoginDisconnect = true;
|
||||
- private final Queue<Consumer<Connection>> pendingActions = Queues.newConcurrentLinkedQueue();
|
||||
+ private final Queue<WrappedConsumer> pendingActions = Queues.newConcurrentLinkedQueue(); // Paper - Optimize network
|
||||
public Channel channel;
|
||||
public SocketAddress address;
|
||||
// Spigot Start
|
||||
@@ -145,6 +145,10 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
}
|
||||
// Paper end - packet limiter
|
||||
@Nullable public SocketAddress haProxyAddress; // Paper - Add API to get player's proxy address
|
||||
+ // Paper start - Optimize network
|
||||
+ public boolean isPending = true;
|
||||
+ public boolean queueImmunity;
|
||||
+ // Paper end - Optimize network
|
||||
|
||||
public Connection(PacketFlow receiving) {
|
||||
this.receiving = receiving;
|
||||
@@ -425,11 +429,38 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
}
|
||||
|
||||
public void send(Packet<?> packet, @Nullable PacketSendListener listener, boolean flush) {
|
||||
- if (this.isConnected()) {
|
||||
- this.flushQueue();
|
||||
+ // Paper start - Optimize network: Handle oversized packets better
|
||||
+ final boolean connected = this.isConnected();
|
||||
+ if (!connected && !this.preparing) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ packet.onPacketDispatch(this.getPlayer());
|
||||
+ if (connected && (InnerUtil.canSendImmediate(this, packet)
|
||||
+ || (io.papermc.paper.util.MCUtil.isMainThread() && packet.isReady() && this.pendingActions.isEmpty()
|
||||
+ && (packet.getExtraPackets() == null || packet.getExtraPackets().isEmpty())))) {
|
||||
this.sendPacket(packet, listener, flush);
|
||||
} else {
|
||||
- this.pendingActions.add(connection -> connection.sendPacket(packet, listener, flush));
|
||||
+ // Write the packets to the queue, then flush - antixray hooks there already
|
||||
+ final java.util.List<Packet<?>> extraPackets = InnerUtil.buildExtraPackets(packet);
|
||||
+ final boolean hasExtraPackets = extraPackets != null && !extraPackets.isEmpty();
|
||||
+ if (!hasExtraPackets) {
|
||||
+ this.pendingActions.add(new PacketSendAction(packet, listener, flush));
|
||||
+ } else {
|
||||
+ final java.util.List<PacketSendAction> actions = new java.util.ArrayList<>(1 + extraPackets.size());
|
||||
+ actions.add(new PacketSendAction(packet, null, false)); // Delay the future listener until the end of the extra packets
|
||||
+
|
||||
+ for (int i = 0, len = extraPackets.size(); i < len;) {
|
||||
+ final Packet<?> extraPacket = extraPackets.get(i);
|
||||
+ final boolean end = ++i == len;
|
||||
+ actions.add(new PacketSendAction(extraPacket, end ? listener : null, end)); // Append listener to the end
|
||||
+ }
|
||||
+
|
||||
+ this.pendingActions.addAll(actions);
|
||||
+ }
|
||||
+
|
||||
+ this.flushQueue();
|
||||
+ // Paper end - Optimize network
|
||||
}
|
||||
}
|
||||
|
||||
@@ -438,7 +469,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
this.flushQueue();
|
||||
action.accept(this);
|
||||
} else {
|
||||
- this.pendingActions.add(action);
|
||||
+ this.pendingActions.add(new WrappedConsumer(action)); // Paper - Optimize network
|
||||
}
|
||||
}
|
||||
|
||||
@@ -452,6 +483,14 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
}
|
||||
|
||||
private void doSendPacket(Packet<?> packet, @Nullable PacketSendListener sendListener, boolean flush) {
|
||||
+ // Paper start - Optimize network
|
||||
+ final net.minecraft.server.level.ServerPlayer player = this.getPlayer();
|
||||
+ if (!this.isConnected()) {
|
||||
+ packet.onPacketDispatchFinish(player, null);
|
||||
+ return;
|
||||
+ }
|
||||
+ try {
|
||||
+ // Paper end - Optimize network
|
||||
ChannelFuture channelFuture = flush ? this.channel.writeAndFlush(packet) : this.channel.write(packet);
|
||||
if (sendListener != null) {
|
||||
channelFuture.addListener(future -> {
|
||||
@@ -467,14 +506,24 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
});
|
||||
}
|
||||
|
||||
+ // Paper start - Optimize network
|
||||
+ if (packet.hasFinishListener()) {
|
||||
+ channelFuture.addListener((ChannelFutureListener) future -> packet.onPacketDispatchFinish(player, future));
|
||||
+ }
|
||||
channelFuture.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
|
||||
+ } catch (final Exception e) {
|
||||
+ LOGGER.error("NetworkException: {}", player, e);
|
||||
+ this.disconnect(Component.translatable("disconnect.genericReason", "Internal Exception: " + e.getMessage()));
|
||||
+ packet.onPacketDispatchFinish(player, null);
|
||||
+ }
|
||||
+ // Paper end - Optimize network
|
||||
}
|
||||
|
||||
public void flushChannel() {
|
||||
if (this.isConnected()) {
|
||||
this.flush();
|
||||
} else {
|
||||
- this.pendingActions.add(Connection::flush);
|
||||
+ this.pendingActions.add(new WrappedConsumer(Connection::flush)); // Paper - Optimize network
|
||||
}
|
||||
}
|
||||
|
||||
@@ -486,16 +535,57 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
}
|
||||
}
|
||||
|
||||
- private void flushQueue() {
|
||||
- if (this.channel != null && this.channel.isOpen()) {
|
||||
+ // Paper start - Optimize network: Rewrite this to be safer if ran off main thread
|
||||
+ private boolean flushQueue() {
|
||||
+ if (!this.isConnected()) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ if (io.papermc.paper.util.MCUtil.isMainThread()) {
|
||||
+ return this.processQueue();
|
||||
+ } else if (this.isPending) {
|
||||
+ // Should only happen during login/status stages
|
||||
synchronized (this.pendingActions) {
|
||||
- Consumer<Connection> consumer;
|
||||
- while ((consumer = this.pendingActions.poll()) != null) {
|
||||
- consumer.accept(this);
|
||||
+ return this.processQueue();
|
||||
+ }
|
||||
+ }
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ private boolean processQueue() {
|
||||
+ if (this.pendingActions.isEmpty()) {
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ // If we are on main, we are safe here in that nothing else should be processing queue off main anymore
|
||||
+ // But if we are not on main due to login/status, the parent is synchronized on packetQueue
|
||||
+ final java.util.Iterator<WrappedConsumer> iterator = this.pendingActions.iterator();
|
||||
+ while (iterator.hasNext()) {
|
||||
+ final WrappedConsumer queued = iterator.next(); // poll -> peek
|
||||
+
|
||||
+ // Fix NPE (Spigot bug caused by handleDisconnection())
|
||||
+ if (queued == null) {
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ if (queued.isConsumed()) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (queued instanceof PacketSendAction packetSendAction) {
|
||||
+ final Packet<?> packet = packetSendAction.packet;
|
||||
+ if (!packet.isReady()) {
|
||||
+ return false;
|
||||
}
|
||||
}
|
||||
+
|
||||
+ iterator.remove();
|
||||
+ if (queued.tryMarkConsumed()) {
|
||||
+ queued.accept(this);
|
||||
+ }
|
||||
}
|
||||
+ return true;
|
||||
}
|
||||
+ // Paper end - Optimize network
|
||||
|
||||
private static final int MAX_PER_TICK = io.papermc.paper.configuration.GlobalConfiguration.get().misc.maxJoinsPerTick; // Paper - Buffer joins to world
|
||||
private static int joinAttemptsThisTick; // Paper - Buffer joins to world
|
||||
@@ -561,6 +651,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
// Spigot Start
|
||||
this.preparing = false;
|
||||
// Spigot End
|
||||
+ this.clearPacketQueue(); // Paper - Optimize network
|
||||
if (this.channel == null) {
|
||||
this.delayedDisconnect = disconnectionDetails;
|
||||
}
|
||||
@@ -749,7 +840,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
public void handleDisconnection() {
|
||||
if (this.channel != null && !this.channel.isOpen()) {
|
||||
if (this.disconnectionHandled) {
|
||||
- LOGGER.warn("handleDisconnection() called twice");
|
||||
+ // LOGGER.warn("handleDisconnection() called twice"); // Paper - Don't log useless message
|
||||
} else {
|
||||
this.disconnectionHandled = true;
|
||||
PacketListener packetListener = this.getPacketListener();
|
||||
@@ -760,7 +851,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
);
|
||||
packetListener1.onDisconnect(disconnectionDetails);
|
||||
}
|
||||
- this.pendingActions.clear(); // Free up packet queue.
|
||||
+ this.clearPacketQueue(); // Paper - Optimize network
|
||||
// Paper start - Add PlayerConnectionCloseEvent
|
||||
if (packetListener instanceof net.minecraft.server.network.ServerCommonPacketListenerImpl commonPacketListener) {
|
||||
/* Player was logged in, either game listener or configuration listener */
|
||||
@@ -795,4 +886,93 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
public void setBandwidthLogger(LocalSampleLogger bandwithLogger) {
|
||||
this.bandwidthDebugMonitor = new BandwidthDebugMonitor(bandwithLogger);
|
||||
}
|
||||
+
|
||||
+ // Paper start - Optimize network
|
||||
+ public void clearPacketQueue() {
|
||||
+ final net.minecraft.server.level.ServerPlayer player = getPlayer();
|
||||
+ for (final Consumer<Connection> queuedAction : this.pendingActions) {
|
||||
+ if (queuedAction instanceof PacketSendAction packetSendAction) {
|
||||
+ final Packet<?> packet = packetSendAction.packet;
|
||||
+ if (packet.hasFinishListener()) {
|
||||
+ packet.onPacketDispatchFinish(player, null);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ this.pendingActions.clear();
|
||||
+ }
|
||||
+
|
||||
+ private static class InnerUtil { // Attempt to hide these methods from ProtocolLib, so it doesn't accidently pick them up.
|
||||
+
|
||||
+ @Nullable
|
||||
+ private static java.util.List<Packet<?>> buildExtraPackets(final Packet<?> packet) {
|
||||
+ final java.util.List<Packet<?>> extra = packet.getExtraPackets();
|
||||
+ if (extra == null || extra.isEmpty()) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ final java.util.List<Packet<?>> ret = new java.util.ArrayList<>(1 + extra.size());
|
||||
+ buildExtraPackets0(extra, ret);
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ private static void buildExtraPackets0(final java.util.List<Packet<?>> extraPackets, final java.util.List<Packet<?>> into) {
|
||||
+ for (final Packet<?> extra : extraPackets) {
|
||||
+ into.add(extra);
|
||||
+ final java.util.List<Packet<?>> extraExtra = extra.getExtraPackets();
|
||||
+ if (extraExtra != null && !extraExtra.isEmpty()) {
|
||||
+ buildExtraPackets0(extraExtra, into);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private static boolean canSendImmediate(final Connection networkManager, final net.minecraft.network.protocol.Packet<?> packet) {
|
||||
+ return networkManager.isPending || networkManager.packetListener.protocol() != ConnectionProtocol.PLAY ||
|
||||
+ packet instanceof net.minecraft.network.protocol.common.ClientboundKeepAlivePacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundPlayerChatPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundSystemChatPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundCommandSuggestionsPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundSetTitleTextPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundSetSubtitleTextPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundSetActionBarTextPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundSetTitlesAnimationPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundClearTitlesPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundSoundPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundSoundEntityPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundStopSoundPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundBossEventPacket;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private static class WrappedConsumer implements Consumer<Connection> {
|
||||
+ private final Consumer<Connection> delegate;
|
||||
+ private final java.util.concurrent.atomic.AtomicBoolean consumed = new java.util.concurrent.atomic.AtomicBoolean(false);
|
||||
+
|
||||
+ private WrappedConsumer(final Consumer<Connection> delegate) {
|
||||
+ this.delegate = delegate;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void accept(final Connection connection) {
|
||||
+ this.delegate.accept(connection);
|
||||
+ }
|
||||
+
|
||||
+ public boolean tryMarkConsumed() {
|
||||
+ return consumed.compareAndSet(false, true);
|
||||
+ }
|
||||
+
|
||||
+ public boolean isConsumed() {
|
||||
+ return consumed.get();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private static final class PacketSendAction extends WrappedConsumer {
|
||||
+ private final Packet<?> packet;
|
||||
+
|
||||
+ private PacketSendAction(final Packet<?> packet, @Nullable final PacketSendListener packetSendListener, final boolean flush) {
|
||||
+ super(connection -> connection.sendPacket(packet, packetSendListener, flush));
|
||||
+ this.packet = packet;
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - Optimize network
|
||||
}
|
||||
diff --git a/net/minecraft/network/protocol/Packet.java b/net/minecraft/network/protocol/Packet.java
|
||||
index 65ff8b9112ec76eeac48c679044fc02ae7d4ffeb..e4789584cbe43959681a8522c66eab58369bebd0 100644
|
||||
--- a/net/minecraft/network/protocol/Packet.java
|
||||
+++ b/net/minecraft/network/protocol/Packet.java
|
||||
@@ -35,4 +35,32 @@ public interface Packet<T extends PacketListener> {
|
||||
static <B extends ByteBuf, T extends Packet<?>> StreamCodec<B, T> codec(StreamMemberEncoder<B, T> encoder, StreamDecoder<B, T> decoder) {
|
||||
return StreamCodec.ofMember(encoder, decoder);
|
||||
}
|
||||
+
|
||||
+ // Paper start
|
||||
+ /**
|
||||
+ * @param player Null if not at PLAY stage yet
|
||||
+ */
|
||||
+ default void onPacketDispatch(@org.jetbrains.annotations.Nullable net.minecraft.server.level.ServerPlayer player) {
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * @param player Null if not at PLAY stage yet
|
||||
+ * @param future Can be null if packet was cancelled
|
||||
+ */
|
||||
+ default void onPacketDispatchFinish(@org.jetbrains.annotations.Nullable net.minecraft.server.level.ServerPlayer player, @org.jetbrains.annotations.Nullable io.netty.channel.ChannelFuture future) {
|
||||
+ }
|
||||
+
|
||||
+ default boolean hasFinishListener() {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ default boolean isReady() {
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ @org.jetbrains.annotations.Nullable
|
||||
+ default java.util.List<Packet<?>> getExtraPackets() {
|
||||
+ return null;
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
diff --git a/net/minecraft/server/network/ServerConnectionListener.java b/net/minecraft/server/network/ServerConnectionListener.java
|
||||
index 18fa53903cd6500ae65d993a6fe7f49d6b069339..b68adf37af7172671163d4a8074d2bfa97724b4b 100644
|
||||
--- a/net/minecraft/server/network/ServerConnectionListener.java
|
||||
+++ b/net/minecraft/server/network/ServerConnectionListener.java
|
||||
@@ -66,11 +66,13 @@ public class ServerConnectionListener {
|
||||
|
||||
// Paper start - prevent blocking on adding a new connection while the server is ticking
|
||||
private final java.util.Queue<Connection> pending = new java.util.concurrent.ConcurrentLinkedQueue<>();
|
||||
+ private static final boolean disableFlushConsolidation = Boolean.getBoolean("Paper.disableFlushConsolidate"); // Paper - Optimize network
|
||||
|
||||
private final void addPending() {
|
||||
Connection connection;
|
||||
while ((connection = this.pending.poll()) != null) {
|
||||
this.connections.add(connection);
|
||||
+ connection.isPending = false; // Paper - Optimize network
|
||||
}
|
||||
}
|
||||
// Paper end - prevent blocking on adding a new connection while the server is ticking
|
||||
@@ -120,6 +122,7 @@ public class ServerConnectionListener {
|
||||
} catch (ChannelException var5) {
|
||||
}
|
||||
|
||||
+ if (!disableFlushConsolidation) channel.pipeline().addFirst(new io.netty.handler.flush.FlushConsolidationHandler()); // Paper - Optimize network
|
||||
ChannelPipeline channelPipeline = channel.pipeline().addLast("timeout", new ReadTimeoutHandler(30));
|
||||
if (ServerConnectionListener.this.server.repliesToStatus()) {
|
||||
channelPipeline.addLast("legacy_query", new LegacyQueryHandler(ServerConnectionListener.this.getServer()));
|
@@ -0,0 +1,189 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Fri, 15 Feb 2019 01:08:19 -0500
|
||||
Subject: [PATCH] Allow Saving of Oversized Chunks
|
||||
|
||||
The Minecraft World Region File format has a hard cap of 1MB per chunk.
|
||||
This is due to the fact that the header of the file format only allocates
|
||||
a single byte for sector count, meaning a maximum of 256 sectors, at 4k per sector.
|
||||
|
||||
This limit can be reached fairly easily with books, resulting in the chunk being unable
|
||||
to save to the world. Worse off, is that nothing printed when this occured, and silently
|
||||
performed a chunk rollback on next load.
|
||||
|
||||
This leads to security risk with duplication and is being actively exploited.
|
||||
|
||||
This patch catches the too large scenario, falls back and moves any large Entity
|
||||
or Tile Entity into a new compound, and this compound is saved into a different file.
|
||||
|
||||
On Chunk Load, we check for oversized status, and if so, we load the extra file and
|
||||
merge the Entities and Tile Entities from the oversized chunk back into the level to
|
||||
then be loaded as normal.
|
||||
|
||||
Once a chunk is returned back to normal size, the oversized flag will clear, and no
|
||||
extra data file will exist.
|
||||
|
||||
This fix maintains compatability with all existing Anvil Region Format tools as it
|
||||
does not alter the save format. They will just not know about the extra entities.
|
||||
|
||||
This fix also maintains compatability if someone switches server jars to one without
|
||||
this fix, as the data will remain in the oversized file. Once the server returns
|
||||
to a jar with this fix, the data will be restored.
|
||||
|
||||
diff --git a/net/minecraft/world/level/chunk/storage/RegionFile.java b/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
index 2b4fa89f15a42ddd627983fbd1377fb7c9864896..7491644233d52dc56d83de5ad3373f6a9a455378 100644
|
||||
--- a/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
@@ -54,6 +54,7 @@ public class RegionFile implements AutoCloseable {
|
||||
this.info = info;
|
||||
this.path = path;
|
||||
this.version = version;
|
||||
+ this.initOversizedState(); // Paper
|
||||
if (!Files.isDirectory(externalFileDir)) {
|
||||
throw new IllegalArgumentException("Expected directory, got " + externalFileDir.toAbsolutePath());
|
||||
} else {
|
||||
@@ -424,4 +425,75 @@ public class RegionFile implements AutoCloseable {
|
||||
interface CommitOp {
|
||||
void run() throws IOException;
|
||||
}
|
||||
+
|
||||
+ // Paper start
|
||||
+ private final byte[] oversized = new byte[1024];
|
||||
+ private int oversizedCount;
|
||||
+
|
||||
+ private synchronized void initOversizedState() throws IOException {
|
||||
+ Path metaFile = getOversizedMetaFile();
|
||||
+ if (Files.exists(metaFile)) {
|
||||
+ final byte[] read = java.nio.file.Files.readAllBytes(metaFile);
|
||||
+ System.arraycopy(read, 0, oversized, 0, oversized.length);
|
||||
+ for (byte temp : oversized) {
|
||||
+ oversizedCount += temp;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private static int getChunkIndex(int x, int z) {
|
||||
+ return (x & 31) + (z & 31) * 32;
|
||||
+ }
|
||||
+
|
||||
+ synchronized boolean isOversized(int x, int z) {
|
||||
+ return this.oversized[getChunkIndex(x, z)] == 1;
|
||||
+ }
|
||||
+
|
||||
+ synchronized void setOversized(int x, int z, boolean oversized) throws IOException {
|
||||
+ final int offset = getChunkIndex(x, z);
|
||||
+ boolean previous = this.oversized[offset] == 1;
|
||||
+ this.oversized[offset] = (byte) (oversized ? 1 : 0);
|
||||
+ if (!previous && oversized) {
|
||||
+ oversizedCount++;
|
||||
+ } else if (!oversized && previous) {
|
||||
+ oversizedCount--;
|
||||
+ }
|
||||
+ if (previous && !oversized) {
|
||||
+ Path oversizedFile = getOversizedFile(x, z);
|
||||
+ if (Files.exists(oversizedFile)) {
|
||||
+ Files.delete(oversizedFile);
|
||||
+ }
|
||||
+ }
|
||||
+ if (oversizedCount > 0) {
|
||||
+ if (previous != oversized) {
|
||||
+ writeOversizedMeta();
|
||||
+ }
|
||||
+ } else if (previous) {
|
||||
+ Path oversizedMetaFile = getOversizedMetaFile();
|
||||
+ if (Files.exists(oversizedMetaFile)) {
|
||||
+ Files.delete(oversizedMetaFile);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private void writeOversizedMeta() throws IOException {
|
||||
+ java.nio.file.Files.write(getOversizedMetaFile(), oversized);
|
||||
+ }
|
||||
+
|
||||
+ private Path getOversizedMetaFile() {
|
||||
+ return this.path.getParent().resolve(this.path.getFileName().toString().replaceAll("\\.mca$", "") + ".oversized.nbt");
|
||||
+ }
|
||||
+
|
||||
+ private Path getOversizedFile(int x, int z) {
|
||||
+ return this.path.getParent().resolve(this.path.getFileName().toString().replaceAll("\\.mca$", "") + "_oversized_" + x + "_" + z + ".nbt");
|
||||
+ }
|
||||
+
|
||||
+ synchronized net.minecraft.nbt.CompoundTag getOversizedData(int x, int z) throws IOException {
|
||||
+ Path file = getOversizedFile(x, z);
|
||||
+ try (DataInputStream out = new DataInputStream(new java.io.BufferedInputStream(new java.util.zip.InflaterInputStream(Files.newInputStream(file))))) {
|
||||
+ return net.minecraft.nbt.NbtIo.read((java.io.DataInput) out);
|
||||
+ }
|
||||
+
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
diff --git a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
index 5ac84d6b47e7fdc16e1c09b739829de3d316bf5b..51bf310423013d0ae9d3202d66e36a053a767197 100644
|
||||
--- a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
@@ -47,6 +47,43 @@ public final class RegionFileStorage implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ private static void printOversizedLog(String msg, Path file, int x, int z) {
|
||||
+ org.apache.logging.log4j.LogManager.getLogger().fatal(msg + " (" + file.toString().replaceAll(".+[\\\\/]", "") + " - " + x + "," + z + ") Go clean it up to remove this message. /minecraft:tp " + (x<<4)+" 128 "+(z<<4) + " - DO NOT REPORT THIS TO PAPER - You may ask for help on Discord, but do not file an issue. These error messages can not be removed.");
|
||||
+ }
|
||||
+
|
||||
+ private static CompoundTag readOversizedChunk(RegionFile regionfile, ChunkPos chunkCoordinate) throws IOException {
|
||||
+ synchronized (regionfile) {
|
||||
+ try (DataInputStream datainputstream = regionfile.getChunkDataInputStream(chunkCoordinate)) {
|
||||
+ CompoundTag oversizedData = regionfile.getOversizedData(chunkCoordinate.x, chunkCoordinate.z);
|
||||
+ CompoundTag chunk = NbtIo.read(datainputstream);
|
||||
+ if (oversizedData == null) {
|
||||
+ return chunk;
|
||||
+ }
|
||||
+ CompoundTag oversizedLevel = oversizedData.getCompound("Level");
|
||||
+
|
||||
+ mergeChunkList(chunk.getCompound("Level"), oversizedLevel, "Entities", "Entities");
|
||||
+ mergeChunkList(chunk.getCompound("Level"), oversizedLevel, "TileEntities", "TileEntities");
|
||||
+
|
||||
+ return chunk;
|
||||
+ } catch (Throwable throwable) {
|
||||
+ throwable.printStackTrace();
|
||||
+ throw throwable;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private static void mergeChunkList(CompoundTag level, CompoundTag oversizedLevel, String key, String oversizedKey) {
|
||||
+ net.minecraft.nbt.ListTag levelList = level.getList(key, net.minecraft.nbt.Tag.TAG_COMPOUND);
|
||||
+ net.minecraft.nbt.ListTag oversizedList = oversizedLevel.getList(oversizedKey, net.minecraft.nbt.Tag.TAG_COMPOUND);
|
||||
+
|
||||
+ if (!oversizedList.isEmpty()) {
|
||||
+ levelList.addAll(oversizedList);
|
||||
+ level.put(key, levelList);
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
@Nullable
|
||||
public CompoundTag read(ChunkPos chunkPos) throws IOException {
|
||||
// CraftBukkit start - SPIGOT-5680: There's no good reason to preemptively create files on read, save that for writing
|
||||
@@ -55,6 +92,12 @@ public final class RegionFileStorage implements AutoCloseable {
|
||||
return null;
|
||||
}
|
||||
// CraftBukkit end
|
||||
+ // Paper start
|
||||
+ if (regionFile.isOversized(chunkPos.x, chunkPos.z)) {
|
||||
+ printOversizedLog("Loading Oversized Chunk!", regionFile.getPath(), chunkPos.x, chunkPos.z);
|
||||
+ return readOversizedChunk(regionFile, chunkPos);
|
||||
+ }
|
||||
+ // Paper end
|
||||
|
||||
CompoundTag var4;
|
||||
try (DataInputStream chunkDataInputStream = regionFile.getChunkDataInputStream(chunkPos)) {
|
||||
@@ -90,6 +133,7 @@ public final class RegionFileStorage implements AutoCloseable {
|
||||
} else {
|
||||
try (DataOutputStream chunkDataOutputStream = regionFile.getChunkDataOutputStream(chunkPos)) {
|
||||
NbtIo.write(chunkData, chunkDataOutputStream);
|
||||
+ regionFile.setOversized(chunkPos.x, chunkPos.z, false); // Paper - We don't do this anymore, mojang stores differently, but clear old meta flag if it exists to get rid of our own meta file once last oversized is gone
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,857 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Fri, 13 May 2016 01:38:06 -0400
|
||||
Subject: [PATCH] Entity Activation Range 2.0
|
||||
|
||||
Optimizes performance of Activation Range
|
||||
|
||||
Adds many new configurations and a new wake up inactive system
|
||||
|
||||
Fixes and adds new Immunities to improve gameplay behavior
|
||||
|
||||
Adds water Mobs to activation range config and nerfs fish
|
||||
Adds flying monsters to control ghast and phantoms
|
||||
Adds villagers as separate config
|
||||
|
||||
diff --git a/io/papermc/paper/entity/activation/ActivationRange.java b/io/papermc/paper/entity/activation/ActivationRange.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..bd888ef719b9bfc93bace0b1d0fb771ac659f515
|
||||
--- /dev/null
|
||||
+++ b/io/papermc/paper/entity/activation/ActivationRange.java
|
||||
@@ -0,0 +1,318 @@
|
||||
+package io.papermc.paper.entity.activation;
|
||||
+
|
||||
+import net.minecraft.core.BlockPos;
|
||||
+import net.minecraft.server.MinecraftServer;
|
||||
+import net.minecraft.world.entity.Entity;
|
||||
+import net.minecraft.world.entity.ExperienceOrb;
|
||||
+import net.minecraft.world.entity.FlyingMob;
|
||||
+import net.minecraft.world.entity.LightningBolt;
|
||||
+import net.minecraft.world.entity.LivingEntity;
|
||||
+import net.minecraft.world.entity.Mob;
|
||||
+import net.minecraft.world.entity.ai.Brain;
|
||||
+import net.minecraft.world.entity.animal.Animal;
|
||||
+import net.minecraft.world.entity.animal.Bee;
|
||||
+import net.minecraft.world.entity.animal.Sheep;
|
||||
+import net.minecraft.world.entity.animal.horse.Llama;
|
||||
+import net.minecraft.world.entity.boss.EnderDragonPart;
|
||||
+import net.minecraft.world.entity.boss.enderdragon.EndCrystal;
|
||||
+import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
|
||||
+import net.minecraft.world.entity.boss.wither.WitherBoss;
|
||||
+import net.minecraft.world.entity.item.ItemEntity;
|
||||
+import net.minecraft.world.entity.item.PrimedTnt;
|
||||
+import net.minecraft.world.entity.monster.Creeper;
|
||||
+import net.minecraft.world.entity.monster.Pillager;
|
||||
+import net.minecraft.world.entity.npc.Villager;
|
||||
+import net.minecraft.world.entity.player.Player;
|
||||
+import net.minecraft.world.entity.projectile.AbstractArrow;
|
||||
+import net.minecraft.world.entity.projectile.AbstractHurtingProjectile;
|
||||
+import net.minecraft.world.entity.projectile.EyeOfEnder;
|
||||
+import net.minecraft.world.entity.projectile.FireworkRocketEntity;
|
||||
+import net.minecraft.world.entity.projectile.ThrowableProjectile;
|
||||
+import net.minecraft.world.entity.projectile.ThrownTrident;
|
||||
+import net.minecraft.world.entity.schedule.Activity;
|
||||
+import net.minecraft.world.level.Level;
|
||||
+import net.minecraft.world.phys.AABB;
|
||||
+import org.spigotmc.SpigotWorldConfig;
|
||||
+
|
||||
+public final class ActivationRange {
|
||||
+
|
||||
+ private ActivationRange() {
|
||||
+ }
|
||||
+
|
||||
+ static Activity[] VILLAGER_PANIC_IMMUNITIES = {
|
||||
+ Activity.HIDE,
|
||||
+ Activity.PRE_RAID,
|
||||
+ Activity.RAID,
|
||||
+ Activity.PANIC
|
||||
+ };
|
||||
+
|
||||
+ private static int checkInactiveWakeup(final Entity entity) {
|
||||
+ final Level world = entity.level();
|
||||
+ final SpigotWorldConfig config = world.spigotConfig;
|
||||
+ final long inactiveFor = MinecraftServer.currentTick - entity.activatedTick;
|
||||
+ if (entity.activationType == ActivationType.VILLAGER) {
|
||||
+ if (inactiveFor > config.wakeUpInactiveVillagersEvery && world.wakeupInactiveRemainingVillagers > 0) {
|
||||
+ world.wakeupInactiveRemainingVillagers--;
|
||||
+ return config.wakeUpInactiveVillagersFor;
|
||||
+ }
|
||||
+ } else if (entity.activationType == ActivationType.ANIMAL) {
|
||||
+ if (inactiveFor > config.wakeUpInactiveAnimalsEvery && world.wakeupInactiveRemainingAnimals > 0) {
|
||||
+ world.wakeupInactiveRemainingAnimals--;
|
||||
+ return config.wakeUpInactiveAnimalsFor;
|
||||
+ }
|
||||
+ } else if (entity.activationType == ActivationType.FLYING_MONSTER) {
|
||||
+ if (inactiveFor > config.wakeUpInactiveFlyingEvery && world.wakeupInactiveRemainingFlying > 0) {
|
||||
+ world.wakeupInactiveRemainingFlying--;
|
||||
+ return config.wakeUpInactiveFlyingFor;
|
||||
+ }
|
||||
+ } else if (entity.activationType == ActivationType.MONSTER || entity.activationType == ActivationType.RAIDER) {
|
||||
+ if (inactiveFor > config.wakeUpInactiveMonstersEvery && world.wakeupInactiveRemainingMonsters > 0) {
|
||||
+ world.wakeupInactiveRemainingMonsters--;
|
||||
+ return config.wakeUpInactiveMonstersFor;
|
||||
+ }
|
||||
+ }
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ static AABB maxBB = new AABB(0, 0, 0, 0, 0, 0);
|
||||
+
|
||||
+ /**
|
||||
+ * These entities are excluded from Activation range checks.
|
||||
+ *
|
||||
+ * @param entity
|
||||
+ * @param config
|
||||
+ * @return boolean If it should always tick.
|
||||
+ */
|
||||
+ public static boolean initializeEntityActivationState(final Entity entity, final SpigotWorldConfig config) {
|
||||
+ return (entity.activationType == ActivationType.MISC && config.miscActivationRange == 0)
|
||||
+ || (entity.activationType == ActivationType.RAIDER && config.raiderActivationRange == 0)
|
||||
+ || (entity.activationType == ActivationType.ANIMAL && config.animalActivationRange == 0)
|
||||
+ || (entity.activationType == ActivationType.MONSTER && config.monsterActivationRange == 0)
|
||||
+ || (entity.activationType == ActivationType.VILLAGER && config.villagerActivationRange <= 0)
|
||||
+ || (entity.activationType == ActivationType.WATER && config.waterActivationRange <= 0)
|
||||
+ || (entity.activationType == ActivationType.FLYING_MONSTER && config.flyingMonsterActivationRange <= 0)
|
||||
+ || entity instanceof EyeOfEnder
|
||||
+ || entity instanceof Player
|
||||
+ || entity instanceof ThrowableProjectile
|
||||
+ || entity instanceof EnderDragon
|
||||
+ || entity instanceof EnderDragonPart
|
||||
+ || entity instanceof WitherBoss
|
||||
+ || entity instanceof AbstractHurtingProjectile
|
||||
+ || entity instanceof LightningBolt
|
||||
+ || entity instanceof PrimedTnt
|
||||
+ || entity instanceof net.minecraft.world.entity.item.FallingBlockEntity
|
||||
+ || entity instanceof net.minecraft.world.entity.vehicle.AbstractMinecart
|
||||
+ || entity instanceof net.minecraft.world.entity.vehicle.AbstractBoat
|
||||
+ || entity instanceof EndCrystal
|
||||
+ || entity instanceof FireworkRocketEntity
|
||||
+ || entity instanceof ThrownTrident;
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Find what entities are in range of the players in the world and set
|
||||
+ * active if in range.
|
||||
+ *
|
||||
+ * @param world
|
||||
+ */
|
||||
+ public static void activateEntities(final Level world) {
|
||||
+ final int miscActivationRange = world.spigotConfig.miscActivationRange;
|
||||
+ final int raiderActivationRange = world.spigotConfig.raiderActivationRange;
|
||||
+ final int animalActivationRange = world.spigotConfig.animalActivationRange;
|
||||
+ final int monsterActivationRange = world.spigotConfig.monsterActivationRange;
|
||||
+ final int waterActivationRange = world.spigotConfig.waterActivationRange;
|
||||
+ final int flyingActivationRange = world.spigotConfig.flyingMonsterActivationRange;
|
||||
+ final int villagerActivationRange = world.spigotConfig.villagerActivationRange;
|
||||
+ world.wakeupInactiveRemainingAnimals = Math.min(world.wakeupInactiveRemainingAnimals + 1, world.spigotConfig.wakeUpInactiveAnimals);
|
||||
+ world.wakeupInactiveRemainingVillagers = Math.min(world.wakeupInactiveRemainingVillagers + 1, world.spigotConfig.wakeUpInactiveVillagers);
|
||||
+ world.wakeupInactiveRemainingMonsters = Math.min(world.wakeupInactiveRemainingMonsters + 1, world.spigotConfig.wakeUpInactiveMonsters);
|
||||
+ world.wakeupInactiveRemainingFlying = Math.min(world.wakeupInactiveRemainingFlying + 1, world.spigotConfig.wakeUpInactiveFlying);
|
||||
+
|
||||
+ int maxRange = Math.max(monsterActivationRange, animalActivationRange);
|
||||
+ maxRange = Math.max(maxRange, raiderActivationRange);
|
||||
+ maxRange = Math.max(maxRange, miscActivationRange);
|
||||
+ maxRange = Math.max(maxRange, flyingActivationRange);
|
||||
+ maxRange = Math.max(maxRange, waterActivationRange);
|
||||
+ maxRange = Math.max(maxRange, villagerActivationRange);
|
||||
+ maxRange = Math.min((world.spigotConfig.simulationDistance << 4) - 8, maxRange);
|
||||
+
|
||||
+ for (final Player player : world.players()) {
|
||||
+ player.activatedTick = MinecraftServer.currentTick;
|
||||
+ if (world.spigotConfig.ignoreSpectatorActivation && player.isSpectator()) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ final int worldHeight = world.getHeight();
|
||||
+ ActivationRange.maxBB = player.getBoundingBox().inflate(maxRange, worldHeight, maxRange);
|
||||
+ ActivationType.MISC.boundingBox = player.getBoundingBox().inflate(miscActivationRange, worldHeight, miscActivationRange);
|
||||
+ ActivationType.RAIDER.boundingBox = player.getBoundingBox().inflate(raiderActivationRange, worldHeight, raiderActivationRange);
|
||||
+ ActivationType.ANIMAL.boundingBox = player.getBoundingBox().inflate(animalActivationRange, worldHeight, animalActivationRange);
|
||||
+ ActivationType.MONSTER.boundingBox = player.getBoundingBox().inflate(monsterActivationRange, worldHeight, monsterActivationRange);
|
||||
+ ActivationType.WATER.boundingBox = player.getBoundingBox().inflate(waterActivationRange, worldHeight, waterActivationRange);
|
||||
+ ActivationType.FLYING_MONSTER.boundingBox = player.getBoundingBox().inflate(flyingActivationRange, worldHeight, flyingActivationRange);
|
||||
+ ActivationType.VILLAGER.boundingBox = player.getBoundingBox().inflate(villagerActivationRange, worldHeight, villagerActivationRange);
|
||||
+
|
||||
+ final java.util.List<Entity> entities = world.getEntities((Entity) null, ActivationRange.maxBB, e -> true);
|
||||
+ final boolean tickMarkers = world.paperConfig().entities.markers.tick;
|
||||
+ for (final Entity entity : entities) {
|
||||
+ if (!tickMarkers && entity instanceof net.minecraft.world.entity.Marker) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ ActivationRange.activateEntity(entity);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Tries to activate an entity.
|
||||
+ *
|
||||
+ * @param entity
|
||||
+ */
|
||||
+ private static void activateEntity(final Entity entity) {
|
||||
+ if (MinecraftServer.currentTick > entity.activatedTick) {
|
||||
+ if (entity.defaultActivationState) {
|
||||
+ entity.activatedTick = MinecraftServer.currentTick;
|
||||
+ return;
|
||||
+ }
|
||||
+ if (entity.activationType.boundingBox.intersects(entity.getBoundingBox())) {
|
||||
+ entity.activatedTick = MinecraftServer.currentTick;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * If an entity is not in range, do some more checks to see if we should
|
||||
+ * give it a shot.
|
||||
+ *
|
||||
+ * @param entity
|
||||
+ * @return
|
||||
+ */
|
||||
+ public static int checkEntityImmunities(final Entity entity) { // return # of ticks to get immunity
|
||||
+ final SpigotWorldConfig config = entity.level().spigotConfig;
|
||||
+ final int inactiveWakeUpImmunity = checkInactiveWakeup(entity);
|
||||
+ if (inactiveWakeUpImmunity > -1) {
|
||||
+ return inactiveWakeUpImmunity;
|
||||
+ }
|
||||
+ if (entity.getRemainingFireTicks() > 0) {
|
||||
+ return 2;
|
||||
+ }
|
||||
+ if (entity.activatedImmunityTick >= MinecraftServer.currentTick) {
|
||||
+ return 1;
|
||||
+ }
|
||||
+ final long inactiveFor = MinecraftServer.currentTick - entity.activatedTick;
|
||||
+ if ((entity.activationType != ActivationType.WATER && entity.isInWater() && entity.isPushedByFluid())) {
|
||||
+ return 100;
|
||||
+ }
|
||||
+ if (!entity.onGround() || entity.getDeltaMovement().horizontalDistanceSqr() > 9.999999747378752E-6D) {
|
||||
+ return 100;
|
||||
+ }
|
||||
+ if (!(entity instanceof final AbstractArrow arrow)) {
|
||||
+ if ((!entity.onGround() && !(entity instanceof FlyingMob))) {
|
||||
+ return 10;
|
||||
+ }
|
||||
+ } else if (!arrow.isInGround()) {
|
||||
+ return 1;
|
||||
+ }
|
||||
+ // special cases.
|
||||
+ if (entity instanceof final LivingEntity living) {
|
||||
+ if (living.onClimbable() || living.jumping || living.hurtTime > 0 || !living.activeEffects.isEmpty() || living.isFreezing()) {
|
||||
+ return 1;
|
||||
+ }
|
||||
+ if (entity instanceof final Mob mob && mob.getTarget() != null) {
|
||||
+ return 20;
|
||||
+ }
|
||||
+ if (entity instanceof final Bee bee) {
|
||||
+ final BlockPos movingTarget = bee.getMovingTarget();
|
||||
+ if (bee.isAngry() ||
|
||||
+ (bee.getHivePos() != null && bee.getHivePos().equals(movingTarget)) ||
|
||||
+ (bee.getSavedFlowerPos() != null && bee.getSavedFlowerPos().equals(movingTarget))
|
||||
+ ) {
|
||||
+ return 20;
|
||||
+ }
|
||||
+ }
|
||||
+ if (entity instanceof final Villager villager) {
|
||||
+ final Brain<Villager> behaviorController = villager.getBrain();
|
||||
+
|
||||
+ if (config.villagersActiveForPanic) {
|
||||
+ for (final Activity activity : VILLAGER_PANIC_IMMUNITIES) {
|
||||
+ if (behaviorController.isActive(activity)) {
|
||||
+ return 20 * 5;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (config.villagersWorkImmunityAfter > 0 && inactiveFor >= config.villagersWorkImmunityAfter) {
|
||||
+ if (behaviorController.isActive(Activity.WORK)) {
|
||||
+ return config.villagersWorkImmunityFor;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ if (entity instanceof final Llama llama && llama.inCaravan()) {
|
||||
+ return 1;
|
||||
+ }
|
||||
+ if (entity instanceof final Animal animal) {
|
||||
+ if (animal.isBaby() || animal.isInLove()) {
|
||||
+ return 5;
|
||||
+ }
|
||||
+ if (entity instanceof final Sheep sheep && sheep.isSheared()) {
|
||||
+ return 1;
|
||||
+ }
|
||||
+ }
|
||||
+ if (entity instanceof final Creeper creeper && creeper.isIgnited()) { // isExplosive
|
||||
+ return 20;
|
||||
+ }
|
||||
+ if (entity instanceof final Mob mob && mob.targetSelector.hasTasks()) {
|
||||
+ return 0;
|
||||
+ }
|
||||
+ if (entity instanceof final Pillager pillager) {
|
||||
+ // TODO:?
|
||||
+ }
|
||||
+ }
|
||||
+ // SPIGOT-6644: Otherwise the target refresh tick will be missed
|
||||
+ if (entity instanceof ExperienceOrb) {
|
||||
+ return 20;
|
||||
+ }
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Checks if the entity is active for this tick.
|
||||
+ *
|
||||
+ * @param entity
|
||||
+ * @return
|
||||
+ */
|
||||
+ public static boolean checkIfActive(final Entity entity) {
|
||||
+ // Never safe to skip fireworks or item gravity
|
||||
+ if (entity instanceof FireworkRocketEntity || (entity instanceof ItemEntity && (entity.tickCount + entity.getId()) % 4 == 0)) { // Needed for item gravity, see ItemEntity tick
|
||||
+ return true;
|
||||
+ }
|
||||
+ // special case always immunities
|
||||
+ // immunize brand-new entities, dead entities, and portal scenarios
|
||||
+ if (entity.defaultActivationState || entity.tickCount < 20 * 10 || !entity.isAlive() || (entity.portalProcess != null && !entity.portalProcess.hasExpired()) || entity.portalCooldown > 0) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ // immunize leashed entities
|
||||
+ if (entity instanceof final Mob mob && mob.getLeashHolder() instanceof Player) {
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ boolean isActive = entity.activatedTick >= MinecraftServer.currentTick;
|
||||
+ entity.isTemporarilyActive = false;
|
||||
+
|
||||
+ // Should this entity tick?
|
||||
+ if (!isActive) {
|
||||
+ if ((MinecraftServer.currentTick - entity.activatedTick - 1) % 20 == 0) {
|
||||
+ // Check immunities every 20 ticks.
|
||||
+ final int immunity = checkEntityImmunities(entity);
|
||||
+ if (immunity >= 0) {
|
||||
+ entity.activatedTick = MinecraftServer.currentTick + immunity;
|
||||
+ } else {
|
||||
+ entity.isTemporarilyActive = true;
|
||||
+ }
|
||||
+ isActive = true;
|
||||
+ }
|
||||
+ }
|
||||
+ // removed the original's dumb tick skipping for active entities
|
||||
+ return isActive;
|
||||
+ }
|
||||
+}
|
||||
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
|
||||
index 9bbcafa8e70f95d5ab6318211a0265acbfc76b1c..f8f145cd9614f7e38d6aa72501fe31837340a8bb 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;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Queues;
|
||||
-import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.ImmutableList.Builder;
|
||||
import com.mojang.datafixers.DataFixer;
|
||||
import com.mojang.logging.LogUtils;
|
||||
@@ -19,7 +18,6 @@ import it.unimi.dsi.fastutil.longs.LongIterator;
|
||||
import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
-import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.nio.file.Path;
|
||||
@@ -95,7 +93,6 @@ import net.minecraft.world.level.levelgen.structure.StructureStart;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import net.minecraft.world.level.storage.DimensionDataStorage;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
-import net.minecraft.world.phys.Vec3;
|
||||
import org.apache.commons.lang3.mutable.MutableBoolean;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index cce78d73e8adafd66d0f3ffb3fabb5e6c025c7df..a4b523ac1926895ccc87464892fa81753ae8f73c 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -551,6 +551,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
profilerFiller.pop();
|
||||
}
|
||||
|
||||
+ io.papermc.paper.entity.activation.ActivationRange.activateEntities(this); // Paper - EAR
|
||||
this.entityTickList
|
||||
.forEach(
|
||||
entity -> {
|
||||
@@ -960,16 +961,19 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
entity.tickCount++;
|
||||
profilerFiller.push(() -> BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString());
|
||||
profilerFiller.incrementCounter("tickNonPassenger");
|
||||
+ final boolean isActive = io.papermc.paper.entity.activation.ActivationRange.checkIfActive(entity); // Paper - EAR 2
|
||||
+ if (isActive) { // Paper - EAR 2
|
||||
entity.tick();
|
||||
entity.postTick(); // CraftBukkit
|
||||
+ } else {entity.inactiveTick();} // Paper - EAR 2
|
||||
profilerFiller.pop();
|
||||
|
||||
for (Entity entity1 : entity.getPassengers()) {
|
||||
- this.tickPassenger(entity, entity1);
|
||||
+ this.tickPassenger(entity, entity1, isActive); // Paper - EAR 2
|
||||
}
|
||||
}
|
||||
|
||||
- private void tickPassenger(Entity ridingEntity, Entity passengerEntity) {
|
||||
+ private void tickPassenger(Entity ridingEntity, Entity passengerEntity, final boolean isActive) { // Paper - EAR 2
|
||||
if (passengerEntity.isRemoved() || passengerEntity.getVehicle() != ridingEntity) {
|
||||
passengerEntity.stopRiding();
|
||||
} else if (passengerEntity instanceof Player || this.entityTickList.contains(passengerEntity)) {
|
||||
@@ -978,12 +982,21 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
ProfilerFiller profilerFiller = Profiler.get();
|
||||
profilerFiller.push(() -> BuiltInRegistries.ENTITY_TYPE.getKey(passengerEntity.getType()).toString());
|
||||
profilerFiller.incrementCounter("tickPassenger");
|
||||
+ // Paper start - EAR 2
|
||||
+ if (isActive) {
|
||||
passengerEntity.rideTick();
|
||||
passengerEntity.postTick(); // CraftBukkit
|
||||
+ } else {
|
||||
+ passengerEntity.setDeltaMovement(Vec3.ZERO);
|
||||
+ passengerEntity.inactiveTick();
|
||||
+ // copied from inside of if (isPassenger()) of passengerTick, but that ifPassenger is unnecessary
|
||||
+ ridingEntity.positionRider(passengerEntity);
|
||||
+ }
|
||||
+ // Paper end - EAR 2
|
||||
profilerFiller.pop();
|
||||
|
||||
for (Entity entity : passengerEntity.getPassengers()) {
|
||||
- this.tickPassenger(passengerEntity, entity);
|
||||
+ this.tickPassenger(passengerEntity, entity, isActive); // Paper - EAR 2
|
||||
}
|
||||
}
|
||||
}
|
||||
diff --git a/net/minecraft/world/entity/AgeableMob.java b/net/minecraft/world/entity/AgeableMob.java
|
||||
index a9f01e616ef6b0d74caf57cd68eb371a4fd30fd5..179f4e4b9b1eb57f78bbb2f9fa34b11ea79b7a88 100644
|
||||
--- a/net/minecraft/world/entity/AgeableMob.java
|
||||
+++ b/net/minecraft/world/entity/AgeableMob.java
|
||||
@@ -126,6 +126,23 @@ public abstract class AgeableMob extends PathfinderMob {
|
||||
super.onSyncedDataUpdated(key);
|
||||
}
|
||||
|
||||
+ // Paper start - EAR 2
|
||||
+ @Override
|
||||
+ public void inactiveTick() {
|
||||
+ super.inactiveTick();
|
||||
+ if (this.level().isClientSide || this.ageLocked) { // CraftBukkit
|
||||
+ this.refreshDimensions();
|
||||
+ } else {
|
||||
+ int age = this.getAge();
|
||||
+ if (age < 0) {
|
||||
+ this.setAge(++age);
|
||||
+ } else if (age > 0) {
|
||||
+ this.setAge(--age);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - EAR 2
|
||||
+
|
||||
@Override
|
||||
public void aiStep() {
|
||||
super.aiStep();
|
||||
diff --git a/net/minecraft/world/entity/AreaEffectCloud.java b/net/minecraft/world/entity/AreaEffectCloud.java
|
||||
index b4a1202a9f43525caf215d2f5c86ad92ea4f6de7..47db6ac3ef23fd0da127cfb5a4d3ba9ebd2ab54d 100644
|
||||
--- a/net/minecraft/world/entity/AreaEffectCloud.java
|
||||
+++ b/net/minecraft/world/entity/AreaEffectCloud.java
|
||||
@@ -128,6 +128,16 @@ public class AreaEffectCloud extends Entity implements TraceableEntity {
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
+ // Paper start - EAR 2
|
||||
+ @Override
|
||||
+ public void inactiveTick() {
|
||||
+ super.inactiveTick();
|
||||
+ if (this.tickCount >= this.waitTime + this.duration) {
|
||||
+ this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - EAR 2
|
||||
+
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index 020d05bb60abec10fa37e651c17f600c883af61d..2e5f1dc15ea6a20f8cbdef97ecbf00e5d56603cf 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -386,6 +386,15 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
public boolean fixedPose = false; // Paper - Expand Pose API
|
||||
private final int despawnTime; // Paper - entity despawn time limit
|
||||
public final io.papermc.paper.entity.activation.ActivationType activationType = io.papermc.paper.entity.activation.ActivationType.activationTypeFor(this); // Paper - EAR 2/tracking ranges
|
||||
+ // Paper start - EAR 2
|
||||
+ public final boolean defaultActivationState;
|
||||
+ public long activatedTick = Integer.MIN_VALUE;
|
||||
+ public boolean isTemporarilyActive;
|
||||
+ public long activatedImmunityTick = Integer.MIN_VALUE;
|
||||
+
|
||||
+ public void inactiveTick() {
|
||||
+ }
|
||||
+ // Paper end - EAR 2
|
||||
|
||||
public void setOrigin(@javax.annotation.Nonnull org.bukkit.Location location) {
|
||||
this.origin = location.toVector();
|
||||
@@ -423,6 +432,13 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
this.position = Vec3.ZERO;
|
||||
this.blockPosition = BlockPos.ZERO;
|
||||
this.chunkPosition = ChunkPos.ZERO;
|
||||
+ // Paper start - EAR 2
|
||||
+ if (level != null) {
|
||||
+ this.defaultActivationState = io.papermc.paper.entity.activation.ActivationRange.initializeEntityActivationState(this, level.spigotConfig);
|
||||
+ } else {
|
||||
+ this.defaultActivationState = false;
|
||||
+ }
|
||||
+ // Paper end - EAR 2
|
||||
SynchedEntityData.Builder builder = new SynchedEntityData.Builder(this);
|
||||
builder.define(DATA_SHARED_FLAGS_ID, (byte)0);
|
||||
builder.define(DATA_AIR_SUPPLY_ID, this.getMaxAirSupply());
|
||||
@@ -946,6 +962,8 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
} else {
|
||||
this.wasOnFire = this.isOnFire();
|
||||
if (type == MoverType.PISTON) {
|
||||
+ this.activatedTick = Math.max(this.activatedTick, MinecraftServer.currentTick + 20); // Paper - EAR 2
|
||||
+ this.activatedImmunityTick = Math.max(this.activatedImmunityTick, MinecraftServer.currentTick + 20); // Paper - EAR 2
|
||||
movement = this.limitPistonMovement(movement);
|
||||
if (movement.equals(Vec3.ZERO)) {
|
||||
return;
|
||||
@@ -959,6 +977,13 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
this.stuckSpeedMultiplier = Vec3.ZERO;
|
||||
this.setDeltaMovement(Vec3.ZERO);
|
||||
}
|
||||
+ // Paper start - ignore movement changes while inactive.
|
||||
+ if (isTemporarilyActive && !(this instanceof ItemEntity) && movement == getDeltaMovement() && type == MoverType.SELF) {
|
||||
+ setDeltaMovement(Vec3.ZERO);
|
||||
+ profilerFiller.pop();
|
||||
+ return;
|
||||
+ }
|
||||
+ // Paper end
|
||||
|
||||
movement = this.maybeBackOffFromEdge(movement, type);
|
||||
Vec3 vec3 = this.collide(movement);
|
||||
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
|
||||
index 41ef8c24903e5efceead43796e647824a54193df..9de400977ec33e485e87cdf1cf145588527e1e10 100644
|
||||
--- a/net/minecraft/world/entity/LivingEntity.java
|
||||
+++ b/net/minecraft/world/entity/LivingEntity.java
|
||||
@@ -3089,6 +3089,14 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
||||
return false;
|
||||
}
|
||||
|
||||
+ // Paper start - EAR 2
|
||||
+ @Override
|
||||
+ public void inactiveTick() {
|
||||
+ super.inactiveTick();
|
||||
+ ++this.noActionTime; // Above all the floats
|
||||
+ }
|
||||
+ // Paper end - EAR 2
|
||||
+
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java
|
||||
index f7d69db61d1293510428ae275e8a50571dde5ddf..1ed07fd23985a6bf8cf8300f74c92b7531a79fc6 100644
|
||||
--- a/net/minecraft/world/entity/Mob.java
|
||||
+++ b/net/minecraft/world/entity/Mob.java
|
||||
@@ -215,6 +215,19 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab
|
||||
return this.lookControl;
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ @Override
|
||||
+ public void inactiveTick() {
|
||||
+ super.inactiveTick();
|
||||
+ if (this.goalSelector.inactiveTick()) {
|
||||
+ this.goalSelector.tick();
|
||||
+ }
|
||||
+ if (this.targetSelector.inactiveTick()) {
|
||||
+ this.targetSelector.tick();
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
public MoveControl getMoveControl() {
|
||||
return this.getControlledVehicle() instanceof Mob mob ? mob.getMoveControl() : this.moveControl;
|
||||
}
|
||||
diff --git a/net/minecraft/world/entity/PathfinderMob.java b/net/minecraft/world/entity/PathfinderMob.java
|
||||
index 0caf50ec50f056b83a20bbc6a2fe0144593aef39..af59a700755654eb68d6bf57d0712c4a2ac6c09b 100644
|
||||
--- a/net/minecraft/world/entity/PathfinderMob.java
|
||||
+++ b/net/minecraft/world/entity/PathfinderMob.java
|
||||
@@ -17,6 +17,8 @@ public abstract class PathfinderMob extends Mob {
|
||||
super(entityType, level);
|
||||
}
|
||||
|
||||
+ public BlockPos movingTarget; public BlockPos getMovingTarget() { return movingTarget; } // Paper
|
||||
+
|
||||
public float getWalkTargetValue(BlockPos pos) {
|
||||
return this.getWalkTargetValue(pos, this.level());
|
||||
}
|
||||
diff --git a/net/minecraft/world/entity/ai/goal/GoalSelector.java b/net/minecraft/world/entity/ai/goal/GoalSelector.java
|
||||
index 9338e63cc28413f5559bb0122ef5e04a84bd51d1..eeba224bd575451ba6023df65ef9d9b97f7f1c71 100644
|
||||
--- a/net/minecraft/world/entity/ai/goal/GoalSelector.java
|
||||
+++ b/net/minecraft/world/entity/ai/goal/GoalSelector.java
|
||||
@@ -25,6 +25,7 @@ public class GoalSelector {
|
||||
private final Map<Goal.Flag, WrappedGoal> lockedFlags = new EnumMap<>(Goal.Flag.class);
|
||||
private final Set<WrappedGoal> availableGoals = new ObjectLinkedOpenHashSet<>();
|
||||
private final EnumSet<Goal.Flag> disabledFlags = EnumSet.noneOf(Goal.Flag.class);
|
||||
+ private int curRate; // Paper - EAR 2
|
||||
|
||||
public void addGoal(int priority, Goal goal) {
|
||||
this.availableGoals.add(new WrappedGoal(priority, goal));
|
||||
@@ -35,6 +36,22 @@ public class GoalSelector {
|
||||
this.availableGoals.removeIf(wrappedGoal -> filter.test(wrappedGoal.getGoal()));
|
||||
}
|
||||
|
||||
+ // Paper start - EAR 2
|
||||
+ public boolean inactiveTick() {
|
||||
+ this.curRate++;
|
||||
+ return this.curRate % 3 == 0; // TODO newGoalRate was already unused in 1.20.4, check if this is correct
|
||||
+ }
|
||||
+
|
||||
+ public boolean hasTasks() {
|
||||
+ for (WrappedGoal task : this.availableGoals) {
|
||||
+ if (task.isRunning()) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Paper end - EAR 2
|
||||
+
|
||||
public void removeGoal(Goal goal) {
|
||||
for (WrappedGoal wrappedGoal : this.availableGoals) {
|
||||
if (wrappedGoal.getGoal() == goal && wrappedGoal.isRunning()) {
|
||||
diff --git a/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java b/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
|
||||
index 789fea258d70e60d38271ebb31270562dc7eb3ab..d0ab3db7bbd2942db19f473474371b20ce822608 100644
|
||||
--- a/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
|
||||
+++ b/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
|
||||
@@ -23,6 +23,14 @@ public abstract class MoveToBlockGoal extends Goal {
|
||||
public MoveToBlockGoal(PathfinderMob mob, double speedModifier, int searchRange) {
|
||||
this(mob, speedModifier, searchRange, 1);
|
||||
}
|
||||
+ // Paper start - activation range improvements
|
||||
+ @Override
|
||||
+ public void stop() {
|
||||
+ super.stop();
|
||||
+ this.blockPos = BlockPos.ZERO;
|
||||
+ this.mob.movingTarget = null;
|
||||
+ }
|
||||
+ // Paper end
|
||||
|
||||
public MoveToBlockGoal(PathfinderMob mob, double speedModifier, int searchRange, int verticalSearchRange) {
|
||||
this.mob = mob;
|
||||
@@ -113,6 +121,7 @@ public abstract class MoveToBlockGoal extends Goal {
|
||||
mutableBlockPos.setWithOffset(blockPos, i4, i2 - 1, i5);
|
||||
if (this.mob.isWithinRestriction(mutableBlockPos) && this.isValidTarget(this.mob.level(), mutableBlockPos)) {
|
||||
this.blockPos = mutableBlockPos;
|
||||
+ this.mob.movingTarget = mutableBlockPos == BlockPos.ZERO ? null : mutableBlockPos.immutable(); // Paper
|
||||
return true;
|
||||
}
|
||||
}
|
||||
diff --git a/net/minecraft/world/entity/item/ItemEntity.java b/net/minecraft/world/entity/item/ItemEntity.java
|
||||
index 1c82a41acb8717b2c56498602fd1ecbe6aa58fe5..dcbd35d6bf81d7a0621020710114887b68a7dcc6 100644
|
||||
--- a/net/minecraft/world/entity/item/ItemEntity.java
|
||||
+++ b/net/minecraft/world/entity/item/ItemEntity.java
|
||||
@@ -124,6 +124,29 @@ public class ItemEntity extends Entity implements TraceableEntity {
|
||||
return 0.04;
|
||||
}
|
||||
|
||||
+ // Paper start - EAR 2
|
||||
+ @Override
|
||||
+ public void inactiveTick() {
|
||||
+ super.inactiveTick();
|
||||
+ if (this.pickupDelay > 0 && this.pickupDelay != 32767) {
|
||||
+ this.pickupDelay--;
|
||||
+ }
|
||||
+ if (this.age != -32768) {
|
||||
+ this.age++;
|
||||
+ }
|
||||
+
|
||||
+ if (!this.level().isClientSide && this.age >= this.despawnRate) {// Paper - Alternative item-despawn-rate
|
||||
+ // CraftBukkit start - fire ItemDespawnEvent
|
||||
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callItemDespawnEvent(this).isCancelled()) {
|
||||
+ this.age = 0;
|
||||
+ return;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+ this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - EAR 2
|
||||
+
|
||||
@Override
|
||||
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 27568a1604d2dd5d46e836bbc25431929e218aa1..2b83262e4a13eae86df82913ce4f3121e3631a43 100644
|
||||
--- a/net/minecraft/world/entity/npc/Villager.java
|
||||
+++ b/net/minecraft/world/entity/npc/Villager.java
|
||||
@@ -265,11 +265,35 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
||||
return this.assignProfessionWhenSpawned;
|
||||
}
|
||||
|
||||
+ // Paper start - EAR 2
|
||||
+ @Override
|
||||
+ public void inactiveTick() {
|
||||
+ // SPIGOT-3874, SPIGOT-3894, SPIGOT-3846, SPIGOT-5286 :(
|
||||
+ if (this.getUnhappyCounter() > 0) {
|
||||
+ this.setUnhappyCounter(this.getUnhappyCounter() - 1);
|
||||
+ }
|
||||
+ if (this.isEffectiveAi()) {
|
||||
+ if (this.level().spigotConfig.tickInactiveVillagers) {
|
||||
+ this.customServerAiStep(this.level().getMinecraftWorld());
|
||||
+ } else {
|
||||
+ this.customServerAiStep(this.level().getMinecraftWorld(), true);
|
||||
+ }
|
||||
+ }
|
||||
+ maybeDecayGossip();
|
||||
+ super.inactiveTick();
|
||||
+ }
|
||||
+ // Paper end - EAR 2
|
||||
+
|
||||
@Override
|
||||
protected void customServerAiStep(ServerLevel level) {
|
||||
+ // Paper start - EAR 2
|
||||
+ this.customServerAiStep(level, false);
|
||||
+ }
|
||||
+ protected void customServerAiStep(ServerLevel level, final boolean inactive) {
|
||||
+ // Paper end - EAR 2
|
||||
ProfilerFiller profilerFiller = Profiler.get();
|
||||
profilerFiller.push("villagerBrain");
|
||||
- this.getBrain().tick(level, this);
|
||||
+ if (!inactive) this.getBrain().tick(level, this); // Paper - EAR 2
|
||||
profilerFiller.pop();
|
||||
if (this.assignProfessionWhenSpawned) {
|
||||
this.assignProfessionWhenSpawned = false;
|
||||
@@ -293,7 +317,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
||||
this.lastTradedPlayer = null;
|
||||
}
|
||||
|
||||
- if (!this.isNoAi() && this.random.nextInt(100) == 0) {
|
||||
+ if (!inactive && !this.isNoAi() && this.random.nextInt(100) == 0) { // Paper - EAR 2
|
||||
Raid raidAt = level.getRaidAt(this.blockPosition());
|
||||
if (raidAt != null && raidAt.isActive() && !raidAt.isOver()) {
|
||||
level.broadcastEntityEvent(this, (byte)42);
|
||||
@@ -303,6 +327,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
||||
if (this.getVillagerData().getProfession() == VillagerProfession.NONE && this.isTrading()) {
|
||||
this.stopTrading();
|
||||
}
|
||||
+ if (inactive) return; // Paper - EAR 2
|
||||
|
||||
super.customServerAiStep(level);
|
||||
}
|
||||
diff --git a/net/minecraft/world/entity/projectile/Arrow.java b/net/minecraft/world/entity/projectile/Arrow.java
|
||||
index c1e09e701757a300183b62d343ded03033e63aa7..56574f8ef879159edc0114da09300143a2c79a35 100644
|
||||
--- a/net/minecraft/world/entity/projectile/Arrow.java
|
||||
+++ b/net/minecraft/world/entity/projectile/Arrow.java
|
||||
@@ -66,6 +66,16 @@ public class Arrow extends AbstractArrow {
|
||||
builder.define(ID_EFFECT_COLOR, -1);
|
||||
}
|
||||
|
||||
+ // Paper start - EAR 2
|
||||
+ @Override
|
||||
+ public void inactiveTick() {
|
||||
+ if (this.isInGround()) {
|
||||
+ this.life++;
|
||||
+ }
|
||||
+ super.inactiveTick();
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
diff --git a/net/minecraft/world/entity/projectile/FireworkRocketEntity.java b/net/minecraft/world/entity/projectile/FireworkRocketEntity.java
|
||||
index 7c0862c50b44555fb27ce7dc46f4ec95a3eb0022..774ca9e0b56fd175ae246051de762d0c4256ca58 100644
|
||||
--- a/net/minecraft/world/entity/projectile/FireworkRocketEntity.java
|
||||
+++ b/net/minecraft/world/entity/projectile/FireworkRocketEntity.java
|
||||
@@ -102,6 +102,21 @@ public class FireworkRocketEntity extends Projectile implements ItemSupplier {
|
||||
return super.shouldRender(x, y, z) && !this.isAttachedToEntity();
|
||||
}
|
||||
|
||||
+ // Paper start - EAR 2
|
||||
+ @Override
|
||||
+ public void inactiveTick() {
|
||||
+ this.life++;
|
||||
+ if (this.life > this.lifetime && this.level() instanceof ServerLevel serverLevel) {
|
||||
+ // CraftBukkit start
|
||||
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callFireworkExplodeEvent(this).isCancelled()) {
|
||||
+ this.explode(serverLevel);
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+ }
|
||||
+ super.inactiveTick();
|
||||
+ }
|
||||
+ // Paper end - EAR 2
|
||||
+
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
diff --git a/net/minecraft/world/entity/vehicle/MinecartHopper.java b/net/minecraft/world/entity/vehicle/MinecartHopper.java
|
||||
index c553cf0592dfa606dbbb1e6854d3377b9feb5efb..dec705ec57e4f63ef2ccaa87c5400c116aee9b35 100644
|
||||
--- a/net/minecraft/world/entity/vehicle/MinecartHopper.java
|
||||
+++ b/net/minecraft/world/entity/vehicle/MinecartHopper.java
|
||||
@@ -47,6 +47,7 @@ public class MinecartHopper extends AbstractMinecartContainer implements Hopper
|
||||
if (flag != this.isEnabled()) {
|
||||
this.setEnabled(flag);
|
||||
}
|
||||
+ this.immunize(); // Paper
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
@@ -100,11 +101,13 @@ public class MinecartHopper extends AbstractMinecartContainer implements Hopper
|
||||
|
||||
public boolean suckInItems() {
|
||||
if (HopperBlockEntity.suckInItems(this.level(), this)) {
|
||||
+ this.immunize(); // Paper
|
||||
return true;
|
||||
} else {
|
||||
for (ItemEntity itemEntity : this.level()
|
||||
.getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate(0.25, 0.0, 0.25), EntitySelector.ENTITY_STILL_ALIVE)) {
|
||||
if (HopperBlockEntity.addItem(this, itemEntity)) {
|
||||
+ this.immunize(); // Paper
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -139,4 +142,11 @@ public class MinecartHopper extends AbstractMinecartContainer implements Hopper
|
||||
public AbstractContainerMenu createMenu(int id, Inventory playerInventory) {
|
||||
return new HopperMenu(id, playerInventory, this);
|
||||
}
|
||||
+
|
||||
+ // Paper start
|
||||
+ public void immunize() {
|
||||
+ this.activatedImmunityTick = Math.max(this.activatedImmunityTick, net.minecraft.server.MinecraftServer.currentTick + 20);
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
}
|
||||
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
|
||||
index 691fee2e2097244126f4fac0f5d00bf6916b9766..25fb8a91bd3012da383711d58cc25cbada510f56 100644
|
||||
--- a/net/minecraft/world/level/Level.java
|
||||
+++ b/net/minecraft/world/level/Level.java
|
||||
@@ -153,6 +153,12 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
||||
public Map<BlockPos, BlockEntity> capturedTileEntities = new java.util.LinkedHashMap<>(); // Paper - Retain block place order when capturing blockstates
|
||||
public List<net.minecraft.world.entity.item.ItemEntity> captureDrops;
|
||||
public final it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap<SpawnCategory> ticksPerSpawnCategory = new it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap<>();
|
||||
+ // Paper start
|
||||
+ public int wakeupInactiveRemainingAnimals;
|
||||
+ public int wakeupInactiveRemainingFlying;
|
||||
+ public int wakeupInactiveRemainingMonsters;
|
||||
+ public int wakeupInactiveRemainingVillagers;
|
||||
+ // Paper end
|
||||
public boolean populating;
|
||||
public final org.spigotmc.SpigotWorldConfig spigotConfig; // Spigot
|
||||
// Paper start - add paper world config
|
||||
diff --git a/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java b/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
|
||||
index 754cfdcd5a28287aa3545aaffdce1e391cbefc1e..1e6e940fca9d96ef410c7bf05524bd9b24db4a79 100644
|
||||
--- a/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
|
||||
+++ b/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
|
||||
@@ -149,6 +149,10 @@ public class PistonMovingBlockEntity extends BlockEntity {
|
||||
}
|
||||
|
||||
entity.setDeltaMovement(d1, d2, d3);
|
||||
+ // Paper - EAR items stuck in slime pushed by a piston
|
||||
+ entity.activatedTick = Math.max(entity.activatedTick, net.minecraft.server.MinecraftServer.currentTick + 10);
|
||||
+ entity.activatedImmunityTick = Math.max(entity.activatedImmunityTick, net.minecraft.server.MinecraftServer.currentTick + 10);
|
||||
+ // Paper end
|
||||
break;
|
||||
}
|
||||
}
|
636
paper-server/patches/features/0006-Anti-Xray.patch
Normal file
636
paper-server/patches/features/0006-Anti-Xray.patch
Normal file
@@ -0,0 +1,636 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: stonar96 <minecraft.stonar96@gmail.com>
|
||||
Date: Thu, 25 Nov 2021 13:27:51 +0100
|
||||
Subject: [PATCH] Anti-Xray
|
||||
|
||||
|
||||
diff --git a/io/papermc/paper/FeatureHooks.java b/io/papermc/paper/FeatureHooks.java
|
||||
index aad9d9687dffb872a12ba0ba39d674895b7474e7..764daee7cd619c56314bcea9a4c35702afcb262d 100644
|
||||
--- a/io/papermc/paper/FeatureHooks.java
|
||||
+++ b/io/papermc/paper/FeatureHooks.java
|
||||
@@ -7,6 +7,7 @@ import it.unimi.dsi.fastutil.longs.LongSets;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectSets;
|
||||
+import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -35,20 +36,25 @@ public final class FeatureHooks {
|
||||
}
|
||||
|
||||
public static LevelChunkSection createSection(final Registry<Biome> biomeRegistry, final Level level, final ChunkPos chunkPos, final int chunkSection) {
|
||||
- return new LevelChunkSection(biomeRegistry);
|
||||
+ return new LevelChunkSection(biomeRegistry, level, chunkPos, chunkSection); // Paper - Anti-Xray - Add parameters
|
||||
}
|
||||
|
||||
public static void sendChunkRefreshPackets(final List<ServerPlayer> playersInRange, final LevelChunk chunk) {
|
||||
- final ClientboundLevelChunkWithLightPacket refreshPacket = new ClientboundLevelChunkWithLightPacket(chunk, chunk.level.getLightEngine(), null, null);
|
||||
+ // Paper start - Anti-Xray
|
||||
+ final Map<Object, ClientboundLevelChunkWithLightPacket> refreshPackets = new HashMap<>();
|
||||
for (final ServerPlayer player : playersInRange) {
|
||||
if (player.connection == null) continue;
|
||||
|
||||
- player.connection.send(refreshPacket);
|
||||
+ final Boolean shouldModify = chunk.getLevel().chunkPacketBlockController.shouldModify(player, chunk);
|
||||
+ player.connection.send(refreshPackets.computeIfAbsent(shouldModify, s -> { // Use connection to prevent creating firing event
|
||||
+ return new ClientboundLevelChunkWithLightPacket(chunk, chunk.level.getLightEngine(), null, null, (Boolean) s);
|
||||
+ }));
|
||||
}
|
||||
+ // Paper end - Anti-Xray
|
||||
}
|
||||
|
||||
public static PalettedContainer<BlockState> emptyPalettedBlockContainer() {
|
||||
- return new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
|
||||
+ return new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, null); // Paper - Anti-Xray - Add preset block states
|
||||
}
|
||||
|
||||
public static Set<Long> getSentChunkKeys(final ServerPlayer player) {
|
||||
@@ -74,4 +80,4 @@ public final class FeatureHooks {
|
||||
public static boolean isSpiderCollidingWithWorldBorder(final Spider spider) {
|
||||
return true; // ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.isCollidingWithBorder(spider.level().getWorldBorder(), spider.getBoundingBox().inflate(ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON))
|
||||
}
|
||||
-}
|
||||
\ No newline at end of file
|
||||
+}
|
||||
diff --git a/net/minecraft/network/protocol/game/ClientboundChunksBiomesPacket.java b/net/minecraft/network/protocol/game/ClientboundChunksBiomesPacket.java
|
||||
index d4872b7f4e9591b3b1c67406312905851303f521..cb41460e94161675e2ab43f4b1b5286ee38e2e13 100644
|
||||
--- a/net/minecraft/network/protocol/game/ClientboundChunksBiomesPacket.java
|
||||
+++ b/net/minecraft/network/protocol/game/ClientboundChunksBiomesPacket.java
|
||||
@@ -70,8 +70,10 @@ public record ClientboundChunksBiomesPacket(List<ClientboundChunksBiomesPacket.C
|
||||
}
|
||||
|
||||
public static void extractChunkData(FriendlyByteBuf buffer, LevelChunk chunk) {
|
||||
+ int chunkSectionIndex = 0; // Paper - Anti-Xray
|
||||
for (LevelChunkSection levelChunkSection : chunk.getSections()) {
|
||||
- levelChunkSection.getBiomes().write(buffer);
|
||||
+ levelChunkSection.getBiomes().write(buffer, null, chunkSectionIndex); // Paper - Anti-Xray
|
||||
+ chunkSectionIndex++; // Paper - Anti-Xray
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
||||
index 5d1943d37dfad0c12e77179f0866851532d983e9..3aea76690bc3e35758d3bf274777130af17d8a0f 100644
|
||||
--- a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
||||
+++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
||||
@@ -28,7 +28,13 @@ public class ClientboundLevelChunkPacketData {
|
||||
private final byte[] buffer;
|
||||
private final List<ClientboundLevelChunkPacketData.BlockEntityInfo> blockEntitiesData;
|
||||
|
||||
+ // Paper start - Anti-Xray - Add chunk packet info
|
||||
+ @Deprecated @io.papermc.paper.annotation.DoNotUse
|
||||
public ClientboundLevelChunkPacketData(LevelChunk levelChunk) {
|
||||
+ this(levelChunk, null);
|
||||
+ }
|
||||
+ public ClientboundLevelChunkPacketData(LevelChunk levelChunk, io.papermc.paper.antixray.ChunkPacketInfo<net.minecraft.world.level.block.state.BlockState> chunkPacketInfo) {
|
||||
+ // Paper end
|
||||
this.heightmaps = new CompoundTag();
|
||||
|
||||
for (Entry<Heightmap.Types, Heightmap> entry : levelChunk.getHeightmaps()) {
|
||||
@@ -38,7 +44,11 @@ public class ClientboundLevelChunkPacketData {
|
||||
}
|
||||
|
||||
this.buffer = new byte[calculateChunkSize(levelChunk)];
|
||||
- extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), levelChunk);
|
||||
+ // Paper start - Anti-Xray - Add chunk packet info
|
||||
+ if (chunkPacketInfo != null) {
|
||||
+ chunkPacketInfo.setBuffer(this.buffer);
|
||||
+ }
|
||||
+ extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), levelChunk, chunkPacketInfo);
|
||||
this.blockEntitiesData = Lists.newArrayList();
|
||||
|
||||
for (Entry<BlockPos, BlockEntity> entryx : levelChunk.getBlockEntities().entrySet()) {
|
||||
@@ -85,9 +95,17 @@ public class ClientboundLevelChunkPacketData {
|
||||
return byteBuf;
|
||||
}
|
||||
|
||||
+ // Paper start - Anti-Xray - Add chunk packet info
|
||||
+ @Deprecated @io.papermc.paper.annotation.DoNotUse
|
||||
public static void extractChunkData(FriendlyByteBuf buffer, LevelChunk chunk) {
|
||||
+ ClientboundLevelChunkPacketData.extractChunkData(buffer, chunk, null);
|
||||
+ }
|
||||
+ public static void extractChunkData(FriendlyByteBuf buffer, LevelChunk chunk, io.papermc.paper.antixray.ChunkPacketInfo<net.minecraft.world.level.block.state.BlockState> chunkPacketInfo) {
|
||||
+ int chunkSectionIndex = 0;
|
||||
for (LevelChunkSection levelChunkSection : chunk.getSections()) {
|
||||
- levelChunkSection.write(buffer);
|
||||
+ levelChunkSection.write(buffer, chunkPacketInfo, chunkSectionIndex);
|
||||
+ chunkSectionIndex++;
|
||||
+ // Paper end - Anti-Xray - Add chunk packet info
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java b/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
|
||||
index aadf2dccb996e422cacf8bb510cc642e69ee4972..d2d21fe8d7275b01454e09be252d7dd7710cdc2d 100644
|
||||
--- a/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
|
||||
+++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
|
||||
@@ -18,13 +18,31 @@ public class ClientboundLevelChunkWithLightPacket implements Packet<ClientGamePa
|
||||
private final int z;
|
||||
private final ClientboundLevelChunkPacketData chunkData;
|
||||
private final ClientboundLightUpdatePacketData lightData;
|
||||
+ // Paper start - Async-Anti-Xray - Ready flag for the connection, add chunk packet info
|
||||
+ private volatile boolean ready;
|
||||
|
||||
+ @Override
|
||||
+ public boolean isReady() {
|
||||
+ return this.ready;
|
||||
+ }
|
||||
+
|
||||
+ public void setReady(boolean ready) {
|
||||
+ this.ready = ready;
|
||||
+ }
|
||||
+
|
||||
+ @Deprecated @io.papermc.paper.annotation.DoNotUse
|
||||
public ClientboundLevelChunkWithLightPacket(LevelChunk chunk, LevelLightEngine lightEngine, @Nullable BitSet skyLight, @Nullable BitSet blockLight) {
|
||||
+ this(chunk, lightEngine, skyLight, blockLight, true);
|
||||
+ }
|
||||
+ public ClientboundLevelChunkWithLightPacket(LevelChunk chunk, LevelLightEngine lightEngine, @Nullable BitSet skyLight, @Nullable BitSet blockLight, boolean modifyBlocks) {
|
||||
+ // Paper end - Anti-Xray
|
||||
ChunkPos pos = chunk.getPos();
|
||||
this.x = pos.x;
|
||||
this.z = pos.z;
|
||||
- this.chunkData = new ClientboundLevelChunkPacketData(chunk);
|
||||
+ io.papermc.paper.antixray.ChunkPacketInfo<net.minecraft.world.level.block.state.BlockState> chunkPacketInfo = modifyBlocks ? chunk.getLevel().chunkPacketBlockController.getChunkPacketInfo(this, chunk) : null; // Paper - Ant-Xray
|
||||
+ this.chunkData = new ClientboundLevelChunkPacketData(chunk, chunkPacketInfo); // Paper - Anti-Xray
|
||||
this.lightData = new ClientboundLightUpdatePacketData(pos, lightEngine, skyLight, blockLight);
|
||||
+ chunk.getLevel().chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo); // Paper - Anti-Xray - Modify blocks
|
||||
}
|
||||
|
||||
private ClientboundLevelChunkWithLightPacket(RegistryFriendlyByteBuf buffer) {
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index a4b523ac1926895ccc87464892fa81753ae8f73c..ca9427a7eae9a66f4f1ccedda7b1def7ac2a88da 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -348,7 +348,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
org.bukkit.generator.BiomeProvider biomeProvider // CraftBukkit
|
||||
) {
|
||||
// CraftBukkit start
|
||||
- super(serverLevelData, dimension, server.registryAccess(), levelStem.type(), false, isDebug, biomeZoomSeed, server.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> server.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(levelStorageAccess.levelDirectory.path(), serverLevelData.getLevelName(), dimension.location(), spigotConfig, server.registryAccess(), serverLevelData.getGameRules()))); // Paper - create paper world configs
|
||||
+ super(serverLevelData, dimension, server.registryAccess(), levelStem.type(), false, isDebug, biomeZoomSeed, server.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> server.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(levelStorageAccess.levelDirectory.path(), serverLevelData.getLevelName(), dimension.location(), spigotConfig, server.registryAccess(), serverLevelData.getGameRules())), dispatcher); // Paper - create paper world configs; Async-Anti-Xray: Pass executor
|
||||
this.pvpMode = server.isPvpAllowed();
|
||||
this.levelStorageAccess = levelStorageAccess;
|
||||
this.uuid = org.bukkit.craftbukkit.util.WorldUUID.getUUID(levelStorageAccess.levelDirectory.path().toFile());
|
||||
diff --git a/net/minecraft/server/level/ServerPlayerGameMode.java b/net/minecraft/server/level/ServerPlayerGameMode.java
|
||||
index d6a493de667bfe97b722efe40d1530bdb666bb94..bb352ad40da33a9d411836d4fab2664e226a6e38 100644
|
||||
--- a/net/minecraft/server/level/ServerPlayerGameMode.java
|
||||
+++ b/net/minecraft/server/level/ServerPlayerGameMode.java
|
||||
@@ -28,7 +28,7 @@ import org.slf4j.Logger;
|
||||
|
||||
public class ServerPlayerGameMode {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
- protected ServerLevel level;
|
||||
+ public ServerLevel level; // Paper - Anti-Xray - protected -> public
|
||||
protected final ServerPlayer player;
|
||||
private GameType gameModeForPlayer = GameType.DEFAULT_MODE;
|
||||
@Nullable
|
||||
@@ -312,6 +312,7 @@ public class ServerPlayerGameMode {
|
||||
org.bukkit.craftbukkit.event.CraftEventFactory.callBlockDamageAbortEvent(this.player, pos, this.player.getInventory().getSelected()); // CraftBukkit
|
||||
}
|
||||
}
|
||||
+ this.level.chunkPacketBlockController.onPlayerLeftClickBlock(this, pos, action, face, maxBuildHeight, sequence); // Paper - Anti-Xray
|
||||
}
|
||||
|
||||
public void destroyAndAck(BlockPos pos, int sequence, String message) {
|
||||
diff --git a/net/minecraft/server/network/PlayerChunkSender.java b/net/minecraft/server/network/PlayerChunkSender.java
|
||||
index 342bc843c384761e883de861044f4f8930ae8763..14878690a88fd4de3e2c127086607e6c819c636c 100644
|
||||
--- a/net/minecraft/server/network/PlayerChunkSender.java
|
||||
+++ b/net/minecraft/server/network/PlayerChunkSender.java
|
||||
@@ -78,8 +78,11 @@ public class PlayerChunkSender {
|
||||
}
|
||||
}
|
||||
|
||||
- private static void sendChunk(ServerGamePacketListenerImpl packetListener, ServerLevel level, LevelChunk chunk) {
|
||||
- packetListener.send(new ClientboundLevelChunkWithLightPacket(chunk, level.getLightEngine(), null, null));
|
||||
+ // Paper start - Anti-Xray
|
||||
+ public static void sendChunk(ServerGamePacketListenerImpl packetListener, ServerLevel level, LevelChunk chunk) {
|
||||
+ final boolean shouldModify = level.chunkPacketBlockController.shouldModify(packetListener.player, chunk);
|
||||
+ packetListener.send(new ClientboundLevelChunkWithLightPacket(chunk, level.getLightEngine(), null, null, shouldModify));
|
||||
+ // Paper end - Anti-Xray
|
||||
// Paper start - PlayerChunkLoadEvent
|
||||
if (io.papermc.paper.event.packet.PlayerChunkLoadEvent.getHandlerList().getRegisteredListeners().length > 0) {
|
||||
new io.papermc.paper.event.packet.PlayerChunkLoadEvent(new org.bukkit.craftbukkit.CraftChunk(chunk), packetListener.getPlayer().getBukkitEntity()).callEvent();
|
||||
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
|
||||
index 2580b249d9a024400e295ca5beab551b3571ceb3..d227714de0fe13544779fae6cf0e9ff6af5469c7 100644
|
||||
--- a/net/minecraft/server/players/PlayerList.java
|
||||
+++ b/net/minecraft/server/players/PlayerList.java
|
||||
@@ -404,7 +404,7 @@ public abstract class PlayerList {
|
||||
.getOrThrow(net.minecraft.world.level.biome.Biomes.PLAINS);
|
||||
player.connection.send(new net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket(
|
||||
new net.minecraft.world.level.chunk.EmptyLevelChunk(serverLevel, player.chunkPosition(), plains),
|
||||
- serverLevel.getLightEngine(), (java.util.BitSet)null, (java.util.BitSet) null)
|
||||
+ serverLevel.getLightEngine(), (java.util.BitSet)null, (java.util.BitSet) null, true) // Paper - Anti-Xray
|
||||
);
|
||||
}
|
||||
// Paper end - Send empty chunk
|
||||
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
|
||||
index 25fb8a91bd3012da383711d58cc25cbada510f56..872c3b8826f436b15f6ab0a3619692c5202eadc3 100644
|
||||
--- a/net/minecraft/world/level/Level.java
|
||||
+++ b/net/minecraft/world/level/Level.java
|
||||
@@ -168,6 +168,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
||||
}
|
||||
// Paper end - add paper world config
|
||||
|
||||
+ public final io.papermc.paper.antixray.ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray
|
||||
public static BlockPos lastPhysicsProblem; // Spigot
|
||||
private org.spigotmc.TickLimiter entityLimiter;
|
||||
private org.spigotmc.TickLimiter tileLimiter;
|
||||
@@ -214,7 +215,8 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
||||
org.bukkit.generator.BiomeProvider biomeProvider, // CraftBukkit
|
||||
org.bukkit.World.Environment env, // CraftBukkit
|
||||
java.util.function.Function<org.spigotmc.SpigotWorldConfig, // Spigot - create per world config
|
||||
- io.papermc.paper.configuration.WorldConfiguration> paperWorldConfigCreator // Paper - create paper world config
|
||||
+ io.papermc.paper.configuration.WorldConfiguration> paperWorldConfigCreator, // Paper - create paper world config
|
||||
+ java.util.concurrent.Executor executor // Paper - Anti-Xray
|
||||
) {
|
||||
this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) levelData).getLevelName()); // Spigot
|
||||
this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config
|
||||
@@ -295,6 +297,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
||||
// CraftBukkit end
|
||||
this.entityLimiter = new org.spigotmc.TickLimiter(this.spigotConfig.entityMaxTickTime);
|
||||
this.tileLimiter = new org.spigotmc.TickLimiter(this.spigotConfig.tileMaxTickTime);
|
||||
+ this.chunkPacketBlockController = this.paperConfig().anticheat.antiXray.enabled ? new io.papermc.paper.antixray.ChunkPacketBlockControllerAntiXray(this, executor) : io.papermc.paper.antixray.ChunkPacketBlockController.NO_OPERATION_INSTANCE; // Paper - Anti-Xray
|
||||
}
|
||||
|
||||
// Paper start - Cancel hit for vanished players
|
||||
@@ -495,6 +498,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
||||
// CraftBukkit end
|
||||
|
||||
BlockState blockState = chunkAt.setBlockState(pos, state, (flags & 64) != 0, (flags & 1024) == 0); // CraftBukkit custom NO_PLACE flag
|
||||
+ this.chunkPacketBlockController.onBlockChange(this, pos, state, blockState, flags, recursionLeft); // Paper - Anti-Xray
|
||||
|
||||
if (blockState == null) {
|
||||
// CraftBukkit start - remove blockstate if failed (or the same)
|
||||
diff --git a/net/minecraft/world/level/chunk/ChunkAccess.java b/net/minecraft/world/level/chunk/ChunkAccess.java
|
||||
index 12d9b532e466ec4e46920d409b5f1b3ae60b80f8..bc688ad1097ef4159dfc5f96d963a9fa63262e20 100644
|
||||
--- a/net/minecraft/world/level/chunk/ChunkAccess.java
|
||||
+++ b/net/minecraft/world/level/chunk/ChunkAccess.java
|
||||
@@ -114,14 +114,14 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh
|
||||
}
|
||||
}
|
||||
|
||||
- replaceMissingSections(biomeRegistry, this.sections);
|
||||
+ this.replaceMissingSections(biomeRegistry, this.sections); // Paper - Anti-Xray - make it a non-static method
|
||||
this.biomeRegistry = biomeRegistry; // Craftbukkit
|
||||
}
|
||||
|
||||
- private static void replaceMissingSections(Registry<Biome> biomeRegistry, LevelChunkSection[] sections) {
|
||||
+ private void replaceMissingSections(Registry<Biome> biomeRegistry, LevelChunkSection[] sections) { // Paper - Anti-Xray - make it a non-static method
|
||||
for (int i = 0; i < sections.length; i++) {
|
||||
if (sections[i] == null) {
|
||||
- sections[i] = new LevelChunkSection(biomeRegistry);
|
||||
+ sections[i] = new LevelChunkSection(biomeRegistry, this.levelHeightAccessor instanceof net.minecraft.world.level.Level ? (net.minecraft.world.level.Level) this.levelHeightAccessor : null, this.chunkPos, this.levelHeightAccessor.getSectionYFromSectionIndex(i)); // Paper - Anti-Xray - Add parameters
|
||||
}
|
||||
}
|
||||
}
|
||||
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
index b3cc671f33b2c8c5f3131afffc6ee9d7b83dd3bc..d1d0dc13eecb0e0eb3a7839b570a5fe7f62f3fba 100644
|
||||
--- a/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
@@ -109,7 +109,7 @@ public class LevelChunk extends ChunkAccess {
|
||||
@Nullable LevelChunk.PostLoadProcessor postLoad,
|
||||
@Nullable BlendingData blendingData
|
||||
) {
|
||||
- super(pos, data, level, level.registryAccess().lookupOrThrow(Registries.BIOME), inhabitedTime, sections, blendingData);
|
||||
+ super(pos, data, level, net.minecraft.server.MinecraftServer.getServer().registryAccess().lookupOrThrow(Registries.BIOME), inhabitedTime, sections, blendingData); // Paper - Anti-Xray - The world isn't ready yet, use server singleton for registry
|
||||
this.level = (net.minecraft.server.level.ServerLevel) level; // CraftBukkit - type
|
||||
this.gameEventListenerRegistrySections = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
diff --git a/net/minecraft/world/level/chunk/LevelChunkSection.java b/net/minecraft/world/level/chunk/LevelChunkSection.java
|
||||
index baa9f3e2e6e45c250930658e82bad70a3a292b05..fc21c3268c4b4fda2933d71f0913db28e3796653 100644
|
||||
--- a/net/minecraft/world/level/chunk/LevelChunkSection.java
|
||||
+++ b/net/minecraft/world/level/chunk/LevelChunkSection.java
|
||||
@@ -38,9 +38,15 @@ public class LevelChunkSection {
|
||||
this.recalcBlockCounts();
|
||||
}
|
||||
|
||||
+ // Paper start - Anti-Xray - Add parameters
|
||||
+ @Deprecated @io.papermc.paper.annotation.DoNotUse
|
||||
public LevelChunkSection(Registry<Biome> biomeRegistry) {
|
||||
- this.states = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
|
||||
- this.biomes = new PalettedContainer<>(biomeRegistry.asHolderIdMap(), biomeRegistry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
|
||||
+ this(biomeRegistry, null, null, 0);
|
||||
+ }
|
||||
+ public LevelChunkSection(Registry<Biome> biomeRegistry, net.minecraft.world.level.Level level, net.minecraft.world.level.ChunkPos chunkPos, int chunkSectionY) {
|
||||
+ // Paper end - Anti-Xray
|
||||
+ this.states = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, level == null || level.chunkPacketBlockController == null ? null : level.chunkPacketBlockController.getPresetBlockStates(level, chunkPos, chunkSectionY)); // Paper - Anti-Xray - Add preset block states
|
||||
+ this.biomes = new PalettedContainer<>(biomeRegistry.asHolderIdMap(), biomeRegistry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES, null); // Paper - Anti-Xray - Add preset biomes
|
||||
}
|
||||
|
||||
public BlockState getBlockState(int x, int y, int z) {
|
||||
@@ -168,10 +174,16 @@ public class LevelChunkSection {
|
||||
this.biomes = palettedContainer;
|
||||
}
|
||||
|
||||
+ // Paper start - Anti-Xray - Add chunk packet info
|
||||
+ @Deprecated @io.papermc.paper.annotation.DoNotUse
|
||||
public void write(FriendlyByteBuf buffer) {
|
||||
+ this.write(buffer, null, 0);
|
||||
+ }
|
||||
+ public void write(FriendlyByteBuf buffer, io.papermc.paper.antixray.ChunkPacketInfo<BlockState> chunkPacketInfo, int chunkSectionIndex) {
|
||||
buffer.writeShort(this.nonEmptyBlockCount);
|
||||
- this.states.write(buffer);
|
||||
- this.biomes.write(buffer);
|
||||
+ this.states.write(buffer, chunkPacketInfo, chunkSectionIndex);
|
||||
+ this.biomes.write(buffer, null, chunkSectionIndex);
|
||||
+ // Paper end - Anti-Xray
|
||||
}
|
||||
|
||||
public int getSerializedSize() {
|
||||
diff --git a/net/minecraft/world/level/chunk/PalettedContainer.java b/net/minecraft/world/level/chunk/PalettedContainer.java
|
||||
index e8ec28ce3fe13561b45c4654e174776d9d2d7b71..a6028a54c75de068515e95913b21160ab4326985 100644
|
||||
--- a/net/minecraft/world/level/chunk/PalettedContainer.java
|
||||
+++ b/net/minecraft/world/level/chunk/PalettedContainer.java
|
||||
@@ -28,6 +28,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
|
||||
private static final int MIN_PALETTE_BITS = 0;
|
||||
private final PaletteResize<T> dummyPaletteResize = (bits, objectAdded) -> 0;
|
||||
public final IdMap<T> registry;
|
||||
+ private final T @org.jetbrains.annotations.Nullable [] presetValues; // Paper - Anti-Xray - Add preset values
|
||||
private volatile PalettedContainer.Data<T> data;
|
||||
private final PalettedContainer.Strategy strategy;
|
||||
//private final ThreadingDetector threadingDetector = new ThreadingDetector("PalettedContainer"); // Paper - unused
|
||||
@@ -40,13 +41,21 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
|
||||
// this.threadingDetector.checkAndUnlock(); // Paper - disable this - use proper synchronization
|
||||
}
|
||||
|
||||
+ // Paper start - Anti-Xray - Add preset values
|
||||
+ @Deprecated @io.papermc.paper.annotation.DoNotUse
|
||||
public static <T> Codec<PalettedContainer<T>> codecRW(IdMap<T> registry, Codec<T> codec, PalettedContainer.Strategy strategy, T value) {
|
||||
- PalettedContainerRO.Unpacker<T, PalettedContainer<T>> unpacker = PalettedContainer::unpack;
|
||||
+ return PalettedContainer.codecRW(registry, codec, strategy, value, null);
|
||||
+ }
|
||||
+ public static <T> Codec<PalettedContainer<T>> codecRW(IdMap<T> registry, Codec<T> codec, PalettedContainer.Strategy strategy, T value, T @org.jetbrains.annotations.Nullable [] presetValues) {
|
||||
+ PalettedContainerRO.Unpacker<T, PalettedContainer<T>> unpacker = (idListx, paletteProviderx, serialized) -> {
|
||||
+ return unpack(idListx, paletteProviderx, serialized, value, presetValues);
|
||||
+ };
|
||||
+ // Paper end
|
||||
return codec(registry, codec, strategy, value, unpacker);
|
||||
}
|
||||
|
||||
public static <T> Codec<PalettedContainerRO<T>> codecRO(IdMap<T> registry, Codec<T> codec, PalettedContainer.Strategy strategy, T value) {
|
||||
- PalettedContainerRO.Unpacker<T, PalettedContainerRO<T>> unpacker = (registry1, strategy1, packedData) -> unpack(registry1, strategy1, packedData)
|
||||
+ PalettedContainerRO.Unpacker<T, PalettedContainerRO<T>> unpacker = (registry1, strategy1, packedData) -> unpack(registry1, strategy1, packedData, value, null) // Paper - Anti-Xray - Add preset values
|
||||
.map(container -> (PalettedContainerRO<T>)container);
|
||||
return codec(registry, codec, strategy, value, unpacker);
|
||||
}
|
||||
@@ -66,27 +75,66 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
|
||||
);
|
||||
}
|
||||
|
||||
+ // Paper start - Anti-Xray - Add preset values
|
||||
+ @Deprecated @io.papermc.paper.annotation.DoNotUse
|
||||
+ public PalettedContainer(IdMap<T> registry, PalettedContainer.Strategy strategy, PalettedContainer.Configuration<T> configuration, BitStorage storage, List<T> values) {
|
||||
+ this(registry, strategy, configuration, storage, values, null, null);
|
||||
+ }
|
||||
public PalettedContainer(
|
||||
- IdMap<T> registry, PalettedContainer.Strategy strategy, PalettedContainer.Configuration<T> configuration, BitStorage storage, List<T> values
|
||||
+ IdMap<T> registry, PalettedContainer.Strategy strategy, PalettedContainer.Configuration<T> configuration, BitStorage storage, List<T> values, T defaultValue, T @org.jetbrains.annotations.Nullable [] presetValues
|
||||
) {
|
||||
+ this.presetValues = presetValues;
|
||||
this.registry = registry;
|
||||
this.strategy = strategy;
|
||||
this.data = new PalettedContainer.Data<>(configuration, storage, configuration.factory().create(configuration.bits(), registry, this, values));
|
||||
+ if (presetValues != null && (configuration.factory() == PalettedContainer.Strategy.SINGLE_VALUE_PALETTE_FACTORY ? this.data.palette.valueFor(0) != defaultValue : configuration.factory() != PalettedContainer.Strategy.GLOBAL_PALETTE_FACTORY)) {
|
||||
+ // In 1.18 Mojang unfortunately removed code that already handled possible resize operations on read from disk for us
|
||||
+ // We readd this here but in a smarter way than it was before
|
||||
+ int maxSize = 1 << configuration.bits();
|
||||
+
|
||||
+ for (T presetValue : presetValues) {
|
||||
+ if (this.data.palette.getSize() >= maxSize) {
|
||||
+ java.util.Set<T> allValues = new java.util.HashSet<>(values);
|
||||
+ allValues.addAll(Arrays.asList(presetValues));
|
||||
+ int newBits = Mth.ceillog2(allValues.size());
|
||||
+
|
||||
+ if (newBits > configuration.bits()) {
|
||||
+ this.onResize(newBits, null);
|
||||
+ }
|
||||
+
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ this.data.palette.idFor(presetValue);
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
- private PalettedContainer(IdMap<T> registry, PalettedContainer.Strategy strategy, PalettedContainer.Data<T> data) {
|
||||
+ // Paper start - Anti-Xray - Add preset values
|
||||
+ private PalettedContainer(IdMap<T> registry, PalettedContainer.Strategy strategy, PalettedContainer.Data<T> data, T @org.jetbrains.annotations.Nullable [] presetValues) {
|
||||
+ this.presetValues = presetValues;
|
||||
+ // Paper end - Anti-Xray
|
||||
this.registry = registry;
|
||||
this.strategy = strategy;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
- private PalettedContainer(PalettedContainer<T> other) {
|
||||
+ private PalettedContainer(PalettedContainer<T> other, T @org.jetbrains.annotations.Nullable [] presetValues) { // Paper - Anti-Xray - Add preset values
|
||||
+ this.presetValues = presetValues; // Paper - Anti-Xray - Add preset values
|
||||
this.registry = other.registry;
|
||||
this.strategy = other.strategy;
|
||||
this.data = other.data.copy(this);
|
||||
}
|
||||
|
||||
+ // Paper start - Anti-Xray - Add preset values
|
||||
+ @Deprecated @io.papermc.paper.annotation.DoNotUse
|
||||
public PalettedContainer(IdMap<T> registry, T palette, PalettedContainer.Strategy strategy) {
|
||||
+ this(registry, palette, strategy, null);
|
||||
+ }
|
||||
+ public PalettedContainer(IdMap<T> registry, T palette, PalettedContainer.Strategy strategy, T @org.jetbrains.annotations.Nullable [] presetValues) {
|
||||
+ this.presetValues = presetValues;
|
||||
+ // Paper end - Anti-Xray
|
||||
this.strategy = strategy;
|
||||
this.registry = registry;
|
||||
this.data = this.createOrReuseData(null, 0);
|
||||
@@ -101,11 +149,30 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
|
||||
@Override
|
||||
public synchronized int onResize(int bits, T objectAdded) { // Paper - synchronize
|
||||
PalettedContainer.Data<T> data = this.data;
|
||||
+ // Paper start - Anti-Xray - Add preset values
|
||||
+ if (this.presetValues != null && objectAdded != null && data.configuration().factory() == PalettedContainer.Strategy.SINGLE_VALUE_PALETTE_FACTORY) {
|
||||
+ int duplicates = 0;
|
||||
+ List<T> presetValues = Arrays.asList(this.presetValues);
|
||||
+ duplicates += presetValues.contains(objectAdded) ? 1 : 0;
|
||||
+ duplicates += presetValues.contains(data.palette.valueFor(0)) ? 1 : 0;
|
||||
+ bits = Mth.ceillog2((1 << this.strategy.calculateBitsForSerialization(this.registry, 1 << bits)) + presetValues.size() - duplicates);
|
||||
+ }
|
||||
+ // Paper end - Anti-Xray
|
||||
PalettedContainer.Data<T> data1 = this.createOrReuseData(data, bits);
|
||||
data1.copyFrom(data.palette, data.storage);
|
||||
this.data = data1;
|
||||
- return data1.palette.idFor(objectAdded);
|
||||
+ // Paper start - Anti-Xray
|
||||
+ this.addPresetValues();
|
||||
+ return objectAdded == null ? -1 : data1.palette.idFor(objectAdded);
|
||||
+ }
|
||||
+ private void addPresetValues() {
|
||||
+ if (this.presetValues != null && this.data.configuration().factory() != PalettedContainer.Strategy.GLOBAL_PALETTE_FACTORY) {
|
||||
+ for (T presetValue : this.presetValues) {
|
||||
+ this.data.palette.idFor(presetValue);
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
+ // Paper end - Anti-Xray
|
||||
|
||||
public synchronized T getAndSet(int x, int y, int z, T state) { // Paper - synchronize
|
||||
this.acquire();
|
||||
@@ -172,24 +239,35 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
|
||||
data.palette.read(buffer);
|
||||
buffer.readLongArray(data.storage.getRaw());
|
||||
this.data = data;
|
||||
+ this.addPresetValues(); // Paper - Anti-Xray - Add preset values (inefficient, but this isn't used by the server)
|
||||
} finally {
|
||||
this.release();
|
||||
}
|
||||
}
|
||||
|
||||
+ // Paper start - Anti-Xray; Add chunk packet info
|
||||
@Override
|
||||
- public synchronized void write(FriendlyByteBuf buffer) { // Paper - synchronize
|
||||
+ @Deprecated @io.papermc.paper.annotation.DoNotUse
|
||||
+ public void write(FriendlyByteBuf buffer) {
|
||||
+ this.write(buffer, null, 0);
|
||||
+ }
|
||||
+ @Override
|
||||
+ public synchronized void write(FriendlyByteBuf buffer, @Nullable io.papermc.paper.antixray.ChunkPacketInfo<T> chunkPacketInfo, int chunkSectionIndex) { // Paper - Synchronize
|
||||
this.acquire();
|
||||
|
||||
try {
|
||||
- this.data.write(buffer);
|
||||
+ this.data.write(buffer, chunkPacketInfo, chunkSectionIndex);
|
||||
+ if (chunkPacketInfo != null) {
|
||||
+ chunkPacketInfo.setPresetValues(chunkSectionIndex, this.presetValues);
|
||||
+ }
|
||||
+ // Paper end - Anti-Xray
|
||||
} finally {
|
||||
this.release();
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> DataResult<PalettedContainer<T>> unpack(
|
||||
- IdMap<T> registry, PalettedContainer.Strategy strategy, PalettedContainerRO.PackedData<T> packedData
|
||||
+ IdMap<T> registry, PalettedContainer.Strategy strategy, PalettedContainerRO.PackedData<T> packedData, T defaultValue, T @org.jetbrains.annotations.Nullable [] presetValues // Paper - Anti-Xray - Add preset values
|
||||
) {
|
||||
List<T> list = packedData.paletteEntries();
|
||||
int size = strategy.size();
|
||||
@@ -222,7 +300,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
|
||||
}
|
||||
}
|
||||
|
||||
- return DataResult.success(new PalettedContainer<>(registry, strategy, configuration, bitStorage, list));
|
||||
+ return DataResult.success(new PalettedContainer<>(registry, strategy, configuration, bitStorage, list, defaultValue, presetValues)); // Paper - Anti-Xray - Add preset values
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -280,12 +358,12 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
|
||||
|
||||
@Override
|
||||
public PalettedContainer<T> copy() {
|
||||
- return new PalettedContainer<>(this);
|
||||
+ return new PalettedContainer<>(this, this.presetValues); // Paper - Anti-Xray - Add preset values
|
||||
}
|
||||
|
||||
@Override
|
||||
public PalettedContainer<T> recreate() {
|
||||
- return new PalettedContainer<>(this.registry, this.data.palette.valueFor(0), this.strategy);
|
||||
+ return new PalettedContainer<>(this.registry, this.data.palette.valueFor(0), this.strategy, this.presetValues); // Paper - Anti-Xray - Add preset values
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -324,9 +402,16 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
|
||||
return 1 + this.palette.getSerializedSize() + VarInt.getByteSize(this.storage.getRaw().length) + this.storage.getRaw().length * 8;
|
||||
}
|
||||
|
||||
- public void write(FriendlyByteBuf buffer) {
|
||||
+ // Paper start - Anti-Xray - Add chunk packet info
|
||||
+ public void write(FriendlyByteBuf buffer, @Nullable io.papermc.paper.antixray.ChunkPacketInfo<T> chunkPacketInfo, int chunkSectionIndex) {
|
||||
buffer.writeByte(this.storage.getBits());
|
||||
this.palette.write(buffer);
|
||||
+ if (chunkPacketInfo != null) {
|
||||
+ chunkPacketInfo.setBits(chunkSectionIndex, this.configuration.bits());
|
||||
+ chunkPacketInfo.setPalette(chunkSectionIndex, this.palette);
|
||||
+ chunkPacketInfo.setIndex(chunkSectionIndex, buffer.writerIndex() + VarInt.getByteSize(this.storage.getRaw().length));
|
||||
+ }
|
||||
+ // Paper end
|
||||
buffer.writeLongArray(this.storage.getRaw());
|
||||
}
|
||||
|
||||
diff --git a/net/minecraft/world/level/chunk/PalettedContainerRO.java b/net/minecraft/world/level/chunk/PalettedContainerRO.java
|
||||
index bfbb1a2bb4abbb369a24f2f01439e9ea3e16794b..8d6ed8be4d93f7d4e6ea80c351020d88ee98aa4d 100644
|
||||
--- a/net/minecraft/world/level/chunk/PalettedContainerRO.java
|
||||
+++ b/net/minecraft/world/level/chunk/PalettedContainerRO.java
|
||||
@@ -14,7 +14,10 @@ public interface PalettedContainerRO<T> {
|
||||
|
||||
void getAll(Consumer<T> consumer);
|
||||
|
||||
- void write(FriendlyByteBuf buffer);
|
||||
+ // Paper start - Anti-Xray - Add chunk packet info
|
||||
+ @Deprecated @io.papermc.paper.annotation.DoNotUse void write(FriendlyByteBuf buffer);
|
||||
+ void write(FriendlyByteBuf buffer, @javax.annotation.Nullable io.papermc.paper.antixray.ChunkPacketInfo<T> chunkPacketInfo, int chunkSectionIndex);
|
||||
+ // Paper end
|
||||
|
||||
int getSerializedSize();
|
||||
|
||||
diff --git a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
|
||||
index 37437a86d74291fab1de9495008aafb15dfadce0..cf6e2053d81f7b0f8c8e58b9c0fad3285ebc047d 100644
|
||||
--- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
|
||||
@@ -94,7 +94,7 @@ public record SerializableChunkData(
|
||||
, @Nullable net.minecraft.nbt.Tag persistentDataContainer // CraftBukkit - persistentDataContainer
|
||||
) {
|
||||
public static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codecRW(
|
||||
- Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState()
|
||||
+ Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState(), null // Paper - Anti-Xray
|
||||
);
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private static final String TAG_UPGRADE_DATA = "UpgradeData";
|
||||
@@ -128,6 +128,7 @@ public record SerializableChunkData(
|
||||
|
||||
@Nullable
|
||||
public static SerializableChunkData parse(LevelHeightAccessor levelHeightAccessor, RegistryAccess registries, CompoundTag tag) {
|
||||
+ net.minecraft.server.level.ServerLevel serverLevel = (net.minecraft.server.level.ServerLevel) levelHeightAccessor; // Paper - Anti-Xray This is is seemingly only called from ChunkMap, where, we have a server level. We'll fight this later if needed.
|
||||
if (!tag.contains("Status", 8)) {
|
||||
return null;
|
||||
} else {
|
||||
@@ -212,18 +213,21 @@ public record SerializableChunkData(
|
||||
Codec<PalettedContainer<Holder<Biome>>> codec = makeBiomeCodecRW(registry); // CraftBukkit - read/write
|
||||
|
||||
for (int i2 = 0; i2 < list7.size(); i2++) {
|
||||
- CompoundTag compound2 = list7.getCompound(i2);
|
||||
+ CompoundTag compound2 = list7.getCompound(i2); final CompoundTag sectionData = compound2; // Paper - Anti-Xray - OBFHELPER
|
||||
int _byte = compound2.getByte("Y");
|
||||
LevelChunkSection levelChunkSection;
|
||||
if (_byte >= levelHeightAccessor.getMinSectionY() && _byte <= levelHeightAccessor.getMaxSectionY()) {
|
||||
+ // Paper start - Anti-Xray - Add preset block states
|
||||
+ BlockState[] presetBlockStates = serverLevel.chunkPacketBlockController.getPresetBlockStates(serverLevel, chunkPos, _byte);
|
||||
PalettedContainer<BlockState> palettedContainer;
|
||||
if (compound2.contains("block_states", 10)) {
|
||||
- palettedContainer = BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, compound2.getCompound("block_states"))
|
||||
+ Codec<PalettedContainer<BlockState>> blockStateCodec = presetBlockStates == null ? BLOCK_STATE_CODEC : PalettedContainer.codecRW(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState(), presetBlockStates); // Paper - Anti-Xray
|
||||
+ palettedContainer = blockStateCodec.parse(NbtOps.INSTANCE, sectionData.getCompound("block_states")) // Paper - Anti-Xray
|
||||
.promotePartial(string -> logErrors(chunkPos, _byte, string))
|
||||
.getOrThrow(SerializableChunkData.ChunkReadException::new);
|
||||
} else {
|
||||
palettedContainer = new PalettedContainer<>(
|
||||
- Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES
|
||||
+ Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, presetBlockStates // Paper - Anti-Xray
|
||||
);
|
||||
}
|
||||
|
||||
@@ -234,7 +238,7 @@ public record SerializableChunkData(
|
||||
.getOrThrow(SerializableChunkData.ChunkReadException::new);
|
||||
} else {
|
||||
palettedContainerRo = new PalettedContainer<>(
|
||||
- registry.asHolderIdMap(), registry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES
|
||||
+ registry.asHolderIdMap(), registry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES, null // Paper - Anti-Xray - Add preset biomes
|
||||
);
|
||||
}
|
||||
|
||||
@@ -414,7 +418,7 @@ public record SerializableChunkData(
|
||||
|
||||
// CraftBukkit start - read/write
|
||||
private static Codec<PalettedContainer<Holder<Biome>>> makeBiomeCodecRW(Registry<Biome> iregistry) {
|
||||
- return PalettedContainer.codecRW(iregistry.asHolderIdMap(), iregistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, iregistry.getOrThrow(Biomes.PLAINS));
|
||||
+ return PalettedContainer.codecRW(iregistry.asHolderIdMap(), iregistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, iregistry.getOrThrow(Biomes.PLAINS), null); // Paper - Anti-Xray - Add preset biomes
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
@@ -0,0 +1,354 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Andrew Steinborn <git@steinborn.me>
|
||||
Date: Mon, 26 Jul 2021 02:15:17 -0400
|
||||
Subject: [PATCH] Use Velocity compression and cipher natives
|
||||
|
||||
|
||||
diff --git a/net/minecraft/network/CipherDecoder.java b/net/minecraft/network/CipherDecoder.java
|
||||
index 429325ffb7db2b85ed271ddf3da64c6fdc593673..4a445cb0ab19c6eca94fdc2172e1b286d1947c63 100644
|
||||
--- a/net/minecraft/network/CipherDecoder.java
|
||||
+++ b/net/minecraft/network/CipherDecoder.java
|
||||
@@ -7,14 +7,30 @@ import java.util.List;
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
public class CipherDecoder extends MessageToMessageDecoder<ByteBuf> {
|
||||
- private final CipherBase cipher;
|
||||
+ private final com.velocitypowered.natives.encryption.VelocityCipher cipher; // Paper - Use Velocity cipher
|
||||
|
||||
- public CipherDecoder(Cipher cipher) {
|
||||
- this.cipher = new CipherBase(cipher);
|
||||
+ public CipherDecoder(com.velocitypowered.natives.encryption.VelocityCipher cipher) { // Paper - Use Velocity cipher
|
||||
+ this.cipher = cipher; // Paper - Use Velocity cipher
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext context, ByteBuf in, List<Object> out) throws Exception {
|
||||
- out.add(this.cipher.decipher(context, in));
|
||||
+ // Paper start - Use Velocity cipher
|
||||
+ ByteBuf compatible = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(context.alloc(), this.cipher, in);
|
||||
+ try {
|
||||
+ this.cipher.process(compatible);
|
||||
+ out.add(compatible);
|
||||
+ } catch (Exception e) {
|
||||
+ compatible.release(); // compatible will never be used if we throw an exception
|
||||
+ throw e;
|
||||
+ }
|
||||
+ // Paper end - Use Velocity cipher
|
||||
}
|
||||
+
|
||||
+ // Paper start - Use Velocity cipher
|
||||
+ @Override
|
||||
+ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
|
||||
+ this.cipher.close();
|
||||
+ }
|
||||
+ // Paper end - Use Velocity cipher
|
||||
}
|
||||
diff --git a/net/minecraft/network/CipherEncoder.java b/net/minecraft/network/CipherEncoder.java
|
||||
index 992b9c7aed57ce29cdd2b4f66737d39db214f0cf..e087a35bcaf326c37a3e58f4d06165a61747c5a9 100644
|
||||
--- a/net/minecraft/network/CipherEncoder.java
|
||||
+++ b/net/minecraft/network/CipherEncoder.java
|
||||
@@ -5,15 +5,31 @@ import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
-public class CipherEncoder extends MessageToByteEncoder<ByteBuf> {
|
||||
- private final CipherBase cipher;
|
||||
+public class CipherEncoder extends io.netty.handler.codec.MessageToMessageEncoder<ByteBuf> { // Paper - Use Velocity cipher; change superclass
|
||||
+ private final com.velocitypowered.natives.encryption.VelocityCipher cipher; // Paper - Use Velocity cipher
|
||||
|
||||
- public CipherEncoder(Cipher cipher) {
|
||||
- this.cipher = new CipherBase(cipher);
|
||||
+ public CipherEncoder(com.velocitypowered.natives.encryption.VelocityCipher cipher) { // Paper - Use Velocity cipher
|
||||
+ this.cipher = cipher; // Paper - Use Velocity cipher
|
||||
}
|
||||
|
||||
+ // Paper start - Use Velocity cipher
|
||||
@Override
|
||||
- protected void encode(ChannelHandlerContext context, ByteBuf message, ByteBuf out) throws Exception {
|
||||
- this.cipher.encipher(message, out);
|
||||
+ protected void encode(ChannelHandlerContext context, ByteBuf message, java.util.List<Object> list) throws Exception {
|
||||
+ ByteBuf compatible = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(context.alloc(), this.cipher, message);
|
||||
+ try {
|
||||
+ this.cipher.process(compatible);
|
||||
+ list.add(compatible);
|
||||
+ } catch (Exception e) {
|
||||
+ compatible.release(); // compatible will never be used if we throw an exception
|
||||
+ throw e;
|
||||
+ }
|
||||
+ // Paper end - Use Velocity cipher
|
||||
}
|
||||
+
|
||||
+ // Paper start - Use Velocity cipher
|
||||
+ @Override
|
||||
+ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
|
||||
+ this.cipher.close();
|
||||
+ }
|
||||
+ // Paper end - Use Velocity cipher
|
||||
}
|
||||
diff --git a/net/minecraft/network/CompressionDecoder.java b/net/minecraft/network/CompressionDecoder.java
|
||||
index fcf0e557fbcbd5f306625096d859578fe8511734..2c7f935fcecb24a4394fdde523219a5b5984a673 100644
|
||||
--- a/net/minecraft/network/CompressionDecoder.java
|
||||
+++ b/net/minecraft/network/CompressionDecoder.java
|
||||
@@ -12,14 +12,22 @@ import java.util.zip.Inflater;
|
||||
public class CompressionDecoder extends ByteToMessageDecoder {
|
||||
public static final int MAXIMUM_COMPRESSED_LENGTH = 2097152;
|
||||
public static final int MAXIMUM_UNCOMPRESSED_LENGTH = 8388608;
|
||||
+ private com.velocitypowered.natives.compression.VelocityCompressor compressor; // Paper - Use Velocity cipher
|
||||
private Inflater inflater;
|
||||
private int threshold;
|
||||
private boolean validateDecompressed;
|
||||
|
||||
+ // Paper start - Use Velocity cipher
|
||||
+ @io.papermc.paper.annotation.DoNotUse
|
||||
public CompressionDecoder(int threshold, boolean validateDecompressed) {
|
||||
+ this(null, threshold, validateDecompressed);
|
||||
+ }
|
||||
+ public CompressionDecoder(com.velocitypowered.natives.compression.VelocityCompressor compressor, int threshold, boolean validateDecompressed) {
|
||||
this.threshold = threshold;
|
||||
this.validateDecompressed = validateDecompressed;
|
||||
- this.inflater = new Inflater();
|
||||
+ this.inflater = compressor == null ? new Inflater() : null;
|
||||
+ this.compressor = compressor;
|
||||
+ // Paper end - Use Velocity cipher
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -39,14 +47,42 @@ public class CompressionDecoder extends ByteToMessageDecoder {
|
||||
}
|
||||
}
|
||||
|
||||
+ if (inflater != null) { // Paper - Use Velocity cipher; fallback to vanilla inflater
|
||||
this.setupInflaterInput(in);
|
||||
ByteBuf byteBuf = this.inflate(context, i);
|
||||
this.inflater.reset();
|
||||
out.add(byteBuf);
|
||||
+ return; // Paper - Use Velocity cipher
|
||||
+ } // Paper - use velocity compression
|
||||
+
|
||||
+ // Paper start - Use Velocity cipher
|
||||
+ int claimedUncompressedSize = i; // OBFHELPER
|
||||
+ ByteBuf compatibleIn = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(context.alloc(), this.compressor, in);
|
||||
+ ByteBuf uncompressed = com.velocitypowered.natives.util.MoreByteBufUtils.preferredBuffer(context.alloc(), this.compressor, claimedUncompressedSize);
|
||||
+ try {
|
||||
+ this.compressor.inflate(compatibleIn, uncompressed, claimedUncompressedSize);
|
||||
+ out.add(uncompressed);
|
||||
+ in.clear();
|
||||
+ } catch (Exception e) {
|
||||
+ uncompressed.release();
|
||||
+ throw e;
|
||||
+ } finally {
|
||||
+ compatibleIn.release();
|
||||
+ }
|
||||
+ // Paper end - Use Velocity cipher
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ // Paper start - Use Velocity cipher
|
||||
+ @Override
|
||||
+ public void handlerRemoved0(ChannelHandlerContext ctx) {
|
||||
+ if (this.compressor != null) {
|
||||
+ this.compressor.close();
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - Use Velocity cipher
|
||||
+
|
||||
private void setupInflaterInput(ByteBuf buffer) {
|
||||
ByteBuffer byteBuffer;
|
||||
if (buffer.nioBufferCount() > 0) {
|
||||
@@ -81,7 +117,13 @@ public class CompressionDecoder extends ByteToMessageDecoder {
|
||||
}
|
||||
}
|
||||
|
||||
- public void setThreshold(int threshold, boolean validateDecompressed) {
|
||||
+ // Paper start - Use Velocity cipher
|
||||
+ public void setThreshold(com.velocitypowered.natives.compression.VelocityCompressor compressor, int threshold, boolean validateDecompressed) {
|
||||
+ if (this.compressor == null && compressor != null) { // Only re-configure once. Re-reconfiguring would require closing the native compressor.
|
||||
+ this.compressor = compressor;
|
||||
+ this.inflater = null;
|
||||
+ }
|
||||
+ // Paper end - Use Velocity cipher
|
||||
this.threshold = threshold;
|
||||
this.validateDecompressed = validateDecompressed;
|
||||
}
|
||||
diff --git a/net/minecraft/network/CompressionEncoder.java b/net/minecraft/network/CompressionEncoder.java
|
||||
index bc674b08a41d5529fe06c6d3f077051cf4138f73..ea8a894158c44c2e7943dea43ecd8e1f0075b18f 100644
|
||||
--- a/net/minecraft/network/CompressionEncoder.java
|
||||
+++ b/net/minecraft/network/CompressionEncoder.java
|
||||
@@ -6,17 +6,31 @@ import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import java.util.zip.Deflater;
|
||||
|
||||
public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
|
||||
- private final byte[] encodeBuf = new byte[8192];
|
||||
+ @javax.annotation.Nullable private final byte[] encodeBuf; // Paper - Use Velocity cipher
|
||||
+ @javax.annotation.Nullable // Paper - Use Velocity cipher
|
||||
private final Deflater deflater;
|
||||
+ @javax.annotation.Nullable private final com.velocitypowered.natives.compression.VelocityCompressor compressor; // Paper - Use Velocity cipher
|
||||
private int threshold;
|
||||
|
||||
+ // Paper start - Use Velocity cipher
|
||||
public CompressionEncoder(int threshold) {
|
||||
+ this(null, threshold);
|
||||
+ }
|
||||
+ public CompressionEncoder(@javax.annotation.Nullable com.velocitypowered.natives.compression.VelocityCompressor compressor, int threshold) {
|
||||
this.threshold = threshold;
|
||||
- this.deflater = new Deflater();
|
||||
+ if (compressor == null) {
|
||||
+ this.encodeBuf = new byte[8192];
|
||||
+ this.deflater = new Deflater();
|
||||
+ } else {
|
||||
+ this.encodeBuf = null;
|
||||
+ this.deflater = null;
|
||||
+ }
|
||||
+ this.compressor = compressor;
|
||||
+ // Paper end - Use Velocity cipher
|
||||
}
|
||||
|
||||
@Override
|
||||
- protected void encode(ChannelHandlerContext context, ByteBuf encodingByteBuf, ByteBuf byteBuf) {
|
||||
+ protected void encode(ChannelHandlerContext context, ByteBuf encodingByteBuf, ByteBuf byteBuf) throws Exception { // Paper - Use Velocity cipher
|
||||
int i = encodingByteBuf.readableBytes();
|
||||
if (i > 8388608) {
|
||||
throw new IllegalArgumentException("Packet too big (is " + i + ", should be less than 8388608)");
|
||||
@@ -25,6 +39,7 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
|
||||
VarInt.write(byteBuf, 0);
|
||||
byteBuf.writeBytes(encodingByteBuf);
|
||||
} else {
|
||||
+ if (this.deflater != null) { // Paper - Use Velocity cipher
|
||||
byte[] bytes = new byte[i];
|
||||
encodingByteBuf.readBytes(bytes);
|
||||
VarInt.write(byteBuf, bytes.length);
|
||||
@@ -37,6 +52,17 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
|
||||
}
|
||||
|
||||
this.deflater.reset();
|
||||
+ // Paper start - Use Velocity cipher
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ VarInt.write(byteBuf, i);
|
||||
+ final ByteBuf compatibleIn = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(context.alloc(), this.compressor, encodingByteBuf);
|
||||
+ try {
|
||||
+ this.compressor.deflate(compatibleIn, byteBuf);
|
||||
+ } finally {
|
||||
+ compatibleIn.release();
|
||||
+ }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,4 +74,31 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
|
||||
public void setThreshold(int threshold) {
|
||||
this.threshold = threshold;
|
||||
}
|
||||
+
|
||||
+ // Paper start - Use Velocity cipher
|
||||
+ @Override
|
||||
+ protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, ByteBuf msg, boolean preferDirect) throws Exception {
|
||||
+ if (this.compressor != null) {
|
||||
+ // We allocate bytes to be compressed plus 1 byte. This covers two cases:
|
||||
+ //
|
||||
+ // - Compression
|
||||
+ // According to https://github.com/ebiggers/libdeflate/blob/master/libdeflate.h#L103,
|
||||
+ // if the data compresses well (and we do not have some pathological case) then the maximum
|
||||
+ // size the compressed size will ever be is the input size minus one.
|
||||
+ // - Uncompressed
|
||||
+ // This is fairly obvious - we will then have one more than the uncompressed size.
|
||||
+ final int initialBufferSize = msg.readableBytes() + 1;
|
||||
+ return com.velocitypowered.natives.util.MoreByteBufUtils.preferredBuffer(ctx.alloc(), this.compressor, initialBufferSize);
|
||||
+ }
|
||||
+
|
||||
+ return super.allocateBuffer(ctx, msg, preferDirect);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void handlerRemoved(ChannelHandlerContext ctx) {
|
||||
+ if (this.compressor != null) {
|
||||
+ this.compressor.close();
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - Use Velocity cipher
|
||||
}
|
||||
diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java
|
||||
index c4bb28857ee11dccc9924666634488044c666fd1..8fe485c5bf79804bb4d1f774f95a92b14a576e80 100644
|
||||
--- a/net/minecraft/network/Connection.java
|
||||
+++ b/net/minecraft/network/Connection.java
|
||||
@@ -770,11 +770,22 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
return connection;
|
||||
}
|
||||
|
||||
- public void setEncryptionKey(Cipher decryptingCipher, Cipher encryptingCipher) {
|
||||
- this.encrypted = true;
|
||||
- this.channel.pipeline().addBefore("splitter", "decrypt", new CipherDecoder(decryptingCipher));
|
||||
- this.channel.pipeline().addBefore("prepender", "encrypt", new CipherEncoder(encryptingCipher));
|
||||
+ // Paper start - Use Velocity cipher
|
||||
+ public void setEncryptionKey(javax.crypto.SecretKey key) throws net.minecraft.util.CryptException {
|
||||
+ if (!this.encrypted) {
|
||||
+ try {
|
||||
+ com.velocitypowered.natives.encryption.VelocityCipher decryptionCipher = com.velocitypowered.natives.util.Natives.cipher.get().forDecryption(key);
|
||||
+ com.velocitypowered.natives.encryption.VelocityCipher encryptionCipher = com.velocitypowered.natives.util.Natives.cipher.get().forEncryption(key);
|
||||
+
|
||||
+ this.encrypted = true;
|
||||
+ this.channel.pipeline().addBefore("splitter", "decrypt", new CipherDecoder(decryptionCipher));
|
||||
+ this.channel.pipeline().addBefore("prepender", "encrypt", new CipherEncoder(encryptionCipher));
|
||||
+ } catch (java.security.GeneralSecurityException e) {
|
||||
+ throw new net.minecraft.util.CryptException(e);
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
+ // Paper end - Use Velocity cipher
|
||||
|
||||
public boolean isEncrypted() {
|
||||
return this.encrypted;
|
||||
@@ -813,16 +824,17 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
// Paper end - add proper async disconnect
|
||||
public void setupCompression(int threshold, boolean validateDecompressed) {
|
||||
if (threshold >= 0) {
|
||||
+ com.velocitypowered.natives.compression.VelocityCompressor compressor = com.velocitypowered.natives.util.Natives.compress.get().create(io.papermc.paper.configuration.GlobalConfiguration.get().misc.compressionLevel.or(-1)); // Paper - Use Velocity cipher
|
||||
if (this.channel.pipeline().get("decompress") instanceof CompressionDecoder compressionDecoder) {
|
||||
- compressionDecoder.setThreshold(threshold, validateDecompressed);
|
||||
+ compressionDecoder.setThreshold(compressor, threshold, validateDecompressed); // Paper - Use Velocity cipher
|
||||
} else {
|
||||
- this.channel.pipeline().addAfter("splitter", "decompress", new CompressionDecoder(threshold, validateDecompressed));
|
||||
+ this.channel.pipeline().addAfter("splitter", "decompress", new CompressionDecoder(compressor, threshold, validateDecompressed)); // Paper - Use Velocity cipher
|
||||
}
|
||||
|
||||
if (this.channel.pipeline().get("compress") instanceof CompressionEncoder compressionEncoder) {
|
||||
compressionEncoder.setThreshold(threshold);
|
||||
} else {
|
||||
- this.channel.pipeline().addAfter("prepender", "compress", new CompressionEncoder(threshold));
|
||||
+ this.channel.pipeline().addAfter("prepender", "compress", new CompressionEncoder(compressor, threshold)); // Paper - Use Velocity cipher
|
||||
}
|
||||
this.channel.pipeline().fireUserEventTriggered(io.papermc.paper.network.ConnectionEvent.COMPRESSION_THRESHOLD_SET); // Paper - Add Channel initialization listeners
|
||||
} else {
|
||||
diff --git a/net/minecraft/server/network/ServerConnectionListener.java b/net/minecraft/server/network/ServerConnectionListener.java
|
||||
index b68adf37af7172671163d4a8074d2bfa97724b4b..9d9f1b93a68bbc3e201408a3669bba7f73006218 100644
|
||||
--- a/net/minecraft/server/network/ServerConnectionListener.java
|
||||
+++ b/net/minecraft/server/network/ServerConnectionListener.java
|
||||
@@ -108,6 +108,10 @@ public class ServerConnectionListener {
|
||||
LOGGER.warn("Using HAProxy, please ensure the server port is adequately firewalled.");
|
||||
}
|
||||
// Paper end - Warn people with console access that HAProxy is in use.
|
||||
+ // Paper start - Use Velocity cipher
|
||||
+ ServerConnectionListener.LOGGER.info("Paper: Using " + com.velocitypowered.natives.util.Natives.compress.getLoadedVariant() + " compression from Velocity.");
|
||||
+ ServerConnectionListener.LOGGER.info("Paper: Using " + com.velocitypowered.natives.util.Natives.cipher.getLoadedVariant() + " cipher from Velocity.");
|
||||
+ // Paper end - Use Velocity cipher
|
||||
|
||||
this.channels
|
||||
.add(
|
||||
diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
||||
index d5d1cdc0759338ce1554b19689fca90d2573189e..507c6b2628cab56e00b64fe1b21f873e717eda2d 100644
|
||||
--- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
||||
+++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
||||
@@ -276,11 +276,9 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
|
||||
}
|
||||
|
||||
SecretKey secretKey = packet.getSecretKey(_private);
|
||||
- Cipher cipher = Crypt.getCipher(2, secretKey);
|
||||
- Cipher cipher1 = Crypt.getCipher(1, secretKey);
|
||||
string = new BigInteger(Crypt.digestData("", this.server.getKeyPair().getPublic(), secretKey)).toString(16);
|
||||
this.state = ServerLoginPacketListenerImpl.State.AUTHENTICATING;
|
||||
- this.connection.setEncryptionKey(cipher, cipher1);
|
||||
+ this.connection.setEncryptionKey(secretKey); // Paper - Use Velocity cipher
|
||||
} catch (CryptException var7) {
|
||||
throw new IllegalStateException("Protocol error", var7);
|
||||
}
|
@@ -0,0 +1,95 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Thu, 2 Apr 2020 02:37:57 -0400
|
||||
Subject: [PATCH] Optimize Collision to not load chunks
|
||||
|
||||
The collision code takes an AABB and generates a cuboid of checks rather
|
||||
than a cylinder, so at high velocity this can generate a lot of chunk checks.
|
||||
|
||||
Treat an unloaded chunk as a collision for entities, and also for players if
|
||||
the "prevent moving into unloaded chunks" setting is enabled.
|
||||
|
||||
If that serting is not enabled, collisions will be ignored for players, since
|
||||
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 2e5f1dc15ea6a20f8cbdef97ecbf00e5d56603cf..5a67aa9f1fe103e5622ed6fa93bc4bc25ddbb688 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -218,6 +218,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
// Paper end - Share random for entities to make them more random
|
||||
public org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason; // Paper - Entity#getEntitySpawnReason
|
||||
|
||||
+ public boolean collisionLoadChunks = false; // Paper
|
||||
private org.bukkit.craftbukkit.entity.CraftEntity bukkitEntity;
|
||||
|
||||
public org.bukkit.craftbukkit.entity.CraftEntity getBukkitEntity() {
|
||||
diff --git a/net/minecraft/world/level/BlockCollisions.java b/net/minecraft/world/level/BlockCollisions.java
|
||||
index fd2c338db43aad070cc32c24891b40599c544ac9..2861ea4b699d403b1245f8be5a62503d366ded65 100644
|
||||
--- a/net/minecraft/world/level/BlockCollisions.java
|
||||
+++ b/net/minecraft/world/level/BlockCollisions.java
|
||||
@@ -80,16 +80,37 @@ public class BlockCollisions<T> extends AbstractIterator<T> {
|
||||
@Override
|
||||
protected T computeNext() {
|
||||
while (this.cursor.advance()) {
|
||||
- int i = this.cursor.nextX();
|
||||
- int i1 = this.cursor.nextY();
|
||||
- int i2 = this.cursor.nextZ();
|
||||
+ int i = this.cursor.nextX(); final int x = i; // Paper - OBFHELPER
|
||||
+ int i1 = this.cursor.nextY(); final int y = i1; // Paper - OBFHELPER
|
||||
+ int i2 = this.cursor.nextZ(); final int z = i2; // Paper - OBFHELPER
|
||||
int nextType = this.cursor.getNextType();
|
||||
if (nextType != 3) {
|
||||
- BlockGetter chunk = this.getChunk(i, i2);
|
||||
- if (chunk != null) {
|
||||
- this.pos.set(i, i1, i2);
|
||||
- BlockState blockState = chunk.getBlockState(this.pos);
|
||||
- if ((!this.onlySuffocatingBlocks || blockState.isSuffocating(chunk, this.pos))
|
||||
+ // Paper start - ensure we don't load chunks
|
||||
+ // BlockGetter blockGetter = this.getChunk(i, k);
|
||||
+ if (true) {
|
||||
+ @Nullable final Entity source = this.context instanceof net.minecraft.world.phys.shapes.EntityCollisionContext entityContext ? entityContext.getEntity() : null;
|
||||
+ final boolean far = source != null && io.papermc.paper.util.MCUtil.distanceSq(source.getX(), y, source.getZ(), x, y, z) > 14;
|
||||
+ this.pos.set(x, y, z);
|
||||
+ BlockState blockState;
|
||||
+ if (this.collisionGetter instanceof net.minecraft.server.level.WorldGenRegion) {
|
||||
+ BlockGetter blockGetter = this.getChunk(x, z);
|
||||
+ if (blockGetter == null) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ blockState = blockGetter.getBlockState(this.pos);
|
||||
+ } else if ((!far && source instanceof net.minecraft.server.level.ServerPlayer) || (source != null && source.collisionLoadChunks)) {
|
||||
+ blockState = this.collisionGetter.getBlockState(this.pos);
|
||||
+ } else {
|
||||
+ blockState = this.collisionGetter.getBlockStateIfLoaded(this.pos);
|
||||
+ }
|
||||
+ if (blockState == null) {
|
||||
+ if (!(source instanceof net.minecraft.server.level.ServerPlayer) || source.level().paperConfig().chunks.preventMovingIntoUnloadedChunks) {
|
||||
+ return this.resultProvider.apply(new BlockPos.MutableBlockPos(x, y, z), Shapes.create(far ? source.getBoundingBox() : new AABB(new BlockPos(x, y, z))));
|
||||
+ }
|
||||
+ continue;
|
||||
+ }
|
||||
+ if (true // onlySuffocatingBlocks is only true on the client, so we don't care about it here
|
||||
+ // Paper end - ensure we don't load chunks
|
||||
&& (nextType != 1 || blockState.hasLargeCollisionShape())
|
||||
&& (nextType != 2 || blockState.is(Blocks.MOVING_PISTON))) {
|
||||
VoxelShape collisionShape = this.context.getCollisionShape(blockState, this.collisionGetter, this.pos);
|
||||
diff --git a/net/minecraft/world/level/CollisionGetter.java b/net/minecraft/world/level/CollisionGetter.java
|
||||
index cb54c3aadd8f3c719d3f7ef1fda4aa517919b7c3..844f76a38884e823a558fe59c421ffd4711f80b4 100644
|
||||
--- a/net/minecraft/world/level/CollisionGetter.java
|
||||
+++ b/net/minecraft/world/level/CollisionGetter.java
|
||||
@@ -50,11 +50,13 @@ public interface CollisionGetter extends BlockGetter {
|
||||
}
|
||||
|
||||
default boolean noCollision(@Nullable Entity entity, AABB collisionBox, boolean checkLiquid) {
|
||||
+ try { if (entity != null) entity.collisionLoadChunks = true; // Paper
|
||||
for (VoxelShape voxelShape : checkLiquid ? this.getBlockAndLiquidCollisions(entity, collisionBox) : this.getBlockCollisions(entity, collisionBox)) {
|
||||
if (!voxelShape.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+ } finally { if (entity != null) entity.collisionLoadChunks = false; } // Paper
|
||||
|
||||
if (!this.getEntityCollisions(entity, collisionBox).isEmpty()) {
|
||||
return false;
|
@@ -0,0 +1,168 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Mon, 6 Apr 2020 17:53:29 -0700
|
||||
Subject: [PATCH] Optimize GoalSelector Goal.Flag Set operations
|
||||
|
||||
Optimise the stream.anyMatch statement to move to a bitset
|
||||
where we can replace the call with a single bitwise operation.
|
||||
|
||||
diff --git a/net/minecraft/world/entity/ai/goal/Goal.java b/net/minecraft/world/entity/ai/goal/Goal.java
|
||||
index e99836c4073d8b25eddd3b4c784dbdb1f0c4f2b1..a2d788a656b2a5767c8a00bbcaca16efa2db8d24 100644
|
||||
--- a/net/minecraft/world/entity/ai/goal/Goal.java
|
||||
+++ b/net/minecraft/world/entity/ai/goal/Goal.java
|
||||
@@ -7,7 +7,15 @@ import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
public abstract class Goal {
|
||||
- private final EnumSet<Goal.Flag> flags = EnumSet.noneOf(Goal.Flag.class);
|
||||
+ private final ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<net.minecraft.world.entity.ai.goal.Goal.Flag> goalTypes = new ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from pathfindergoalselector
|
||||
+
|
||||
+ // Paper start - remove streams from pathfindergoalselector; make sure types are not empty
|
||||
+ protected Goal() {
|
||||
+ if (this.goalTypes.size() == 0) {
|
||||
+ this.goalTypes.addUnchecked(Flag.UNKNOWN_BEHAVIOR);
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - remove streams from pathfindergoalselector
|
||||
|
||||
public abstract boolean canUse();
|
||||
|
||||
@@ -33,8 +41,13 @@ public abstract class Goal {
|
||||
}
|
||||
|
||||
public void setFlags(EnumSet<Goal.Flag> flagSet) {
|
||||
- this.flags.clear();
|
||||
- this.flags.addAll(flagSet);
|
||||
+ // Paper start - remove streams from pathfindergoalselector
|
||||
+ this.goalTypes.clear();
|
||||
+ this.goalTypes.addAllUnchecked(flagSet);
|
||||
+ if (this.goalTypes.size() == 0) {
|
||||
+ this.goalTypes.addUnchecked(Flag.UNKNOWN_BEHAVIOR);
|
||||
+ }
|
||||
+ // Paper end - remove streams from pathfindergoalselector;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -42,18 +55,20 @@ public abstract class Goal {
|
||||
return this.getClass().getSimpleName();
|
||||
}
|
||||
|
||||
- public EnumSet<Goal.Flag> getFlags() {
|
||||
- return this.flags;
|
||||
+ // Paper start - remove streams from pathfindergoalselector
|
||||
+ public ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<Goal.Flag> getFlags() {
|
||||
+ return this.goalTypes;
|
||||
+ // Paper end - remove streams from pathfindergoalselector
|
||||
}
|
||||
|
||||
|
||||
// Paper start - Mob Goal API
|
||||
public boolean hasFlag(final Goal.Flag flag) {
|
||||
- return this.flags.contains(flag);
|
||||
+ return this.goalTypes.hasElement(flag);
|
||||
}
|
||||
|
||||
public void addFlag(final Goal.Flag flag) {
|
||||
- this.flags.add(flag);
|
||||
+ this.goalTypes.addUnchecked(flag);
|
||||
}
|
||||
|
||||
// Paper end - Mob Goal API
|
||||
diff --git a/net/minecraft/world/entity/ai/goal/GoalSelector.java b/net/minecraft/world/entity/ai/goal/GoalSelector.java
|
||||
index eeba224bd575451ba6023df65ef9d9b97f7f1c71..0846ad1f26ee73d72c77b53579a00c321d696b73 100644
|
||||
--- a/net/minecraft/world/entity/ai/goal/GoalSelector.java
|
||||
+++ b/net/minecraft/world/entity/ai/goal/GoalSelector.java
|
||||
@@ -24,7 +24,8 @@ public class GoalSelector {
|
||||
};
|
||||
private final Map<Goal.Flag, WrappedGoal> lockedFlags = new EnumMap<>(Goal.Flag.class);
|
||||
private final Set<WrappedGoal> availableGoals = new ObjectLinkedOpenHashSet<>();
|
||||
- private final EnumSet<Goal.Flag> disabledFlags = EnumSet.noneOf(Goal.Flag.class);
|
||||
+ private static final Goal.Flag[] GOAL_FLAG_VALUES = Goal.Flag.values(); // Paper - remove streams from pathfindergoalselector
|
||||
+ private final ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<net.minecraft.world.entity.ai.goal.Goal.Flag> goalTypes = new ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from pathfindergoalselector
|
||||
private int curRate; // Paper - EAR 2
|
||||
|
||||
public void addGoal(int priority, Goal goal) {
|
||||
@@ -62,18 +63,18 @@ public class GoalSelector {
|
||||
this.availableGoals.removeIf(wrappedGoal1 -> wrappedGoal1.getGoal() == goal);
|
||||
}
|
||||
|
||||
- private static boolean goalContainsAnyFlags(WrappedGoal goal, EnumSet<Goal.Flag> flag) {
|
||||
- for (Goal.Flag flag1 : goal.getFlags()) {
|
||||
- if (flag.contains(flag1)) {
|
||||
- return true;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- return false;
|
||||
+ // Paper start - Perf: optimize goal types
|
||||
+ private static boolean goalContainsAnyFlags(WrappedGoal goal, ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<Goal.Flag> flags) {
|
||||
+ return goal.getFlags().hasCommonElements(controls);
|
||||
}
|
||||
|
||||
private static boolean goalCanBeReplacedForAllFlags(WrappedGoal goal, Map<Goal.Flag, WrappedGoal> flag) {
|
||||
- for (Goal.Flag flag1 : goal.getFlags()) {
|
||||
+ long flagIterator = goal.getFlags().getBackingSet();
|
||||
+ int wrappedGoalSize = goal.getFlags().size();
|
||||
+ for (int i = 0; i < wrappedGoalSize; ++i) {
|
||||
+ final Goal.Flag flag1 = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)];
|
||||
+ flagIterator ^= ca.spottedleaf.concurrentutil.util.IntegerUtil.getTrailingBit(flagIterator);
|
||||
+ // Paper end - Perf: optimize goal types
|
||||
if (!flag.getOrDefault(flag1, NO_GOAL).canBeReplacedBy(goal)) {
|
||||
return false;
|
||||
}
|
||||
@@ -87,7 +88,7 @@ public class GoalSelector {
|
||||
profilerFiller.push("goalCleanup");
|
||||
|
||||
for (WrappedGoal wrappedGoal : this.availableGoals) {
|
||||
- if (wrappedGoal.isRunning() && (goalContainsAnyFlags(wrappedGoal, this.disabledFlags) || !wrappedGoal.canContinueToUse())) {
|
||||
+ if (wrappedGoal.isRunning() && (goalContainsAnyFlags(wrappedGoal, this.goalTypes) || !wrappedGoal.canContinueToUse())) { // Paper - Perf: optimize goal types by removing streams
|
||||
wrappedGoal.stop();
|
||||
}
|
||||
}
|
||||
@@ -97,11 +98,14 @@ public class GoalSelector {
|
||||
profilerFiller.push("goalUpdate");
|
||||
|
||||
for (WrappedGoal wrappedGoalx : this.availableGoals) {
|
||||
- if (!wrappedGoalx.isRunning()
|
||||
- && !goalContainsAnyFlags(wrappedGoalx, this.disabledFlags)
|
||||
- && goalCanBeReplacedForAllFlags(wrappedGoalx, this.lockedFlags)
|
||||
- && wrappedGoalx.canUse()) {
|
||||
- for (Goal.Flag flag : wrappedGoalx.getFlags()) {
|
||||
+ // Paper start
|
||||
+ if (!wrappedGoalx.isRunning() && !goalContainsAnyFlags(wrappedGoalx, this.goalTypes) && goalCanBeReplacedForAllFlags(wrappedGoalx, this.lockedFlags) && wrappedGoalx.canUse()) {
|
||||
+ long flagIterator = wrappedGoalx.getFlags().getBackingSet();
|
||||
+ int wrappedGoalSize = wrappedGoalx.getFlags().size();
|
||||
+ for (int i = 0; i < wrappedGoalSize; ++i) {
|
||||
+ final Goal.Flag flag = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)];
|
||||
+ flagIterator ^= ca.spottedleaf.concurrentutil.util.IntegerUtil.getTrailingBit(flagIterator);
|
||||
+ // Paper end
|
||||
WrappedGoal wrappedGoal1 = this.lockedFlags.getOrDefault(flag, NO_GOAL);
|
||||
wrappedGoal1.stop();
|
||||
this.lockedFlags.put(flag, wrappedGoalx);
|
||||
@@ -133,11 +137,11 @@ public class GoalSelector {
|
||||
}
|
||||
|
||||
public void disableControlFlag(Goal.Flag flag) {
|
||||
- this.disabledFlags.add(flag);
|
||||
+ this.goalTypes.addUnchecked(flag); // Paper - remove streams from pathfindergoalselector
|
||||
}
|
||||
|
||||
public void enableControlFlag(Goal.Flag flag) {
|
||||
- this.disabledFlags.remove(flag);
|
||||
+ this.goalTypes.removeUnchecked(flag); // Paper - remove streams from pathfindergoalselector
|
||||
}
|
||||
|
||||
public void setControlFlag(Goal.Flag flag, boolean enabled) {
|
||||
diff --git a/net/minecraft/world/entity/ai/goal/WrappedGoal.java b/net/minecraft/world/entity/ai/goal/WrappedGoal.java
|
||||
index 4bdbd323b642ed3422948fe24780be8b503602dc..5aaad796d2e2f7b57b1fd911a1498cdf235c36be 100644
|
||||
--- a/net/minecraft/world/entity/ai/goal/WrappedGoal.java
|
||||
+++ b/net/minecraft/world/entity/ai/goal/WrappedGoal.java
|
||||
@@ -69,7 +69,7 @@ public class WrappedGoal extends Goal {
|
||||
}
|
||||
|
||||
@Override
|
||||
- public EnumSet<Goal.Flag> getFlags() {
|
||||
+ public ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<Goal.Flag> getFlags() { // Paper - remove streams from pathfindergoalselector
|
||||
return this.goal.getFlags();
|
||||
}
|
||||
|
@@ -0,0 +1,122 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Sun, 3 May 2020 22:35:09 -0400
|
||||
Subject: [PATCH] Optimize Voxel Shape Merging
|
||||
|
||||
This method shows up as super hot in profiler, and also a high "self" time.
|
||||
|
||||
Upon analyzing, it appears most usages of this method fall down to the final
|
||||
else statement of the nasty ternary.
|
||||
|
||||
Upon even further analyzation, it appears then the majority of those have a
|
||||
consistent list 1.... One with Infinity head and Tails.
|
||||
|
||||
First optimization is to detect these infinite states and immediately return that
|
||||
VoxelShapeMergerList so we can avoid testing the rest for most cases.
|
||||
|
||||
Break the method into 2 to help the JVM promote inlining of this fast path.
|
||||
|
||||
Then it was also noticed that VoxelShapeMergerList constructor is also a hotspot
|
||||
with a high self time...
|
||||
|
||||
Well, knowing that in most cases our list 1 is actualy the same value, it allows
|
||||
us to know that with an infinite list1, the result on the merger is essentially
|
||||
list2 as the final values.
|
||||
|
||||
This let us analyze the 2 potential states (Infinite with 2 sources or 4 sources)
|
||||
and compute a deterministic result for the MergerList values.
|
||||
|
||||
Additionally, this lets us avoid even allocating new objects for this too, further
|
||||
reducing memory usage.
|
||||
|
||||
diff --git a/net/minecraft/world/phys/shapes/IndirectMerger.java b/net/minecraft/world/phys/shapes/IndirectMerger.java
|
||||
index 60b56a5086b8aad0fad693f686b89138b3a4c80d..5b079ec43c259368d0eed4e8385880712abda4f7 100644
|
||||
--- a/net/minecraft/world/phys/shapes/IndirectMerger.java
|
||||
+++ b/net/minecraft/world/phys/shapes/IndirectMerger.java
|
||||
@@ -10,12 +10,32 @@ public class IndirectMerger implements IndexMerger {
|
||||
private final int[] firstIndices;
|
||||
private final int[] secondIndices;
|
||||
private final int resultLength;
|
||||
+ // Paper start
|
||||
+ private static final int[] INFINITE_B_1 = {1, 1};
|
||||
+ private static final int[] INFINITE_B_0 = {0, 0};
|
||||
+ private static final int[] INFINITE_C = {0, 1};
|
||||
+ // Paper end
|
||||
|
||||
public IndirectMerger(DoubleList lower, DoubleList upper, boolean excludeUpper, boolean excludeLower) {
|
||||
double d = Double.NaN;
|
||||
int size = lower.size();
|
||||
int size1 = upper.size();
|
||||
int i = size + size1;
|
||||
+ // Paper start - optimize common path of infinity doublelist
|
||||
+ double tail = lower.getDouble(size - 1);
|
||||
+ double head = lower.getDouble(0);
|
||||
+ if (head == Double.NEGATIVE_INFINITY && tail == Double.POSITIVE_INFINITY && !excludeUpper && !excludeLower && (size == 2 || size == 4)) {
|
||||
+ this.result = upper.toDoubleArray();
|
||||
+ this.resultLength = upper.size();
|
||||
+ if (size == 2) {
|
||||
+ this.firstIndices = INFINITE_B_0;
|
||||
+ } else {
|
||||
+ this.firstIndices = INFINITE_B_1;
|
||||
+ }
|
||||
+ this.secondIndices = INFINITE_C;
|
||||
+ return;
|
||||
+ }
|
||||
+ // Paper end
|
||||
this.result = new double[i];
|
||||
this.firstIndices = new int[i];
|
||||
this.secondIndices = new int[i];
|
||||
diff --git a/net/minecraft/world/phys/shapes/Shapes.java b/net/minecraft/world/phys/shapes/Shapes.java
|
||||
index e1b4c4b53844b0755e0640a05e8782fd9a7700a2..e759221fb54aa510d2d8bbba47e1d794367aec6d 100644
|
||||
--- a/net/minecraft/world/phys/shapes/Shapes.java
|
||||
+++ b/net/minecraft/world/phys/shapes/Shapes.java
|
||||
@@ -279,9 +279,22 @@ public final class Shapes {
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
- protected static IndexMerger createIndexMerger(int size, DoubleList list1, DoubleList list2, boolean excludeUpper, boolean excludeLower) {
|
||||
+ private static IndexMerger createIndexMerger(int size, DoubleList list1, DoubleList list2, boolean excludeUpper, boolean excludeLower) { // Paper - private
|
||||
+ // Paper start - fast track the most common scenario
|
||||
+ // doublelist is usually a DoubleArrayList with Infinite head/tails that falls to the final else clause
|
||||
+ // This is actually the most common path, so jump to it straight away
|
||||
+ if (list1.getDouble(0) == Double.NEGATIVE_INFINITY && list1.getDouble(list1.size() - 1) == Double.POSITIVE_INFINITY) {
|
||||
+ return new IndirectMerger(list1, list2, excludeUpper, excludeLower);
|
||||
+ }
|
||||
+ // Split out rest to hopefully inline the above
|
||||
+ return lessCommonMerge(size, list1, list2, excludeUpper, excludeLower);
|
||||
+ }
|
||||
+
|
||||
+ private static IndexMerger lessCommonMerge(int size, DoubleList list1, DoubleList list2, boolean excludeUpper, boolean excludeLower) {
|
||||
+ // Paper end - fast track the most common scenario
|
||||
int i = list1.size() - 1;
|
||||
int i1 = list2.size() - 1;
|
||||
+ // Paper note - Rewrite below as optimized order if instead of nasty ternary
|
||||
if (list1 instanceof CubePointRange && list2 instanceof CubePointRange) {
|
||||
long l = lcm(i, i1);
|
||||
if (size * l <= 256L) {
|
||||
@@ -289,14 +302,21 @@ public final class Shapes {
|
||||
}
|
||||
}
|
||||
|
||||
- if (list1.getDouble(i) < list2.getDouble(0) - 1.0E-7) {
|
||||
+ // Paper start - Identical happens more often than Disjoint
|
||||
+ if (i == i1 && Objects.equals(list1, list2)) {
|
||||
+ if (list1 instanceof IdenticalMerger) {
|
||||
+ return (IndexMerger) list1;
|
||||
+ } else if (list2 instanceof IdenticalMerger) {
|
||||
+ return (IndexMerger) list2;
|
||||
+ }
|
||||
+ return new IdenticalMerger(list1);
|
||||
+ } else if (list1.getDouble(i) < list2.getDouble(0) - 1.0E-7) {
|
||||
+ // Paper end - Identical happens more often than Disjoint
|
||||
return new NonOverlappingMerger(list1, list2, false);
|
||||
} else if (list2.getDouble(i1) < list1.getDouble(0) - 1.0E-7) {
|
||||
return new NonOverlappingMerger(list2, list1, true);
|
||||
} else {
|
||||
- return (IndexMerger)(i == i1 && Objects.equals(list1, list2)
|
||||
- ? new IdenticalMerger(list1)
|
||||
- : new IndirectMerger(list1, list2, excludeUpper, excludeLower));
|
||||
+ return new IndirectMerger(list1, list2, excludeUpper, excludeLower); // Paper - Identical happens more often than Disjoint
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,152 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jake Potrebic <jake.m.potrebic@gmail.com>
|
||||
Date: Sun, 22 Aug 2021 15:21:57 -0700
|
||||
Subject: [PATCH] Fix entity type tags suggestions in selectors
|
||||
|
||||
This would really be better as a client fix because just to fix it
|
||||
all EntityArguments have been told to ask the server for completions
|
||||
when if this was fixed on the client, that wouldn't be needed.
|
||||
|
||||
Mojira Issue: https://bugs.mojang.com/browse/MC-235045
|
||||
|
||||
diff --git a/net/minecraft/commands/CommandSourceStack.java b/net/minecraft/commands/CommandSourceStack.java
|
||||
index 704a63890a06d793f8ac3452838917e7c7335232..75262c8c9eaecb4a88a94f4076d67119c67a97da 100644
|
||||
--- a/net/minecraft/commands/CommandSourceStack.java
|
||||
+++ b/net/minecraft/commands/CommandSourceStack.java
|
||||
@@ -652,4 +652,20 @@ public class CommandSourceStack implements ExecutionCommandSource<CommandSourceS
|
||||
return this.source.getBukkitSender(this);
|
||||
}
|
||||
// CraftBukkit end
|
||||
+ // Paper start - tell clients to ask server for suggestions for EntityArguments
|
||||
+ @Override
|
||||
+ public Collection<String> getSelectedEntities() {
|
||||
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().commands.fixTargetSelectorTagCompletion && this.source instanceof ServerPlayer player) {
|
||||
+ final Entity cameraEntity = player.getCamera();
|
||||
+ final double pickDistance = player.entityInteractionRange();
|
||||
+ final Vec3 min = cameraEntity.getEyePosition(1.0F);
|
||||
+ final Vec3 viewVector = cameraEntity.getViewVector(1.0F);
|
||||
+ final Vec3 max = min.add(viewVector.x * pickDistance, viewVector.y * pickDistance, viewVector.z * pickDistance);
|
||||
+ final net.minecraft.world.phys.AABB aabb = cameraEntity.getBoundingBox().expandTowards(viewVector.scale(pickDistance)).inflate(1.0D, 1.0D, 1.0D);
|
||||
+ final net.minecraft.world.phys.EntityHitResult hitResult = net.minecraft.world.entity.projectile.ProjectileUtil.getEntityHitResult(cameraEntity, min, max, aabb, (e) -> !e.isSpectator() && e.isPickable(), pickDistance * pickDistance);
|
||||
+ return hitResult != null ? java.util.Collections.singletonList(hitResult.getEntity().getStringUUID()) : SharedSuggestionProvider.super.getSelectedEntities();
|
||||
+ }
|
||||
+ return SharedSuggestionProvider.super.getSelectedEntities();
|
||||
+ }
|
||||
+ // Paper end - tell clients to ask server for suggestions for EntityArguments
|
||||
}
|
||||
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
|
||||
index 45fa9cdc34e78613e346138aa92a6d3bbdee374c..e422a266de555bbf77eee201df9e4c5d89f7b801 100644
|
||||
--- a/net/minecraft/commands/Commands.java
|
||||
+++ b/net/minecraft/commands/Commands.java
|
||||
@@ -510,6 +510,7 @@ public class Commands {
|
||||
Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> commandNodeToSuggestionNode
|
||||
) {
|
||||
commandNodeToSuggestionNode.keySet().removeIf((node) -> !org.spigotmc.SpigotConfig.sendNamespaced && node.getName().contains(":")); // Paper - Remove namedspaced from result nodes to prevent redirect trimming ~ see comment below
|
||||
+ boolean registeredAskServerSuggestionsForTree = false; // Paper - tell clients to ask server for suggestions for EntityArguments
|
||||
for (CommandNode<CommandSourceStack> commandNode : children) { // Paper - Perf: Async command map building; pass copy of children
|
||||
// Paper start - Brigadier API
|
||||
if (commandNode.clientNode != null) {
|
||||
@@ -572,6 +573,12 @@ public class Commands {
|
||||
RequiredArgumentBuilder<SharedSuggestionProvider, ?> requiredArgumentBuilder = (RequiredArgumentBuilder<SharedSuggestionProvider, ?>)argumentBuilder;
|
||||
if (requiredArgumentBuilder.getSuggestionsProvider() != null) {
|
||||
requiredArgumentBuilder.suggests(SuggestionProviders.safelySwap(requiredArgumentBuilder.getSuggestionsProvider()));
|
||||
+ // Paper start - tell clients to ask server for suggestions for EntityArguments
|
||||
+ registeredAskServerSuggestionsForTree = requiredArgumentBuilder.getSuggestionsProvider() == net.minecraft.commands.synchronization.SuggestionProviders.ASK_SERVER;
|
||||
+ } else if (io.papermc.paper.configuration.GlobalConfiguration.get().commands.fixTargetSelectorTagCompletion && !registeredAskServerSuggestionsForTree && requiredArgumentBuilder.getType() instanceof net.minecraft.commands.arguments.EntityArgument) {
|
||||
+ requiredArgumentBuilder.suggests(requiredArgumentBuilder.getType()::listSuggestions);
|
||||
+ registeredAskServerSuggestionsForTree = true; // You can only
|
||||
+ // Paper end - tell clients to ask server for suggestions for EntityArguments
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/net/minecraft/commands/arguments/EntityArgument.java b/net/minecraft/commands/arguments/EntityArgument.java
|
||||
index ba9a7636e158222bcfb50ae8487f29df6fdbdd0c..8957b99116cb087198fe372d4d2c2b3397fed19f 100644
|
||||
--- a/net/minecraft/commands/arguments/EntityArgument.java
|
||||
+++ b/net/minecraft/commands/arguments/EntityArgument.java
|
||||
@@ -138,7 +138,7 @@ public class EntityArgument implements ArgumentType<EntitySelector> {
|
||||
final boolean permission = sharedSuggestionProvider instanceof CommandSourceStack stack
|
||||
? stack.bypassSelectorPermissions || stack.hasPermission(2, "minecraft.command.selector")
|
||||
: sharedSuggestionProvider.hasPermission(2);
|
||||
- EntitySelectorParser entitySelectorParser = new EntitySelectorParser(stringReader, permission);
|
||||
+ EntitySelectorParser entitySelectorParser = new EntitySelectorParser(stringReader, permission, true); // Paper - tell clients to ask server for suggestions for EntityArguments
|
||||
// Paper end - Fix EntityArgument permissions
|
||||
|
||||
try {
|
||||
@@ -149,7 +149,19 @@ public class EntityArgument implements ArgumentType<EntitySelector> {
|
||||
return entitySelectorParser.fillSuggestions(
|
||||
builder,
|
||||
offsetBuilder -> {
|
||||
- Collection<String> onlinePlayerNames = sharedSuggestionProvider.getOnlinePlayerNames();
|
||||
+ // Paper start - tell clients to ask server for suggestions for EntityArguments
|
||||
+ final Collection<String> onlinePlayerNames;
|
||||
+ if (sharedSuggestionProvider instanceof CommandSourceStack commandSourceStack && commandSourceStack.getEntity() instanceof ServerPlayer sourcePlayer) {
|
||||
+ onlinePlayerNames = new java.util.ArrayList<>();
|
||||
+ for (final ServerPlayer player : commandSourceStack.getServer().getPlayerList().getPlayers()) {
|
||||
+ if (sourcePlayer.getBukkitEntity().canSee(player.getBukkitEntity())) {
|
||||
+ onlinePlayerNames.add(player.getGameProfile().getName());
|
||||
+ }
|
||||
+ }
|
||||
+ } else {
|
||||
+ onlinePlayerNames = sharedSuggestionProvider.getOnlinePlayerNames();
|
||||
+ }
|
||||
+ // Paper end - tell clients to ask server for suggestions for EntityArguments
|
||||
Iterable<String> iterable = (Iterable<String>)(this.playersOnly
|
||||
? onlinePlayerNames
|
||||
: Iterables.concat(onlinePlayerNames, sharedSuggestionProvider.getSelectedEntities()));
|
||||
diff --git a/net/minecraft/commands/arguments/selector/EntitySelectorParser.java b/net/minecraft/commands/arguments/selector/EntitySelectorParser.java
|
||||
index a6f232747df631f6afe440606bea94c588f1a0dd..fb42630741674c6cbd20b7d45d78dea1dc73a78f 100644
|
||||
--- a/net/minecraft/commands/arguments/selector/EntitySelectorParser.java
|
||||
+++ b/net/minecraft/commands/arguments/selector/EntitySelectorParser.java
|
||||
@@ -115,8 +115,15 @@ public class EntitySelectorParser {
|
||||
private boolean hasScores;
|
||||
private boolean hasAdvancements;
|
||||
private boolean usesSelectors;
|
||||
+ public boolean parsingEntityArgumentSuggestions; // Paper - tell clients to ask server for suggestions for EntityArguments
|
||||
|
||||
public EntitySelectorParser(StringReader reader, boolean allowSelectors) {
|
||||
+ // Paper start - tell clients to ask server for suggestions for EntityArguments
|
||||
+ this(reader, allowSelectors, false);
|
||||
+ }
|
||||
+ public EntitySelectorParser(StringReader reader, boolean allowSelectors, boolean parsingEntityArgumentSuggestions) {
|
||||
+ this.parsingEntityArgumentSuggestions = parsingEntityArgumentSuggestions;
|
||||
+ // Paper end - tell clients to ask server for suggestions for EntityArguments
|
||||
this.reader = reader;
|
||||
this.allowSelectors = allowSelectors;
|
||||
}
|
||||
diff --git a/net/minecraft/commands/arguments/selector/options/EntitySelectorOptions.java b/net/minecraft/commands/arguments/selector/options/EntitySelectorOptions.java
|
||||
index f6b58139aace70436034f0a16370236d975cb4ae..ee9949c41d38817b21b6f4fd728059a46fddf135 100644
|
||||
--- a/net/minecraft/commands/arguments/selector/options/EntitySelectorOptions.java
|
||||
+++ b/net/minecraft/commands/arguments/selector/options/EntitySelectorOptions.java
|
||||
@@ -76,6 +76,19 @@ public class EntitySelectorOptions {
|
||||
public static final DynamicCommandExceptionType ERROR_ENTITY_TYPE_INVALID = new DynamicCommandExceptionType(
|
||||
type -> Component.translatableEscape("argument.entity.options.type.invalid", type)
|
||||
);
|
||||
+ // Paper start - tell clients to ask server for suggestions for EntityArguments
|
||||
+ public static final DynamicCommandExceptionType ERROR_ENTITY_TAG_INVALID = new DynamicCommandExceptionType((object) -> {
|
||||
+ return io.papermc.paper.adventure.PaperAdventure
|
||||
+ .asVanilla(net.kyori.adventure.text.Component
|
||||
+ .text("Invalid or unknown entity type tag '" + object + "'")
|
||||
+ .hoverEvent(net.kyori.adventure.text.event.HoverEvent
|
||||
+ .showText(net.kyori.adventure.text.Component
|
||||
+ .text("You can disable this error in 'paper.yml'")
|
||||
+ )
|
||||
+ )
|
||||
+ );
|
||||
+ });
|
||||
+ // Paper end - tell clients to ask server for suggestions for EntityArguments
|
||||
|
||||
private static void register(String id, EntitySelectorOptions.Modifier handler, Predicate<EntitySelectorParser> predicate, Component tooltip) {
|
||||
OPTIONS.put(id, new EntitySelectorOptions.Option(handler, predicate, tooltip));
|
||||
@@ -299,6 +312,12 @@ public class EntitySelectorOptions {
|
||||
|
||||
if (parser.isTag()) {
|
||||
TagKey<EntityType<?>> tagKey = TagKey.create(Registries.ENTITY_TYPE, ResourceLocation.read(parser.getReader()));
|
||||
+ // Paper start - tell clients to ask server for suggestions for EntityArguments; throw error if invalid entity tag (only on suggestions to keep cmd success behavior)
|
||||
+ if (parser.parsingEntityArgumentSuggestions && io.papermc.paper.configuration.GlobalConfiguration.get().commands.fixTargetSelectorTagCompletion && net.minecraft.core.registries.BuiltInRegistries.ENTITY_TYPE.get(tagKey).isEmpty()) {
|
||||
+ parser.getReader().setCursor(cursor);
|
||||
+ throw ERROR_ENTITY_TAG_INVALID.createWithContext(parser.getReader(), tagKey);
|
||||
+ }
|
||||
+ // Paper end - tell clients to ask server for suggestions for EntityArguments
|
||||
parser.addPredicate(entity -> entity.getType().is(tagKey) != shouldInvertValue);
|
||||
} else {
|
||||
ResourceLocation resourceLocation = ResourceLocation.read(parser.getReader());
|
@@ -0,0 +1,64 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Wed, 6 May 2020 05:00:57 -0400
|
||||
Subject: [PATCH] Handle Oversized block entities in chunks
|
||||
|
||||
Splits out Extra Packets if too many TE's are encountered to prevent
|
||||
creating too large of a packet to sed.
|
||||
|
||||
Co-authored-by: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
|
||||
diff --git a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
||||
index 3aea76690bc3e35758d3bf274777130af17d8a0f..9e321ef1c3d5803519b243685f4ee598dc0cf640 100644
|
||||
--- a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
||||
+++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
||||
@@ -27,6 +27,14 @@ public class ClientboundLevelChunkPacketData {
|
||||
private final CompoundTag heightmaps;
|
||||
private final byte[] buffer;
|
||||
private final List<ClientboundLevelChunkPacketData.BlockEntityInfo> blockEntitiesData;
|
||||
+ // Paper start - Handle oversized block entities in chunks
|
||||
+ private final java.util.List<net.minecraft.network.protocol.Packet<?>> extraPackets = new java.util.ArrayList<>();
|
||||
+ private static final int BLOCK_ENTITY_LIMIT = Integer.getInteger("Paper.excessiveTELimit", 750);
|
||||
+
|
||||
+ public List<net.minecraft.network.protocol.Packet<?>> getExtraPackets() {
|
||||
+ return this.extraPackets;
|
||||
+ }
|
||||
+ // Paper end - Handle oversized block entities in chunks
|
||||
|
||||
// Paper start - Anti-Xray - Add chunk packet info
|
||||
@Deprecated @io.papermc.paper.annotation.DoNotUse
|
||||
@@ -50,8 +58,18 @@ public class ClientboundLevelChunkPacketData {
|
||||
}
|
||||
extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), levelChunk, chunkPacketInfo);
|
||||
this.blockEntitiesData = Lists.newArrayList();
|
||||
+ int totalTileEntities = 0; // Paper - Handle oversized block entities in chunks
|
||||
|
||||
for (Entry<BlockPos, BlockEntity> entryx : levelChunk.getBlockEntities().entrySet()) {
|
||||
+ // Paper start - Handle oversized block entities in chunks
|
||||
+ if (++totalTileEntities > BLOCK_ENTITY_LIMIT) {
|
||||
+ net.minecraft.network.protocol.Packet<ClientGamePacketListener> packet = entryx.getValue().getUpdatePacket();
|
||||
+ if (packet != null) {
|
||||
+ this.extraPackets.add(packet);
|
||||
+ continue;
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - Handle oversized block entities in chunks
|
||||
this.blockEntitiesData.add(ClientboundLevelChunkPacketData.BlockEntityInfo.create(entryx.getValue()));
|
||||
}
|
||||
}
|
||||
diff --git a/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java b/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
|
||||
index d2d21fe8d7275b01454e09be252d7dd7710cdc2d..5eef540242413df3ed136aa8837866a94cc285b3 100644
|
||||
--- a/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
|
||||
+++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
|
||||
@@ -84,4 +84,11 @@ public class ClientboundLevelChunkWithLightPacket implements Packet<ClientGamePa
|
||||
public ClientboundLightUpdatePacketData getLightData() {
|
||||
return this.lightData;
|
||||
}
|
||||
+
|
||||
+ // Paper start - Handle oversized block entities in chunks
|
||||
+ @Override
|
||||
+ public java.util.List<Packet<?>> getExtraPackets() {
|
||||
+ return this.chunkData.getExtraPackets();
|
||||
+ }
|
||||
+ // Paper end - Handle oversized block entities in chunks
|
||||
}
|
@@ -0,0 +1,68 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Mon, 2 Aug 2021 10:10:40 +0200
|
||||
Subject: [PATCH] Check distance in entity interactions
|
||||
|
||||
|
||||
diff --git a/net/minecraft/Util.java b/net/minecraft/Util.java
|
||||
index 60952bd49a89b8d6247d0c8bac837e5b3d586a76..fe84fe69a2a9ed95ec45a9e5af6e6f5a5a74edda 100644
|
||||
--- a/net/minecraft/Util.java
|
||||
+++ b/net/minecraft/Util.java
|
||||
@@ -130,6 +130,7 @@ public class Util {
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalStateException("No jar file system provider found"));
|
||||
private static Consumer<String> thePauser = string -> {};
|
||||
+ public static final double COLLISION_EPSILON = 1.0E-7; // Paper - Check distance in entity interactions
|
||||
|
||||
public static <K, V> Collector<Entry<? extends K, ? extends V>, ?, Map<K, V>> toMap() {
|
||||
return Collectors.toMap(Entry::getKey, Entry::getValue);
|
||||
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
|
||||
index 9de400977ec33e485e87cdf1cf145588527e1e10..c83aeaf4e50dd7290c608dfe260a3bd2404b6004 100644
|
||||
--- a/net/minecraft/world/entity/LivingEntity.java
|
||||
+++ b/net/minecraft/world/entity/LivingEntity.java
|
||||
@@ -1385,7 +1385,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
||||
this.hurtCurrentlyUsedShield(amount);
|
||||
f1 = amount;
|
||||
amount = 0.0F;
|
||||
- if (!damageSource.is(DamageTypeTags.IS_PROJECTILE) && damageSource.getDirectEntity() instanceof LivingEntity livingEntity) {
|
||||
+ if (!damageSource.is(DamageTypeTags.IS_PROJECTILE) && damageSource.getDirectEntity() instanceof LivingEntity livingEntity && livingEntity.distanceToSqr(this) <= (200.0D * 200.0D)) { // Paper - Check distance in entity interactions
|
||||
this.blockUsingShield(livingEntity);
|
||||
}
|
||||
|
||||
@@ -1470,6 +1470,14 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
||||
d = damageSource.getSourcePosition().x() - this.getX();
|
||||
d1 = damageSource.getSourcePosition().z() - this.getZ();
|
||||
}
|
||||
+ // Paper start - Check distance in entity interactions; see for loop in knockback method
|
||||
+ if (Math.abs(d) > 200) {
|
||||
+ d = Math.random() - Math.random();
|
||||
+ }
|
||||
+ if (Math.abs(d1) > 200) {
|
||||
+ d1 = Math.random() - Math.random();
|
||||
+ }
|
||||
+ // Paper end - Check distance in entity interactions
|
||||
|
||||
this.knockback(0.4F, d, d1, damageSource.getDirectEntity(), damageSource.getDirectEntity() == null ? io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.DAMAGE : io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.ENTITY_ATTACK); // CraftBukkit // Paper - knockback events
|
||||
if (!flag) {
|
||||
@@ -2345,7 +2353,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
||||
this.hurtCurrentlyUsedShield((float) -event.getDamage(DamageModifier.BLOCKING));
|
||||
Entity entity = damageSource.getDirectEntity();
|
||||
|
||||
- if (!damageSource.is(DamageTypeTags.IS_PROJECTILE) && entity instanceof LivingEntity) { // Paper - Fix shield disable inconsistency
|
||||
+ if (!damageSource.is(DamageTypeTags.IS_PROJECTILE) && entity instanceof LivingEntity && entity.distanceToSqr(this) <= (200.0D * 200.0D)) { // Paper - Fix shield disable inconsistency & Check distance in entity interactions
|
||||
this.blockUsingShield((LivingEntity) entity);
|
||||
}
|
||||
}
|
||||
diff --git a/net/minecraft/world/entity/vehicle/AbstractBoat.java b/net/minecraft/world/entity/vehicle/AbstractBoat.java
|
||||
index 5cd65e94ac7830aaa2a64057fc2a81478b55ea41..b9cb86717d7e6c05eb97f3b1bbf1d0111a0ba6ed 100644
|
||||
--- a/net/minecraft/world/entity/vehicle/AbstractBoat.java
|
||||
+++ b/net/minecraft/world/entity/vehicle/AbstractBoat.java
|
||||
@@ -641,7 +641,7 @@ public abstract class AbstractBoat extends VehicleEntity implements Leashable {
|
||||
this.waterLevel = this.getY(1.0);
|
||||
double d2 = this.getWaterLevelAbove() - this.getBbHeight() + 0.101;
|
||||
if (this.level().noCollision(this, this.getBoundingBox().move(0.0, d2 - this.getY(), 0.0))) {
|
||||
- this.setPos(this.getX(), d2, this.getZ());
|
||||
+ this.move(MoverType.SELF, new Vec3(0.0D, d2 - this.getY(), 0.0D)); // Paper - Check distance in entity interactions // TODO Still needed??
|
||||
this.setDeltaMovement(this.getDeltaMovement().multiply(1.0, 0.0, 1.0));
|
||||
this.lastYd = 0.0;
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: lukas81298 <lukas81298@gommehd.net>
|
||||
Date: Fri, 22 Jan 2021 21:50:18 +0100
|
||||
Subject: [PATCH] optimize dirt and snow spreading
|
||||
|
||||
|
||||
diff --git a/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java b/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java
|
||||
index 11937aa74efe08bdbd66a619c7a825f91d971afd..722f2b9a24679e0fc67aae2cd27051f96f962efe 100644
|
||||
--- a/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java
|
||||
+++ b/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java
|
||||
@@ -17,8 +17,13 @@ public abstract class SpreadingSnowyDirtBlock extends SnowyDirtBlock {
|
||||
}
|
||||
|
||||
private static boolean canBeGrass(BlockState state, LevelReader levelReader, BlockPos pos) {
|
||||
+ // Paper start - Perf: optimize dirt and snow spreading
|
||||
+ return canBeGrass(levelReader.getChunk(pos), state, levelReader, pos);
|
||||
+ }
|
||||
+ private static boolean canBeGrass(net.minecraft.world.level.chunk.ChunkAccess chunk, BlockState state, LevelReader levelReader, BlockPos pos) {
|
||||
+ // Paper end - Perf: optimize dirt and snow spreading
|
||||
BlockPos blockPos = pos.above();
|
||||
- BlockState blockState = levelReader.getBlockState(blockPos);
|
||||
+ BlockState blockState = chunk.getBlockState(blockPos); // Paper - Perf: optimize dirt and snow spreading
|
||||
if (blockState.is(Blocks.SNOW) && blockState.getValue(SnowLayerBlock.LAYERS) == 1) {
|
||||
return true;
|
||||
} else if (blockState.getFluidState().getAmount() == 8) {
|
||||
@@ -33,14 +38,27 @@ public abstract class SpreadingSnowyDirtBlock extends SnowyDirtBlock {
|
||||
protected abstract MapCodec<? extends SpreadingSnowyDirtBlock> codec();
|
||||
|
||||
private static boolean canPropagate(BlockState state, LevelReader level, BlockPos pos) {
|
||||
+ // Paper start - Perf: optimize dirt and snow spreading
|
||||
+ return canPropagate(level.getChunk(pos), state, level, pos);
|
||||
+ }
|
||||
+
|
||||
+ private static boolean canPropagate(net.minecraft.world.level.chunk.ChunkAccess chunk, BlockState state, LevelReader level, BlockPos pos) {
|
||||
+ // Paper end - Perf: optimize dirt and snow spreading
|
||||
BlockPos blockPos = pos.above();
|
||||
- return canBeGrass(state, level, pos) && !level.getFluidState(blockPos).is(FluidTags.WATER);
|
||||
+ return canBeGrass(chunk, state, level, pos) && !chunk.getFluidState(blockPos).is(FluidTags.WATER); // Paper - Perf: optimize dirt and snow spreading
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
|
||||
if (this instanceof GrassBlock && level.paperConfig().tickRates.grassSpread != 1 && (level.paperConfig().tickRates.grassSpread < 1 || (net.minecraft.server.MinecraftServer.currentTick + pos.hashCode()) % level.paperConfig().tickRates.grassSpread != 0)) { return; } // Paper - Configurable random tick rates for blocks
|
||||
- if (!canBeGrass(state, level, pos)) {
|
||||
+ // Paper start - Perf: optimize dirt and snow spreading
|
||||
+ final net.minecraft.world.level.chunk.ChunkAccess cachedBlockChunk = level.getChunkIfLoaded(pos);
|
||||
+ if (cachedBlockChunk == null) { // Is this needed?
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (!canBeGrass(cachedBlockChunk, state, level, pos)) {
|
||||
+ // Paper end - Perf: optimize dirt and snow spreading
|
||||
// CraftBukkit start
|
||||
if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(level, pos, Blocks.DIRT.defaultBlockState()).isCancelled()) {
|
||||
return;
|
||||
@@ -53,8 +71,20 @@ public abstract class SpreadingSnowyDirtBlock extends SnowyDirtBlock {
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
BlockPos blockPos = pos.offset(random.nextInt(3) - 1, random.nextInt(5) - 3, random.nextInt(3) - 1);
|
||||
- if (level.getBlockState(blockPos).is(Blocks.DIRT) && canPropagate(blockState, level, blockPos)) {
|
||||
- org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, blockPos, blockState.setValue(SNOWY, Boolean.valueOf(isSnowySetting(level.getBlockState(blockPos.above()))))); // CraftBukkit
|
||||
+ // Paper start - Perf: optimize dirt and snow spreading
|
||||
+ if (pos.getX() == blockPos.getX() && pos.getY() == blockPos.getY() && pos.getZ() == blockPos.getZ()) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ final net.minecraft.world.level.chunk.ChunkAccess access;
|
||||
+ if (cachedBlockChunk.locX == blockPos.getX() >> 4 && cachedBlockChunk.locZ == blockPos.getZ() >> 4) {
|
||||
+ access = cachedBlockChunk;
|
||||
+ } else {
|
||||
+ access = level.getChunkAt(blockPos);
|
||||
+ }
|
||||
+ if (access.getBlockState(blockPos).is(Blocks.DIRT) && SpreadingSnowyDirtBlock.canPropagate(access, blockState, level, blockPos)) {
|
||||
+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, blockPos, (BlockState) blockState.setValue(SpreadingSnowyDirtBlock.SNOWY, isSnowySetting(access.getBlockState(blockPos.above())))); // CraftBukkit
|
||||
+ // Paper end - Perf: optimize dirt and snow spreading
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Sat, 25 Jan 2020 17:04:35 -0800
|
||||
Subject: [PATCH] Optimise getChunkAt calls for loaded chunks
|
||||
|
||||
bypass the need to get a player chunk, then get the either,
|
||||
then unwrap it...
|
||||
|
||||
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
|
||||
index d310e7489fc4ecede8deef59241444769d87b0a1..796b5f8541b0cf84482ab2b5a60adde544d43593 100644
|
||||
--- a/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -218,6 +218,12 @@ public class ServerChunkCache extends ChunkSource {
|
||||
if (Thread.currentThread() != this.mainThread) {
|
||||
return CompletableFuture.<ChunkAccess>supplyAsync(() -> this.getChunk(x, z, chunkStatus, requireChunk), this.mainThreadProcessor).join();
|
||||
} else {
|
||||
+ // Paper start - Perf: Optimise getChunkAt calls for loaded chunks
|
||||
+ LevelChunk ifLoaded = this.getChunkAtIfCachedImmediately(x, z);
|
||||
+ if (ifLoaded != null) {
|
||||
+ return ifLoaded;
|
||||
+ }
|
||||
+ // Paper end - Perf: Optimise getChunkAt calls for loaded chunks
|
||||
ProfilerFiller profilerFiller = Profiler.get();
|
||||
profilerFiller.incrementCounter("getChunk");
|
||||
long packedChunkPos = ChunkPos.asLong(x, z);
|
||||
@@ -252,30 +258,7 @@ public class ServerChunkCache extends ChunkSource {
|
||||
if (Thread.currentThread() != this.mainThread) {
|
||||
return null;
|
||||
} else {
|
||||
- Profiler.get().incrementCounter("getChunkNow");
|
||||
- long packedChunkPos = ChunkPos.asLong(chunkX, chunkZ);
|
||||
-
|
||||
- for (int i = 0; i < 4; i++) {
|
||||
- if (packedChunkPos == this.lastChunkPos[i] && this.lastChunkStatus[i] == ChunkStatus.FULL) {
|
||||
- ChunkAccess chunkAccess = this.lastChunk[i];
|
||||
- return chunkAccess instanceof LevelChunk ? (LevelChunk)chunkAccess : null;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- ChunkHolder visibleChunkIfPresent = this.getVisibleChunkIfPresent(packedChunkPos);
|
||||
- if (visibleChunkIfPresent == null) {
|
||||
- return null;
|
||||
- } else {
|
||||
- ChunkAccess chunkAccess = visibleChunkIfPresent.getChunkIfPresent(ChunkStatus.FULL);
|
||||
- if (chunkAccess != null) {
|
||||
- this.storeInCache(packedChunkPos, chunkAccess, ChunkStatus.FULL);
|
||||
- if (chunkAccess instanceof LevelChunk) {
|
||||
- return (LevelChunk)chunkAccess;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- return null;
|
||||
- }
|
||||
+ return this.getChunkAtIfCachedImmediately(chunkX, chunkZ); // Paper - Perf: Optimise getChunkAt calls for loaded chunks
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user