[ci skip] Cleanup unapplied patches
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: MrPowerGamerBR <git@mrpowergamerbr.com>
|
||||
Date: Sun, 18 Feb 2024 14:22:37 -0300
|
||||
Subject: [PATCH] Revert "Fix MC-117075: Block entity unload lag spike"
|
||||
|
||||
This reverts commit f3453b204569ea865cc1d1302edb6d125e7f0cb3.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
|
||||
index 8433caa61a973d81a5eedc44428926e999c21a03..db9eb10c33848ca5b342bf00e4a05738210d26b0 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/Level.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
||||
@@ -1458,8 +1458,6 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
||||
int tickedEntities = 0; // Paper - rewrite chunk system
|
||||
|
||||
int tilesThisCycle = 0;
|
||||
- var toRemove = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<TickingBlockEntity>(); // Paper - Fix MC-117075; use removeAll
|
||||
- toRemove.add(null); // Paper - Fix MC-117075
|
||||
for (tileTickPosition = 0; tileTickPosition < this.blockEntityTickers.size(); tileTickPosition++) { // Paper - Disable tick limiters
|
||||
this.tileTickPosition = (this.tileTickPosition < this.blockEntityTickers.size()) ? this.tileTickPosition : 0;
|
||||
TickingBlockEntity tickingblockentity = (TickingBlockEntity) this.blockEntityTickers.get(this.tileTickPosition);
|
||||
@@ -1468,7 +1466,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
||||
if (tickingblockentity.isRemoved()) {
|
||||
// Spigot start
|
||||
tilesThisCycle--;
|
||||
- toRemove.add(tickingblockentity); // Paper - Fix MC-117075; use removeAll
|
||||
+ this.blockEntityTickers.remove(this.tileTickPosition--);
|
||||
// Spigot end
|
||||
} else if (flag && this.shouldTickBlocksAt(tickingblockentity.getPos())) {
|
||||
tickingblockentity.tick();
|
||||
@@ -1479,7 +1477,6 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
||||
// Paper end - rewrite chunk system
|
||||
}
|
||||
}
|
||||
- this.blockEntityTickers.removeAll(toRemove); // Paper - Fix MC-117075
|
||||
|
||||
this.tickingBlockEntities = false;
|
||||
gameprofilerfiller.pop();
|
||||
@@ -0,0 +1,123 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: MrPowerGamerBR <git@mrpowergamerbr.com>
|
||||
Date: Sun, 26 Nov 2023 13:11:10 -0300
|
||||
Subject: [PATCH] Optimize tickBlockEntities
|
||||
|
||||
We cache the last `shouldTickBlocksAt` result, because the `shouldTickBlocksAt` is expensive because it requires pulling chunk holder info from an map for each block entity (even if the block entities are on the same chunk!) every single time. So, if the last chunk position is the same as our cached value, we use the last cached `shouldTickBlocksAt` result!
|
||||
|
||||
We could use a map for caching, but here's why this is way better than using a map: The block entity ticking list is sorted by chunks! Well, sort of... It is sorted by chunk when the chunk has been loaded, newly placed blocks will be appended to the end of the list until the chunk unloads and loads again. Most block entities are things that players placed to be there for a long time anyway (like hoppers, etc)
|
||||
|
||||
But here's the thing: We don't care if we have a small performance penalty if the players have placed new block entities, the small performance hit of when a player placed new block entities is so small ('tis just a long comparsion after all), that the performance boost from already placed block entities is bigger, this helps a lot if your server has a lot of chunks with multiple block entities, and the block entities will be automatically sorted after the chunk is unloaded and loaded again, so it ain't that bad.
|
||||
|
||||
And finally, we also cache the chunk's coordinate key when creating the block entity, which is actually "free" because we just reuse the already cached chunk coordinate key from the chunk!
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
|
||||
index 1e20c525cd0c75079fe971ae830120c69fee362e..97b31dffbaf965e86ad706a1bba7586cd3514ead 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/Level.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
||||
@@ -1458,6 +1458,10 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
||||
int tickedEntities = 0; // Paper - rewrite chunk system
|
||||
|
||||
int tilesThisCycle = 0;
|
||||
+ // SparklyPaper start - optimize tickBlockEntities
|
||||
+ int shouldTickBlocksAtLastResult = -1; // -1 = undefined
|
||||
+ long shouldTickBlocksAtChunkPos = 0L;
|
||||
+ // SparklyPaper end
|
||||
for (tileTickPosition = 0; tileTickPosition < this.blockEntityTickers.size(); tileTickPosition++) { // Paper - Disable tick limiters
|
||||
this.tileTickPosition = (this.tileTickPosition < this.blockEntityTickers.size()) ? this.tileTickPosition : 0;
|
||||
TickingBlockEntity tickingblockentity = (TickingBlockEntity) this.blockEntityTickers.get(this.tileTickPosition);
|
||||
@@ -1468,13 +1472,25 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
||||
tilesThisCycle--;
|
||||
this.blockEntityTickers.markAsRemoved(this.tileTickPosition); // this.blockEntityTickers.remove(this.tileTickPosition--); // SparklyPaper - optimize block entity removals
|
||||
// Spigot end
|
||||
- } else if (flag && this.shouldTickBlocksAt(tickingblockentity.getPos())) {
|
||||
+ // } else if (flag && this.shouldTickBlocksAt(tickingblockentity.getPos())) { // SparklyPaper start - optimize tickBlockEntities
|
||||
+ } else if (flag) {
|
||||
+ long chunkPos = tickingblockentity.getChunkCoordinateKey();
|
||||
+ boolean shouldTick;
|
||||
+ if (shouldTickBlocksAtChunkPos == chunkPos && shouldTickBlocksAtLastResult != -1) {
|
||||
+ shouldTick = shouldTickBlocksAtLastResult == 1;
|
||||
+ } else {
|
||||
+ shouldTick = this.shouldTickBlocksAt(chunkPos);
|
||||
+ shouldTickBlocksAtLastResult = shouldTick ? 1 : 0;
|
||||
+ shouldTickBlocksAtChunkPos = chunkPos;
|
||||
+ }
|
||||
+ if (shouldTick) {
|
||||
tickingblockentity.tick();
|
||||
// Paper start - rewrite chunk system
|
||||
if ((++tickedEntities & 7) == 0) {
|
||||
((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel)(Level)(Object)this).moonrise$midTickTasks();
|
||||
}
|
||||
// Paper end - rewrite chunk system
|
||||
+ } // SparklyPaper end
|
||||
}
|
||||
}
|
||||
this.blockEntityTickers.removeMarkedEntries(); // SparklyPaper - optimize block entity removals
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
index 4640baec5bed6c2d53cc0f8ca1d273cc115abe9b..b4c16fe7c9215b5610b7e7488c29b497b5357ecc 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
@@ -75,6 +75,13 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
|
||||
public String getType() {
|
||||
return "<null>";
|
||||
}
|
||||
+
|
||||
+ // SparklyPaper start - optimize tickBlockEntities
|
||||
+ @Override
|
||||
+ public long getChunkCoordinateKey() {
|
||||
+ return 0;
|
||||
+ }
|
||||
+ // SparklyPaper end
|
||||
};
|
||||
private final Map<BlockPos, LevelChunk.RebindableTickingBlockEntityWrapper> tickersInLevel;
|
||||
public boolean loaded;
|
||||
@@ -981,7 +988,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
|
||||
}
|
||||
|
||||
private <T extends BlockEntity> TickingBlockEntity createTicker(T blockEntity, BlockEntityTicker<T> blockEntityTicker) {
|
||||
- return new LevelChunk.BoundTickingBlockEntity<>(blockEntity, blockEntityTicker);
|
||||
+ return new LevelChunk.BoundTickingBlockEntity<>(blockEntity, blockEntityTicker, this.coordinateKey); // SparklyPaper - optimize tickBlockEntities
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
@@ -1038,6 +1045,13 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
|
||||
public String toString() {
|
||||
return String.valueOf(this.ticker) + " <wrapped>";
|
||||
}
|
||||
+
|
||||
+ // SparklyPaper start - optimize tickBlockEntities
|
||||
+ @Override
|
||||
+ public long getChunkCoordinateKey() {
|
||||
+ return this.ticker.getChunkCoordinateKey();
|
||||
+ }
|
||||
+ // SparklyPaper end
|
||||
}
|
||||
|
||||
private class BoundTickingBlockEntity<T extends BlockEntity> implements TickingBlockEntity {
|
||||
@@ -1045,10 +1059,12 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
|
||||
private final T blockEntity;
|
||||
private final BlockEntityTicker<T> ticker;
|
||||
private boolean loggedInvalidBlockState;
|
||||
+ private final long chunkCoordinateKey; // SparklyPaper - optimize tickBlockEntities
|
||||
|
||||
- BoundTickingBlockEntity(final BlockEntity tileentity, final BlockEntityTicker blockentityticker) {
|
||||
+ BoundTickingBlockEntity(final BlockEntity tileentity, final BlockEntityTicker blockentityticker, long chunkCoordinateKey) { // SparklyPaper - optimize tickBlockEntities
|
||||
this.blockEntity = (T) tileentity; // CraftBukkit - decompile error
|
||||
this.ticker = blockentityticker;
|
||||
+ this.chunkCoordinateKey = chunkCoordinateKey; // SparklyPaper - optimize tickBlockEntities
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1112,5 +1128,12 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
|
||||
|
||||
return "Level ticker for " + s + "@" + String.valueOf(this.getPos());
|
||||
}
|
||||
+
|
||||
+ // SparklyPaper start - optimize tickBlockEntities
|
||||
+ @Override
|
||||
+ public long getChunkCoordinateKey() {
|
||||
+ return this.chunkCoordinateKey;
|
||||
+ }
|
||||
+ // SparklyPaper end
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: MrPowerGamerBR <git@mrpowergamerbr.com>
|
||||
Date: Mon, 10 Jun 2024 13:06:30 -0300
|
||||
Subject: [PATCH] Helpful NMS packet changes
|
||||
|
||||
Some nice changes to the packet internals to make packet sending and manipulation easier for us to avoid Reflection and JVM internals (ooo theUnsafe spooky) usage
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundAddEntityPacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundAddEntityPacket.java
|
||||
index f66e40326c510aa3267542b1a24ed75d1ed6d3f1..797640c4f26abb32a480a611820bbcd72e43d1ac 100644
|
||||
--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundAddEntityPacket.java
|
||||
+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundAddEntityPacket.java
|
||||
@@ -22,7 +22,7 @@ public class ClientboundAddEntityPacket implements Packet<ClientGamePacketListen
|
||||
private static final double LIMIT = 3.9;
|
||||
private final int id;
|
||||
private final UUID uuid;
|
||||
- private final EntityType<?> type;
|
||||
+ public EntityType<?> type; // SparklyPaper - Helpful NMS packet changes: remove final and make public
|
||||
private final double x;
|
||||
private final double y;
|
||||
private final double z;
|
||||
@@ -180,6 +180,32 @@ public class ClientboundAddEntityPacket implements Packet<ClientGamePacketListen
|
||||
return Mth.unpackDegrees(this.yHeadRot);
|
||||
}
|
||||
|
||||
+ // SparklyPaper - Helpful NMS packet changes: expose raw rotational fields
|
||||
+ public int getXaRaw() {
|
||||
+ return this.xa;
|
||||
+ }
|
||||
+
|
||||
+ public int getYaRaw() {
|
||||
+ return this.ya;
|
||||
+ }
|
||||
+
|
||||
+ public int getZaRaw() {
|
||||
+ return this.za;
|
||||
+ }
|
||||
+
|
||||
+ public byte getXRotRaw() {
|
||||
+ return this.xRot;
|
||||
+ }
|
||||
+
|
||||
+ public byte getYRotRaw() {
|
||||
+ return this.yRot;
|
||||
+ }
|
||||
+
|
||||
+ public byte getYHeadRotRaw() {
|
||||
+ return this.yHeadRot;
|
||||
+ }
|
||||
+ // SparklyPaper end
|
||||
+
|
||||
public int getData() {
|
||||
return this.data;
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundBlockUpdatePacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundBlockUpdatePacket.java
|
||||
index 1e8fad30c5f5be48501c7d8584caedcdc232f6c8..772848cf83a92a8ef8d734949b21f2f1d13e3fcb 100644
|
||||
--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundBlockUpdatePacket.java
|
||||
+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundBlockUpdatePacket.java
|
||||
@@ -19,7 +19,7 @@ public class ClientboundBlockUpdatePacket implements Packet<ClientGamePacketList
|
||||
ClientboundBlockUpdatePacket::new
|
||||
);
|
||||
private final BlockPos pos;
|
||||
- public final BlockState blockState;
|
||||
+ public BlockState blockState; // SparklyPaper - Helpful NMS packet changes: remove final
|
||||
|
||||
public ClientboundBlockUpdatePacket(BlockPos pos, BlockState state) {
|
||||
this.pos = pos;
|
||||
diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
||||
index 0a8d07bf68b0ceabd13c70196d357fce79dcc2c3..0b5abaf11508fa6c6809b73f53d6854aa3b3247c 100644
|
||||
--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
||||
+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
||||
@@ -25,7 +25,7 @@ import net.minecraft.world.level.levelgen.Heightmap;
|
||||
public class ClientboundLevelChunkPacketData {
|
||||
private static final int TWO_MEGABYTES = 2097152;
|
||||
private final CompoundTag heightmaps;
|
||||
- private final byte[] buffer;
|
||||
+ public byte[] buffer; // SparklyPaper - Helpful NMS packet changes: remove final and make public
|
||||
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<>();
|
||||
diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundRotateHeadPacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundRotateHeadPacket.java
|
||||
index ab44c24ce5f4570dee9d84b4216299bedfa800d8..99fbb958b82a3398564febb1e87e3ef4efca5b1a 100644
|
||||
--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundRotateHeadPacket.java
|
||||
+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundRotateHeadPacket.java
|
||||
@@ -20,6 +20,13 @@ public class ClientboundRotateHeadPacket implements Packet<ClientGamePacketListe
|
||||
this.yHeadRot = headYaw;
|
||||
}
|
||||
|
||||
+ // SparklyPaper start - Helpful NMS packet changes: add entity ID constructor
|
||||
+ public ClientboundRotateHeadPacket(int entityId, byte headYaw) {
|
||||
+ this.entityId = entityId;
|
||||
+ this.yHeadRot = headYaw;
|
||||
+ }
|
||||
+ // SparklyPaper end
|
||||
+
|
||||
private ClientboundRotateHeadPacket(FriendlyByteBuf buf) {
|
||||
this.entityId = buf.readVarInt();
|
||||
this.yHeadRot = buf.readByte();
|
||||
diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java
|
||||
index 1a37654aff9a9c86c9f7af10a1cf721371f0c5ec..e69ff4e6bb3919340a93ed4c68bdd6c4778669a9 100644
|
||||
--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java
|
||||
+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java
|
||||
@@ -17,9 +17,9 @@ public class ClientboundSectionBlocksUpdatePacket implements Packet<ClientGamePa
|
||||
|
||||
public static final StreamCodec<FriendlyByteBuf, ClientboundSectionBlocksUpdatePacket> STREAM_CODEC = Packet.codec(ClientboundSectionBlocksUpdatePacket::write, ClientboundSectionBlocksUpdatePacket::new);
|
||||
private static final int POS_IN_SECTION_BITS = 12;
|
||||
- private final SectionPos sectionPos;
|
||||
- private final short[] positions;
|
||||
- private final BlockState[] states;
|
||||
+ public SectionPos sectionPos; // SparklyPaper - Helpful NMS packet changes: remove final and make public
|
||||
+ public short[] positions; // SparklyPaper - Helpful NMS packet changes: remove final and make public
|
||||
+ public BlockState[] states; // SparklyPaper - Helpful NMS packet changes: remove final and make public
|
||||
|
||||
public ClientboundSectionBlocksUpdatePacket(SectionPos sectionPos, ShortSet positions, LevelChunkSection section) {
|
||||
this.sectionPos = sectionPos;
|
||||
diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundSetCameraPacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundSetCameraPacket.java
|
||||
index 799f5a8c69f295216997d52fb4bc6c56d3a18115..633f10f17eebd43e8dc7c878b9101decf31190a9 100644
|
||||
--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundSetCameraPacket.java
|
||||
+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundSetCameraPacket.java
|
||||
@@ -18,6 +18,12 @@ public class ClientboundSetCameraPacket implements Packet<ClientGamePacketListen
|
||||
this.cameraId = entity.getId();
|
||||
}
|
||||
|
||||
+ // SparklyPaper - Helpful NMS packet changes: add direct entityId constructor
|
||||
+ public ClientboundSetCameraPacket(int entityId) {
|
||||
+ this.cameraId = entityId;
|
||||
+ }
|
||||
+ // SparklyPaper end
|
||||
+
|
||||
private ClientboundSetCameraPacket(FriendlyByteBuf buf) {
|
||||
this.cameraId = buf.readVarInt();
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/Display.java b/src/main/java/net/minecraft/world/entity/Display.java
|
||||
index e6cbf4506c75046a89fad778e138b448fb4a29a9..3b705c75b840dbb0e741f46aaa3b98180a27ecf5 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/Display.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/Display.java
|
||||
@@ -802,7 +802,7 @@ public abstract class Display extends Entity {
|
||||
public static final byte FLAG_ALIGN_RIGHT = 16;
|
||||
private static final byte INITIAL_TEXT_OPACITY = -1;
|
||||
public static final int INITIAL_BACKGROUND = 1073741824;
|
||||
- private static final EntityDataAccessor<Component> DATA_TEXT_ID = SynchedEntityData.defineId(Display.TextDisplay.class, EntityDataSerializers.COMPONENT);
|
||||
+ public static final EntityDataAccessor<Component> DATA_TEXT_ID = SynchedEntityData.defineId(Display.TextDisplay.class, EntityDataSerializers.COMPONENT); // SparklyPaper - Helpful NMS packet changes: make public
|
||||
public static final EntityDataAccessor<Integer> DATA_LINE_WIDTH_ID = SynchedEntityData.defineId(Display.TextDisplay.class, EntityDataSerializers.INT);
|
||||
public static final EntityDataAccessor<Integer> DATA_BACKGROUND_COLOR_ID = SynchedEntityData.defineId(
|
||||
Display.TextDisplay.class, EntityDataSerializers.INT
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,38 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Kevin Raneri <kevin.raneri@gmail.com>
|
||||
Date: Tue, 9 Nov 2021 14:33:16 -0500
|
||||
Subject: [PATCH] Optimize entity coordinate key
|
||||
|
||||
When executing getCoordinateKey for entities (a hotpath), the JVM is
|
||||
required to repeatedly cast doubles to longs. The performance impact of
|
||||
this depends on the CPU architecture, but generally switching between
|
||||
FPU and ALU incurs a significant performance hit. The casted/rounded
|
||||
data is already available in the blockPosition struct, so we use that
|
||||
instead of re-doing the casting.
|
||||
|
||||
diff --git a/src/main/java/io/papermc/paper/util/MCUtil.java b/src/main/java/io/papermc/paper/util/MCUtil.java
|
||||
index 1d6b3fe2ce240af4ede61588795456b046eee6c9..cdcb1bff7913bfe86fed008271016a3175b6df90 100644
|
||||
--- a/src/main/java/io/papermc/paper/util/MCUtil.java
|
||||
+++ b/src/main/java/io/papermc/paper/util/MCUtil.java
|
||||
@@ -215,7 +215,7 @@ public final class MCUtil {
|
||||
}
|
||||
|
||||
public static long getCoordinateKey(final Entity entity) {
|
||||
- return ((long)(MCUtil.fastFloor(entity.getZ()) >> 4) << 32) | ((MCUtil.fastFloor(entity.getX()) >> 4) & 0xFFFFFFFFL);
|
||||
+ return ((long)(entity.blockPosition.getZ() >> 4) << 32) | ((entity.blockPosition.getX() >> 4) & 0xFFFFFFFFL); // Pufferfish - eliminate double->long cast in hotpath
|
||||
}
|
||||
|
||||
public static long getCoordinateKey(final ChunkPos pair) {
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
index 4ee843dfd826772c9157ca421d8fe1f36f814b51..41ce41e92f3722e7ffb3423c90663d7a677bf277 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
@@ -311,7 +311,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
public double yo;
|
||||
public double zo;
|
||||
private Vec3 position;
|
||||
- private BlockPos blockPosition;
|
||||
+ public BlockPos blockPosition; // Pufferfish - private->public
|
||||
private ChunkPos chunkPosition;
|
||||
private Vec3 deltaMovement;
|
||||
private float yRot;
|
||||
@@ -0,0 +1,55 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: MrPowerGamerBR <git@mrpowergamerbr.com>
|
||||
Date: Sat, 18 Nov 2023 21:04:53 -0300
|
||||
Subject: [PATCH] Avoid unnecessary ItemFrame#getItem() calls
|
||||
|
||||
When ticking a item frame, on each tick, it checks if the item on the item frame is a map and, if it is, it adds the map to be carried by the entity player
|
||||
|
||||
However, the "getItem()" call is a bit expensive, especially because this is only really used if the item in the item frame is a map
|
||||
|
||||
We can avoid this call by checking if the "cachedMapId" is not null
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java
|
||||
index aedf24ba0d64de855a59869052cbc2704e7dc134..f49585cfd485eed504e91887599ff25c3238c8fd 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerEntity.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerEntity.java
|
||||
@@ -115,13 +115,14 @@ public class ServerEntity {
|
||||
ItemFrame entityitemframe = (ItemFrame) entity;
|
||||
|
||||
if (true || this.tickCount % 10 == 0) { // CraftBukkit - Moved below, should always enter this block
|
||||
- ItemStack itemstack = entityitemframe.getItem();
|
||||
-
|
||||
- if (this.level.paperConfig().maps.itemFrameCursorUpdateInterval > 0 && this.tickCount % this.level.paperConfig().maps.itemFrameCursorUpdateInterval == 0 && itemstack.getItem() instanceof MapItem) { // CraftBukkit - Moved this.tickCounter % 10 logic here so item frames do not enter the other blocks // Paper - Make item frame map cursor update interval configurable
|
||||
+ if (this.level.paperConfig().maps.itemFrameCursorUpdateInterval > 0 && this.tickCount % this.level.paperConfig().maps.itemFrameCursorUpdateInterval == 0 && entityitemframe.cachedMapId != null) { // CraftBukkit - Moved this.tickCounter % 10 logic here so item frames do not enter the other blocks // Paper - Make item frame map cursor update interval configurable // SparklyPaper - avoid unnecessary ItemFrame#getItem() calls
|
||||
Integer integer = entityitemframe.cachedMapId; // Paper
|
||||
MapItemSavedData worldmap = MapItem.getSavedData(integer, this.level);
|
||||
|
||||
if (worldmap != null) {
|
||||
+ // SparklyPaper start - avoid unnecessary ItemFrame#getItem() calls
|
||||
+ ItemStack itemstack = entityitemframe.getItem();
|
||||
+ if (itemstack.getItem() instanceof MapItem) { // fail-safe, what if the cached map ID is present but the item isn't a MapItem? (this should NEVER happen but, who knows)
|
||||
Iterator<ServerPlayerConnection> iterator = this.trackedPlayers.iterator(); // CraftBukkit
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
@@ -134,6 +135,7 @@ public class ServerEntity {
|
||||
entityplayer.connection.send(packet);
|
||||
}
|
||||
}
|
||||
+ } // SparklyPaper end
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java b/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java
|
||||
index 759ecd79534a7706f7d4a63eb9dacbefcfe54674..07739c3d74074b2668466250f944dfbe22d4dc86 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java
|
||||
@@ -50,7 +50,8 @@ public class ItemFrame extends HangingEntity {
|
||||
public static final int NUM_ROTATIONS = 8;
|
||||
public float dropChance;
|
||||
public boolean fixed;
|
||||
- public Integer cachedMapId; // Paper
|
||||
+ @Nullable // SparklyPaper - avoid unnecessary ItemFrame#getItem() calls
|
||||
+ public Integer cachedMapId = null; // Paper // SparklyPaper - avoid unnecessary ItemFrame#getItem() calls
|
||||
|
||||
public ItemFrame(EntityType<? extends ItemFrame> type, Level world) {
|
||||
super(type, world);
|
||||
@@ -0,0 +1,44 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: MrPowerGamerBR <git@mrpowergamerbr.com>
|
||||
Date: Sun, 22 Oct 2023 12:27:26 -0300
|
||||
Subject: [PATCH] Only check thundering once per world instead for every chunk
|
||||
|
||||
For some reason the isThundering check is consuming ~3% of CPU time when profiled so, instead of checking the thunder every chunk, we can cache the result and reuse on the same chunk tick
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
index c02ffd419236980cd063741612e99d739d97ec94..daa5948cc7b86a719a313ea595f135cd00b6a3cc 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -599,6 +599,7 @@ public class ServerChunkCache extends ChunkSource {
|
||||
}
|
||||
// Paper end - optimise chunk tick iteration
|
||||
|
||||
+ this.level.isCurrentlyThundering = this.level.isThundering_old(); // SparklyPaper - Only check thundering once per world instead for every chunk
|
||||
int chunksTicked = 0; // Paper
|
||||
// Paper start - optimise chunk tick iteration
|
||||
io.papermc.paper.util.player.NearbyPlayers nearbyPlayers = this.chunkMap.getNearbyPlayers(); // Paper - optimise chunk tick iteration
|
||||
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
|
||||
index 9388dd9b34bb8148ce8b5f7e24122fa4bd1bafa8..0210226d2185803a18c0020d7985c1fccb798953 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/Level.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
||||
@@ -184,6 +184,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
||||
private int tileTickPosition;
|
||||
public final Map<Explosion.CacheKey, Float> explosionDensityCache = new HashMap<>(); // Paper - Optimize explosions
|
||||
public java.util.ArrayDeque<net.minecraft.world.level.block.RedstoneTorchBlock.Toggle> redstoneUpdateInfos; // Paper - Move from Map in BlockRedstoneTorch to here
|
||||
+ public boolean isCurrentlyThundering; // SparklyPaper - Only check if the world is currently thundering once instead for every chunk
|
||||
|
||||
// Paper start - fix and optimise world upgrading
|
||||
// copied from below
|
||||
@@ -1614,7 +1615,12 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
||||
this.rainLevel = f1;
|
||||
}
|
||||
|
||||
+ // SparklyPaper start - Only check thundering once per world instead for every chunk
|
||||
public boolean isThundering() {
|
||||
+ return isCurrentlyThundering;
|
||||
+ }
|
||||
+ public boolean isThundering_old() {
|
||||
+ // SparklyPaper end
|
||||
return this.dimensionType().hasSkyLight() && !this.dimensionType().hasCeiling() ? (double) this.getThunderLevel(1.0F) > 0.9D : false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: MrPowerGamerBR <git@mrpowergamerbr.com>
|
||||
Date: Sat, 28 Oct 2023 19:02:12 -0300
|
||||
Subject: [PATCH] Optimize heavy EntityLookup.ArrayIterable.<init>() calls on
|
||||
tickChunks
|
||||
|
||||
For some reason, on SparklyPower allocating an ArrayIterable is expensive, taking around ~2.5% of tick time (I have no idea why tho), because tickChunks() only uses this for the NaturalSpawner, let's avoid the array allocation by passing thru the raw array data + size
|
||||
|
||||
diff --git a/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java b/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java
|
||||
index 15ee41452992714108efe53b708b5a4e1da7c1ff..f5a5796dda9e0e05ed9afc069c241dedd9aaffa0 100644
|
||||
--- a/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java
|
||||
+++ b/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java
|
||||
@@ -198,6 +198,12 @@ public final class EntityLookup implements LevelEntityGetter<Entity> {
|
||||
return Arrays.copyOf(this.accessibleEntities.getRawData(), this.accessibleEntities.size(), Entity[].class);
|
||||
}
|
||||
|
||||
+ // SparklyPaper start - Optimize heavy EntityLookup$ArrayIterable.<init>() calls on tickChunks
|
||||
+ public EntityList getAccessibleEntities() {
|
||||
+ return this.accessibleEntities;
|
||||
+ }
|
||||
+ // SparklyPaper end
|
||||
+
|
||||
@Override
|
||||
public <U extends Entity> void get(final EntityTypeTest<Entity, U> filter, final AbortableIterationConsumer<U> action) {
|
||||
final Int2ReferenceOpenHashMap<Entity> entityCopy;
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
index f8270b78ab0d561e55301e989d80fe7b4118337a..e51d06140153e7f9a6e41b20addf02ec94e0f72c 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -545,9 +545,9 @@ public class ServerChunkCache extends ChunkSource {
|
||||
}
|
||||
// Paper end - per player mob spawning backoff
|
||||
}
|
||||
- spawnercreature_d = NaturalSpawner.createState(l, this.level.getAllEntities(), this::getFullChunk, null, true);
|
||||
+ spawnercreature_d = NaturalSpawner.createState(l, this.level.getEntityLookup().getAccessibleEntities().getRawData(), this.level.getEntityLookup().getAccessibleEntities().size(), this::getFullChunk, null, true); // SparklyPaper - Optimize heavy EntityLookup$ArrayIterable.<init>() calls on tickChunks
|
||||
} else {
|
||||
- spawnercreature_d = NaturalSpawner.createState(l, this.level.getAllEntities(), this::getFullChunk, !this.level.paperConfig().entities.spawning.perPlayerMobSpawns ? new LocalMobCapCalculator(this.chunkMap) : null, false);
|
||||
+ spawnercreature_d = NaturalSpawner.createState(l, this.level.getEntityLookup().getAccessibleEntities().getRawData(), this.level.getEntityLookup().getAccessibleEntities().size(), this::getFullChunk, !this.level.paperConfig().entities.spawning.perPlayerMobSpawns ? new LocalMobCapCalculator(this.chunkMap) : null, false); // SparklyPaper - Optimize heavy EntityLookup$ArrayIterable.<init>() calls on tickChunks
|
||||
}
|
||||
// Paper end
|
||||
this.level.timings.countNaturalMobs.stopTiming(); // Paper - timings
|
||||
diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
||||
index 9c2d62feff1816f5729060c6192269a5b2d34153..78bcb0af0735fe0ccf68ed06d8dc78d6e8c37064 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
||||
@@ -74,7 +74,60 @@ public final class NaturalSpawner {
|
||||
return createState(spawningChunkCount, entities, chunkSource, densityCapper, false);
|
||||
}
|
||||
|
||||
+ // SparklyPaper start - Optimize heavy EntityLookup$ArrayIterable.<init>() calls on tickChunks
|
||||
+ public static NaturalSpawner.SpawnState createState(int spawningChunkCount, Entity[] entities, int count, NaturalSpawner.ChunkGetter chunkSource, LocalMobCapCalculator densityCapper, boolean countMobs) {
|
||||
+ PotentialCalculator spawnercreatureprobabilities = new PotentialCalculator();
|
||||
+ Object2IntOpenHashMap<MobCategory> object2intopenhashmap = new Object2IntOpenHashMap();
|
||||
+
|
||||
+ for (int index = 0; count > index; index++) {
|
||||
+ Entity entity = entities[index];
|
||||
+
|
||||
+ if (entity instanceof Mob) {
|
||||
+ Mob entityinsentient = (Mob) entity;
|
||||
+
|
||||
+ if (entityinsentient.isPersistenceRequired() || entityinsentient.requiresCustomPersistence()) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ MobCategory enumcreaturetype = entity.getType().getCategory();
|
||||
+
|
||||
+ if (enumcreaturetype != MobCategory.MISC) {
|
||||
+ // Paper start - Only count natural spawns
|
||||
+ if (!entity.level().paperConfig().entities.spawning.countAllMobsForSpawning &&
|
||||
+ !(entity.spawnReason == org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL ||
|
||||
+ entity.spawnReason == org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.CHUNK_GEN)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ // Paper end
|
||||
+ BlockPos blockposition = entity.blockPosition();
|
||||
+
|
||||
+ chunkSource.query(ChunkPos.asLong(blockposition), (chunk) -> {
|
||||
+ MobSpawnSettings.MobSpawnCost biomesettingsmobs_b = NaturalSpawner.getRoughBiome(blockposition, chunk).getMobSettings().getMobSpawnCost(entity.getType());
|
||||
+
|
||||
+ if (biomesettingsmobs_b != null) {
|
||||
+ spawnercreatureprobabilities.addCharge(entity.blockPosition(), biomesettingsmobs_b.charge());
|
||||
+ }
|
||||
+
|
||||
+ if (densityCapper != null && entity instanceof Mob) { // Paper
|
||||
+ densityCapper.addMob(chunk.getPos(), enumcreaturetype);
|
||||
+ }
|
||||
+
|
||||
+ object2intopenhashmap.addTo(enumcreaturetype, 1);
|
||||
+ // Paper start
|
||||
+ if (countMobs) {
|
||||
+ chunk.level.getChunkSource().chunkMap.updatePlayerMobTypeMap(entity);
|
||||
+ }
|
||||
+ // Paper end
|
||||
+ });
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return new NaturalSpawner.SpawnState(spawningChunkCount, object2intopenhashmap, spawnercreatureprobabilities, densityCapper);
|
||||
+ }
|
||||
+
|
||||
public static NaturalSpawner.SpawnState createState(int spawningChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkSource, LocalMobCapCalculator densityCapper, boolean countMobs) {
|
||||
+ // SparklyPaper end
|
||||
// Paper end
|
||||
PotentialCalculator spawnercreatureprobabilities = new PotentialCalculator();
|
||||
Object2IntOpenHashMap<MobCategory> object2intopenhashmap = new Object2IntOpenHashMap();
|
||||
@@ -0,0 +1,93 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: MrPowerGamerBR <git@mrpowergamerbr.com>
|
||||
Date: Fri, 24 Nov 2023 23:37:24 -0300
|
||||
Subject: [PATCH] BlockEntityTickersList optimization tests
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
|
||||
index 94eac6837c06e6fd192c108632f1e365a008d6ad..3588657da9969b4207bbeb8109bc101384c03398 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/Level.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
||||
@@ -1274,8 +1274,8 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
||||
// Iterator iterator = this.blockEntityTickers.iterator();
|
||||
int tilesThisCycle = 0;
|
||||
// SparklyPaper start - optimize tickBlockEntities
|
||||
- // var toRemove = new it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet<TickingBlockEntity>(net.minecraft.Util.identityStrategy()); // Paper - use removeAll
|
||||
- // toRemove.add(null);
|
||||
+ var toRemoveOld = new it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet<TickingBlockEntity>(net.minecraft.Util.identityStrategy()); // Paper - use removeAll
|
||||
+ toRemoveOld.add(null);
|
||||
var toRemove = new java.util.HashSet<Integer>(); // For some reason, Java's HashSet seems to be faster than fastutil's only if we are removing HUGE amounts of tile entities, idk why
|
||||
var startSearchFromIndex = -1;
|
||||
var shouldTickBlocksAtLastResult = -1; // -1 = undefined
|
||||
@@ -1301,13 +1301,13 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
||||
// Spigot start
|
||||
tilesThisCycle--;
|
||||
// SparklyPaper start - optimize tickBlockEntities
|
||||
- // toRemove.add(tickingblockentity); // Paper - use removeAll
|
||||
+ toRemoveOld.add(tickingblockentity); // Paper - use removeAll
|
||||
toRemove.add(tileTickPosition);
|
||||
if (startSearchFromIndex == -1)
|
||||
startSearchFromIndex = tileTickPosition;
|
||||
// SparklyPaper end
|
||||
// Spigot end
|
||||
- // } else if (this.shouldTickBlocksAt(tickingblockentity.getPos())) { // SparklyPaper start - optimize tickBlockEntities
|
||||
+ // } else if (this.shouldTickBlocksAt(tickingblockentity.getPos())) { // SparklyPaper start - optimize tickBlockEntities
|
||||
} else {
|
||||
long chunkPos = tickingblockentity.getChunkCoordinateKey();
|
||||
boolean shouldTick;
|
||||
@@ -1319,18 +1319,47 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
||||
shouldTickBlocksAtChunkPos = chunkPos;
|
||||
}
|
||||
if (shouldTick) {
|
||||
- tickingblockentity.tick();
|
||||
- // Paper start - execute chunk tasks during tick
|
||||
- if ((this.tileTickPosition & 7) == 0) {
|
||||
- // MinecraftServer.getServer().executeMidTickTasks(); // SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks)
|
||||
- }
|
||||
- // Paper end - execute chunk tasks during tick
|
||||
+ tickingblockentity.tick();
|
||||
+ // Paper start - execute chunk tasks during tick
|
||||
+ if ((this.tileTickPosition & 7) == 0) {
|
||||
+ // MinecraftServer.getServer().executeMidTickTasks(); // SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks)
|
||||
+ }
|
||||
+ // Paper end - execute chunk tasks during tick
|
||||
} // SparklyPaper end
|
||||
}
|
||||
}
|
||||
// SparklyPaper start - optimize tickBlockEntities
|
||||
- // this.blockEntityTickers.removeAll(toRemove);
|
||||
- this.blockEntityTickers.removeAllByIndex(startSearchFromIndex, toRemove); // We don't need to care about if the startSearchFromIndex can be -1 here, since if it is -1, then the toRemove list is empty and the call will fast fail
|
||||
+ java.util.ArrayList<TickingBlockEntity> oldList = new java.util.ArrayList<>(this.blockEntityTickers);
|
||||
+ long diffOld = 0;
|
||||
+ long diffNew = 0;
|
||||
+ if (toRemoveOld.size() != 1) { // the old one always have null as the first element
|
||||
+ var startOld = System.nanoTime();
|
||||
+ oldList.removeAll(toRemoveOld);
|
||||
+ var endOld = System.nanoTime();
|
||||
+ diffOld = endOld - startOld;
|
||||
+ System.out.println("Old version deleted " + toRemoveOld.size() + " elements, took " + diffOld + "ns");
|
||||
+ }
|
||||
+ if (startSearchFromIndex != -1) {
|
||||
+ var start = System.nanoTime();
|
||||
+ this.blockEntityTickers.removeAllByIndex(startSearchFromIndex, toRemove); // We don't need to care about if the startSearchFromIndex can be -1 here, since if it is -1, then the toRemove list is empty and the call will fast fail
|
||||
+ var end = System.nanoTime();
|
||||
+ System.out.println("(current tick: " + this.getServer().getTickCount() + ") startSearchFromIndex: " + startSearchFromIndex + " - toRemove: " + toRemove);
|
||||
+ diffNew = end - start;
|
||||
+ System.out.println("New version deleted " + toRemove.size() + " elements, took " + diffNew + "ns");
|
||||
+ }
|
||||
+ if (toRemove.size() != 0) {
|
||||
+ System.out.println("Equals? " + oldList.equals(this.blockEntityTickers));
|
||||
+ String winner = "Unknown";
|
||||
+ long perfIncrease = 0;
|
||||
+ if (diffOld > diffNew) {
|
||||
+ winner = "New Version";
|
||||
+ perfIncrease = diffOld - diffNew;
|
||||
+ } else {
|
||||
+ winner = "Old Version";
|
||||
+ perfIncrease = diffNew - diffOld;
|
||||
+ }
|
||||
+ System.out.println("Who won? " + winner + ", by " + perfIncrease + "ns");
|
||||
+ }
|
||||
// SparklyPaper end
|
||||
this.timings.tileEntityTick.stopTiming(); // Spigot
|
||||
this.tickingBlockEntities = false;
|
||||
Reference in New Issue
Block a user