Current non compilable status of all patches - THIS IS NOT READY

THERE IS STILL NO ETA. GOBLINS WILL EAT YOU.
This commit is contained in:
Aikar 2020-06-25 21:58:00 -04:00
parent 939690baea
commit 014b7f115d
No known key found for this signature in database
GPG key ID: 401ADFC9891FAAFE
171 changed files with 814 additions and 1122 deletions

View file

@ -0,0 +1,101 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: JellySquid <jellysquid+atwork@protonmail.com>
Date: Sat, 9 May 2020 16:25:21 -0400
Subject: [PATCH] Implement JellySquid's Entity Collision optimisations patch
Optimizes Full Block voxel collisions, and removes streams from Entity collisions
Original code by JellySquid, licensed under GNU Lesser General Public License v3.0
you can find the original code on https://github.com/jellysquid3/lithium-fabric/tree/1.15.x/fabric (Yarn mappings)
diff --git a/src/main/java/net/minecraft/server/ICollisionAccess.java b/src/main/java/net/minecraft/server/ICollisionAccess.java
index 3eefbf4d5f10b53f930759a0afa5661253b92c60..5e20dba0d011d20b714d784cb4a545a05bbf6f9c 100644
--- a/src/main/java/net/minecraft/server/ICollisionAccess.java
+++ b/src/main/java/net/minecraft/server/ICollisionAccess.java
@@ -115,11 +115,24 @@ public interface ICollisionAccess extends IBlockAccess {
if ((j2 != 1 || iblockdata.f()) && (j2 != 2 || iblockdata.getBlock() == Blocks.MOVING_PISTON)) {
VoxelShape voxelshape2 = iblockdata.b((IBlockAccess) ICollisionAccess.this, blockposition_mutableblockposition, voxelshapecollision);
- VoxelShape voxelshape3 = voxelshape2.a((double) k1, (double) l1, (double) i2);
- if (VoxelShapes.c(voxelshape, voxelshape3, OperatorBoolean.AND)) {
- consumer.accept(voxelshape3);
- return true;
+ // Paper start - Lithium Collision Optimizations
+ if (voxelshape2 == VoxelShapes.empty()) {
+ continue;
+ }
+
+ if (voxelshape2 == VoxelShapes.fullCube()) {
+ if (axisalignedbb.intersects(x, y, z, x + 1.0D, y + 1.0D, z + 1.0D)) {
+ consumer.accept(voxelshape2.offset(x, y, z));
+ return true;
+ }
+ } else {
+ VoxelShape shape = voxelshape2.offset(x, y, z);
+ if (VoxelShapes.applyOperation(shape, voxelshape, OperatorBoolean.AND)) {
+ consumer.accept(shape);
+ return true;
+ }
+ // Paper end
}
}
}
diff --git a/src/main/java/net/minecraft/server/IEntityAccess.java b/src/main/java/net/minecraft/server/IEntityAccess.java
index 5135308fb6137a34ed6fd061f0a210de6de4e81c..d434aaaaf0ab6a18ab0fe5ad0bf8ed4662f49120 100644
--- a/src/main/java/net/minecraft/server/IEntityAccess.java
+++ b/src/main/java/net/minecraft/server/IEntityAccess.java
@@ -52,20 +52,41 @@ public interface IEntityAccess {
// Paper end - optimise hard collision
default Stream<VoxelShape> b(@Nullable Entity entity, AxisAlignedBB axisalignedbb, Set<Entity> set) {
- if (axisalignedbb.a() < 1.0E-7D) {
+ // Paper start - remove streams from entity collision
+ if (axisalignedbb.getAverageSideLength() < 1.0E-7D) {
return Stream.empty();
- } else {
- AxisAlignedBB axisalignedbb1 = axisalignedbb.g(1.0E-7D);
- Stream<AxisAlignedBB> stream = ((entity != null && entity.hardCollides()) ? this.getEntities(entity, axisalignedbb) : this.getHardCollidingEntities(entity, axisalignedbb1)).stream().filter((entity1) -> { // Paper - decompile fix // Paper - optimise hard collision
- return !set.contains(entity1);
- }).filter((entity1) -> {
- return entity == null || !entity.isSameVehicle(entity1);
- }).flatMap((entity1) -> {
- return Stream.of(entity1.au(), entity == null ? null : entity.j(entity1)); // Paper - optimise hard collision - diff on change, these are the methods that only hard colliding entities override
- }).filter(Objects::nonNull);
-
- return stream.filter(axisalignedbb1::c).map(VoxelShapes::a);
+
}
+ AxisAlignedBB selection = axisalignedbb.grow(1.0E-7D);
+ List<Entity> entities = entity != null && entity.hardCollides() ? getEntities(entity, selection) : getHardCollidingEntities(entity, selection);
+ List<VoxelShape> shapes = new java.util.ArrayList<>();
+
+ for (Entity otherEntity : entities) {
+ if (!set.isEmpty() && set.contains(otherEntity)) {
+ continue;
+ }
+
+ if (entity != null && entity.isSameVehicle(otherEntity)) {
+ continue;
+ }
+
+ AxisAlignedBB otherEntityBox = otherEntity.getCollisionBox();
+
+ if (otherEntityBox != null && selection.intersects(otherEntityBox)) {
+ shapes.add(VoxelShapes.of(otherEntityBox));
+ }
+
+ if (entity != null) {
+ AxisAlignedBB otherEntityHardBox = entity.getHardCollisionBox(otherEntity);
+
+ if (otherEntityHardBox != null && selection.intersects(otherEntityHardBox)) {
+ shapes.add(VoxelShapes.of(otherEntityHardBox));
+ }
+ }
+ }
+
+ return shapes.stream();
+ // Paper end
}
@Nullable

View file

@ -0,0 +1,178 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sat, 9 May 2020 18:36:27 -0400
Subject: [PATCH] Remove some Streams usage in Entity Collision
While there is more down the collision system, remove some of the wrapping
Spliterator stuff as even this wrapper stream has shown up in profiling.
With other collision optimizations, we might also even avoid inner streams too.
diff --git a/src/main/java/net/minecraft/server/GeneratorAccess.java b/src/main/java/net/minecraft/server/GeneratorAccess.java
index e865a5694f78fb9273a0625ab2c30b87d0711a90..5648ba73c533f622c35c808decdb305f8a1cf6b0 100644
--- a/src/main/java/net/minecraft/server/GeneratorAccess.java
+++ b/src/main/java/net/minecraft/server/GeneratorAccess.java
@@ -52,6 +52,7 @@ public interface GeneratorAccess extends IEntityAccess, IWorldReader, VirtualLev
this.a((EntityHuman) null, i, blockposition, j);
}
+ @Override default java.util.List<VoxelShape> getEntityCollisions(@Nullable Entity entity, AxisAlignedBB axisalignedbb, Set<Entity> set, boolean returnFast) {return IEntityAccess.super.getEntityCollisions(entity, axisalignedbb, set, returnFast); } // Paper
@Override
default Stream<VoxelShape> b(@Nullable Entity entity, AxisAlignedBB axisalignedbb, Set<Entity> set) {
return IEntityAccess.super.b(entity, axisalignedbb, set);
diff --git a/src/main/java/net/minecraft/server/ICollisionAccess.java b/src/main/java/net/minecraft/server/ICollisionAccess.java
index 5e20dba0d011d20b714d784cb4a545a05bbf6f9c..5a21205a49606b294de4cd27b60438c6a5b3c526 100644
--- a/src/main/java/net/minecraft/server/ICollisionAccess.java
+++ b/src/main/java/net/minecraft/server/ICollisionAccess.java
@@ -44,19 +44,40 @@ public interface ICollisionAccess extends IBlockAccess {
default boolean a(@Nullable Entity entity, AxisAlignedBB axisalignedbb, Set<Entity> set) {
try { if (entity != null) entity.collisionLoadChunks = true; // Paper
- return this.c(entity, axisalignedbb, set).allMatch(VoxelShape::isEmpty);
+ // Paper start - reduce stream usage
+ java.util.List<VoxelShape> blockCollisions = getBlockCollision(entity, axisalignedbb, true);
+ for (int i = 0; i < blockCollisions.size(); i++) {
+ VoxelShape blockCollision = blockCollisions.get(i);
+ if (!blockCollision.isEmpty()) {
+ return false;
+ }
+ }
+ return getEntityCollisions(entity, axisalignedbb, set, true).isEmpty();
+ // Paper end
} finally { if (entity != null) entity.collisionLoadChunks = false; } // Paper
}
+ default java.util.List<VoxelShape> getEntityCollisions(@Nullable Entity entity, AxisAlignedBB axisalignedbb, Set<Entity> set, boolean returnFast) { return java.util.Collections.emptyList(); } // Paper
default Stream<VoxelShape> b(@Nullable Entity entity, AxisAlignedBB axisalignedbb, Set<Entity> set) {
return Stream.empty();
}
default Stream<VoxelShape> c(@Nullable Entity entity, AxisAlignedBB axisalignedbb, Set<Entity> set) {
- return Streams.concat(new Stream[]{this.b(entity, axisalignedbb), this.b(entity, axisalignedbb, set)});
+ // Paper start - reduce stream usage
+ java.util.List<VoxelShape> blockCollisions = getBlockCollision(entity, axisalignedbb, false);
+ java.util.List<VoxelShape> entityCollisions = getEntityCollisions(entity, axisalignedbb, set, false);
+ return Stream.concat(blockCollisions.stream(), entityCollisions.stream());
+ // Paper end
}
default Stream<VoxelShape> b(@Nullable final Entity entity, AxisAlignedBB axisalignedbb) {
+ // Paper start - reduce stream usage
+ java.util.List<VoxelShape> collision = getBlockCollision(entity, axisalignedbb, false);
+ return !collision.isEmpty() ? collision.stream() : Stream.empty();
+ }
+
+ default java.util.List<VoxelShape> getBlockCollision(@Nullable final Entity entity, AxisAlignedBB axisalignedbb, boolean returnFast) {
+ // Paper end
int i = MathHelper.floor(axisalignedbb.minX - 1.0E-7D) - 1;
int j = MathHelper.floor(axisalignedbb.maxX + 1.0E-7D) + 1;
int k = MathHelper.floor(axisalignedbb.minY - 1.0E-7D) - 1;
@@ -68,19 +89,19 @@ public interface ICollisionAccess extends IBlockAccess {
final BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition();
final VoxelShape voxelshape = VoxelShapes.a(axisalignedbb);
- return StreamSupport.stream(new AbstractSpliterator<VoxelShape>(Long.MAX_VALUE, 1280) {
- boolean a = entity == null;
-
- public boolean tryAdvance(Consumer<? super VoxelShape> consumer) {
- if (!this.a) {
- this.a = true;
+ // Paper start - reduce stream usage (this part done by Aikar)
+ java.util.List<VoxelShape> collisions = new java.util.ArrayList<>();
+ if (true) {//return StreamSupport.stream(new AbstractSpliterator<VoxelShape>(Long.MAX_VALUE, 1280) {
+ if (true) { //public boolean tryAdvance(Consumer<? super VoxelShape> consumer) {*/ // Paper
+ if (entity != null) {
+ // Paper end
VoxelShape voxelshape1 = ICollisionAccess.this.getWorldBorder().a();
boolean flag = VoxelShapes.c(voxelshape1, VoxelShapes.a(entity.getBoundingBox().shrink(1.0E-7D)), OperatorBoolean.AND);
boolean flag1 = VoxelShapes.c(voxelshape1, VoxelShapes.a(entity.getBoundingBox().g(1.0E-7D)), OperatorBoolean.AND);
if (!flag && flag1) {
- consumer.accept(voxelshape1);
- return true;
+ collisions.add(voxelshape1);// Paper
+ if (returnFast) return collisions;
}
}
@@ -104,9 +125,8 @@ public interface ICollisionAccess extends IBlockAccess {
);
if (iblockdata == null) {
if (!(entity instanceof EntityPlayer) || entity.world.paperConfig.preventMovingIntoUnloadedChunks) {
- VoxelShape voxelshape3 = VoxelShapes.of(far ? entity.getBoundingBox() : new AxisAlignedBB(new BlockPosition(x, y, z)));
- consumer.accept(voxelshape3);
- return true;
+ collisions.add(VoxelShapes.of(far ? entity.getBoundingBox() : new AxisAlignedBB(new BlockPosition(x, y, z))));
+ if (returnFast) return collisions;
}
} else {
//blockposition_mutableblockposition.d(k1, l1, i2); // moved up
@@ -123,14 +143,14 @@ public interface ICollisionAccess extends IBlockAccess {
if (voxelshape2 == VoxelShapes.fullCube()) {
if (axisalignedbb.intersects(x, y, z, x + 1.0D, y + 1.0D, z + 1.0D)) {
- consumer.accept(voxelshape2.offset(x, y, z));
- return true;
+ collisions.add(voxelshape2.offset(x, y, z));
+ if (returnFast) return collisions;
}
} else {
VoxelShape shape = voxelshape2.offset(x, y, z);
if (VoxelShapes.applyOperation(shape, voxelshape, OperatorBoolean.AND)) {
- consumer.accept(shape);
- return true;
+ collisions.add(shape);
+ if (returnFast) return collisions;
}
// Paper end
}
@@ -139,8 +159,9 @@ public interface ICollisionAccess extends IBlockAccess {
}
}
- return false;
+ //return false; // Paper
}
- }, false);
+ } //}, false);
+ return collisions; // Paper
}
}
diff --git a/src/main/java/net/minecraft/server/IEntityAccess.java b/src/main/java/net/minecraft/server/IEntityAccess.java
index d434aaaaf0ab6a18ab0fe5ad0bf8ed4662f49120..3bc57ef91d557383178533b0cc87a71a521d6b3e 100644
--- a/src/main/java/net/minecraft/server/IEntityAccess.java
+++ b/src/main/java/net/minecraft/server/IEntityAccess.java
@@ -55,8 +55,10 @@ public interface IEntityAccess {
// Paper start - remove streams from entity collision
if (axisalignedbb.getAverageSideLength() < 1.0E-7D) {
return Stream.empty();
-
}
+ return getEntityCollisions(entity, axisalignedbb, set, false).stream();
+ }
+ default List<VoxelShape> getEntityCollisions(@Nullable Entity entity, AxisAlignedBB axisalignedbb, Set<Entity> set, boolean returnFast) {
AxisAlignedBB selection = axisalignedbb.grow(1.0E-7D);
List<Entity> entities = entity != null && entity.hardCollides() ? getEntities(entity, selection) : getHardCollidingEntities(entity, selection);
List<VoxelShape> shapes = new java.util.ArrayList<>();
@@ -74,6 +76,7 @@ public interface IEntityAccess {
if (otherEntityBox != null && selection.intersects(otherEntityBox)) {
shapes.add(VoxelShapes.of(otherEntityBox));
+ if (returnFast) return shapes;
}
if (entity != null) {
@@ -81,11 +84,12 @@ public interface IEntityAccess {
if (otherEntityHardBox != null && selection.intersects(otherEntityHardBox)) {
shapes.add(VoxelShapes.of(otherEntityHardBox));
+ if (returnFast) return shapes;
}
}
}
- return shapes.stream();
+ return shapes;
// Paper end
}

View file

@ -0,0 +1,55 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sun, 10 May 2020 22:12:46 -0400
Subject: [PATCH] Ensure Entity AABB's are never invalid
If anything used setPositionRaw, it left potential for an AABB
to be left stale at their old location, which could cause massive
AABB boxes if movement ever then got called on the new position.
This guarantees any time we set the entities position, we also
update their AABB.
diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java
index b176dc26d15065aebc91c75e8a96745f589c0b87..c81b9d814d50a026872d2711f76649c00d65888b 100644
--- a/src/main/java/net/minecraft/server/Entity.java
+++ b/src/main/java/net/minecraft/server/Entity.java
@@ -416,10 +416,11 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
public void setPosition(double d0, double d1, double d2) {
this.setPositionRaw(d0, d1, d2);
- float f = this.size.width / 2.0F;
- float f1 = this.size.height;
-
- this.a(new AxisAlignedBB(d0 - (double) f, d1, d2 - (double) f, d0 + (double) f, d1 + (double) f1, d2 + (double) f));
+ // Paper start - move into setPositionRaw
+ //float f = this.size.width / 2.0F;
+ //float f1 = this.size.height;
+ //this.a(new AxisAlignedBB(d0 - (double) f, d1, d2 - (double) f, d0 + (double) f, d1 + (double) f1, d2 + (double) f));
+ // Paper end
if (valid) ((WorldServer) world).chunkCheck(this); // CraftBukkit
}
@@ -2981,6 +2982,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
return new AxisAlignedBB(vec3d, vec3d1);
}
+ public final void setBoundingBox(AxisAlignedBB axisalignedbb) { a(axisalignedbb); } // Paper - OBFHELPER
public void a(AxisAlignedBB axisalignedbb) {
// CraftBukkit start - block invalid bounding boxes
double minX = axisalignedbb.minX,
@@ -3439,6 +3441,14 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
}
public void setPositionRaw(double d0, double d1, double d2) {
+ // Paper start - never allow AABB to become desynced from position
+ // hanging has its own special logic
+ if (!(this instanceof EntityHanging) && (locX != d0 || locY != d1 || locZ != d2)) {
+ float f = this.size.width / 2.0F;
+ float f1 = this.size.height;
+ this.setBoundingBox(new AxisAlignedBB(d0 - (double) f, d1, d2 - (double) f, d0 + (double) f, d1 + (double) f1, d2 + (double) f));
+ }
+ // Paper end
this.locX = d0;
this.locY = d1;
this.locZ = d2;

View file

@ -0,0 +1,61 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Sun, 10 May 2020 22:49:05 -0400
Subject: [PATCH] Optimize WorldBorder collision checks and air
diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java
index c81b9d814d50a026872d2711f76649c00d65888b..e0ab058bf947ea10b37eadf6122292e708bd3809 100644
--- a/src/main/java/net/minecraft/server/Entity.java
+++ b/src/main/java/net/minecraft/server/Entity.java
@@ -845,7 +845,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
AxisAlignedBB axisalignedbb = this.getBoundingBox();
VoxelShapeCollision voxelshapecollision = VoxelShapeCollision.a(this);
VoxelShape voxelshape = this.world.getWorldBorder().a();
- Stream<VoxelShape> stream = VoxelShapes.c(voxelshape, VoxelShapes.a(axisalignedbb.shrink(1.0E-7D)), OperatorBoolean.AND) ? Stream.empty() : Stream.of(voxelshape);
+ Stream<VoxelShape> stream = !this.world.getWorldBorder().isInBounds(axisalignedbb) ? Stream.empty() : Stream.of(this.world.getWorldBorder().a()); // Paper
Stream<VoxelShape> stream1 = this.world.b(this, axisalignedbb.a(vec3d), (Set) ImmutableSet.of());
StreamAccumulator<VoxelShape> streamaccumulator = new StreamAccumulator<>(Stream.concat(stream1, stream));
Vec3D vec3d1 = vec3d.g() == 0.0D ? vec3d : a(this, vec3d, axisalignedbb, this.world, voxelshapecollision, streamaccumulator);
diff --git a/src/main/java/net/minecraft/server/ICollisionAccess.java b/src/main/java/net/minecraft/server/ICollisionAccess.java
index 5a21205a49606b294de4cd27b60438c6a5b3c526..63dd5e98b6af1d9a9fa9d01621ce5bc33c0d7502 100644
--- a/src/main/java/net/minecraft/server/ICollisionAccess.java
+++ b/src/main/java/net/minecraft/server/ICollisionAccess.java
@@ -95,12 +95,12 @@ public interface ICollisionAccess extends IBlockAccess {
if (true) { //public boolean tryAdvance(Consumer<? super VoxelShape> consumer) {*/ // Paper
if (entity != null) {
// Paper end
- VoxelShape voxelshape1 = ICollisionAccess.this.getWorldBorder().a();
- boolean flag = VoxelShapes.c(voxelshape1, VoxelShapes.a(entity.getBoundingBox().shrink(1.0E-7D)), OperatorBoolean.AND);
- boolean flag1 = VoxelShapes.c(voxelshape1, VoxelShapes.a(entity.getBoundingBox().g(1.0E-7D)), OperatorBoolean.AND);
+ //VoxelShape voxelshape1 = ICollisionAccess.this.getWorldBorder().a(); // Paper - only make if collides
+ boolean flag = !ICollisionAccess.this.getWorldBorder().isInBounds(entity.getBoundingBox().shrink(1.0E-7D)); // Paper
+ boolean flag1 = !ICollisionAccess.this.getWorldBorder().isInBounds(entity.getBoundingBox().g(1.0E-7D)); // Paper
if (!flag && flag1) {
- collisions.add(voxelshape1);// Paper
+ collisions.add(ICollisionAccess.this.getWorldBorder().a());// Paper
if (returnFast) return collisions;
}
}
@@ -133,7 +133,7 @@ public interface ICollisionAccess extends IBlockAccess {
//IBlockData iblockdata = iblockaccess.getType(blockposition_mutableblockposition); // moved up
// Paper end
- if ((j2 != 1 || iblockdata.f()) && (j2 != 2 || iblockdata.getBlock() == Blocks.MOVING_PISTON)) {
+ if (!iblockdata.isAir() && (j2 != 1 || iblockdata.f()) && (j2 != 2 || iblockdata.getBlock() == Blocks.MOVING_PISTON)) { // Paper - fast track air
VoxelShape voxelshape2 = iblockdata.b((IBlockAccess) ICollisionAccess.this, blockposition_mutableblockposition, voxelshapecollision);
// Paper start - Lithium Collision Optimizations
diff --git a/src/main/java/net/minecraft/server/WorldBorder.java b/src/main/java/net/minecraft/server/WorldBorder.java
index 4c20db5a3f9e159997a9851691aca421241d6d95..535d08ffb1d20570b19307c8619cfd39b4541356 100644
--- a/src/main/java/net/minecraft/server/WorldBorder.java
+++ b/src/main/java/net/minecraft/server/WorldBorder.java
@@ -40,6 +40,7 @@ public class WorldBorder {
return (double) chunkcoordintpair.f() > this.c() && (double) chunkcoordintpair.d() < this.e() && (double) chunkcoordintpair.g() > this.d() && (double) chunkcoordintpair.e() < this.f();
}
+ public final boolean isInBounds(AxisAlignedBB aabb) { return this.a(aabb); } // Paper - OBFHELPER
public boolean a(AxisAlignedBB axisalignedbb) {
return axisalignedbb.maxX > this.c() && axisalignedbb.minX < this.e() && axisalignedbb.maxZ > this.d() && axisalignedbb.minZ < this.f();
}

View file

@ -0,0 +1,162 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Mon, 11 May 2020 04:18:54 -0400
Subject: [PATCH] Optimize Pathfinder - Remove Streams / Optimized collections
I utilized the IDE to convert streams to non streams code, so shouldn't
be any risk of behavior change. Only did minor optimization of the
generated code set to remove unnecessary things.
I expect us to just drop this patch on next major update and re-apply
it with the IDE again and re-apply the collections optimization.
Optimize collection by creating a list instead of a set of the key and value.
This lets us get faster foreach iteration, as well as avoids map lookups on
the values when needed.
diff --git a/src/main/java/net/minecraft/server/Pathfinder.java b/src/main/java/net/minecraft/server/Pathfinder.java
index 67c63cfe333e328cbd00ada970bd81efebfe30b6..71b96943d865105f279dda613b7ac6b3ffed793b 100644
--- a/src/main/java/net/minecraft/server/Pathfinder.java
+++ b/src/main/java/net/minecraft/server/Pathfinder.java
@@ -31,9 +31,15 @@ public class Pathfinder {
this.a.a();
this.e.a(chunkcache, entityinsentient);
PathPoint pathpoint = this.e.b();
- Map<PathDestination, BlockPosition> map = (Map) set.stream().collect(Collectors.toMap((blockposition) -> {
- return this.e.a((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ());
- }, Function.identity()));
+ // Paper start - remove streams - and optimize collection
+ List<Map.Entry<PathDestination, BlockPosition>> map = new java.util.ArrayList<>();
+ for (BlockPosition blockposition : set) {
+ // cast is important
+ //noinspection RedundantCast
+ PathDestination path = this.e.a((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ());
+ map.add(new java.util.AbstractMap.SimpleEntry<>(path, blockposition));
+ }
+ // Paper end
PathEntity pathentity = this.a(pathpoint, map, f, i, f1);
this.e.a();
@@ -41,11 +47,11 @@ public class Pathfinder {
}
@Nullable
- private PathEntity a(PathPoint pathpoint, Map<PathDestination, BlockPosition> map, float f, int i, float f1) {
- Set<PathDestination> set = map.keySet();
+ private PathEntity a(PathPoint pathpoint, java.util.List<java.util.Map.Entry<PathDestination, BlockPosition>> list, float f, int i, float f1) { // Paper - list instead of set
+ //Set<PathDestination> set = map.keySet(); // Paper
pathpoint.e = 0.0F;
- pathpoint.f = this.a(pathpoint, set);
+ pathpoint.f = this.a(pathpoint, list); // Paper - list instead of map
pathpoint.g = pathpoint.f;
this.a.a();
this.b.clear();
@@ -62,10 +68,23 @@ public class Pathfinder {
PathPoint pathpoint1 = this.a.c();
pathpoint1.i = true;
- set.stream().filter((pathdestination) -> {
- return pathpoint1.c((PathPoint) pathdestination) <= (float) i;
- }).forEach(PathDestination::e);
- if (set.stream().anyMatch(PathDestination::f)) {
+ // Paper start - remove streams
+ for (int i1 = 0, listSize = list.size(); i1 < listSize; i1++) {
+ PathDestination pathdestination = list.get(i1).getKey();
+ if (pathpoint1.c(pathdestination) <= (float) i) {
+ pathdestination.e();
+ }
+ }
+ boolean result = false;
+ for (int i1 = 0, listSize = list.size(); i1 < listSize; i1++) {
+ PathDestination pathdestination = list.get(i1).getKey();
+ if (pathdestination.f()) {
+ result = true;
+ break;
+ }
+ }
+ if (result) {
+ // Paper end
break;
}
@@ -82,7 +101,7 @@ public class Pathfinder {
if (pathpoint2.j < f && (!pathpoint2.c() || f3 < pathpoint2.e)) {
pathpoint2.h = pathpoint1;
pathpoint2.e = f3;
- pathpoint2.f = this.a(pathpoint2, set) * 1.5F;
+ pathpoint2.f = this.a(pathpoint2, list) * 1.5F; // Paper - use list instead of map
if (pathpoint2.c()) {
this.a.a(pathpoint2, pathpoint2.e + pathpoint2.f);
} else {
@@ -94,36 +113,49 @@ public class Pathfinder {
}
}
- Stream stream;
- if (set.stream().anyMatch(PathDestination::f)) {
- stream = set.stream().filter(PathDestination::f).map((pathdestination) -> {
- return this.a(pathdestination.d(), (BlockPosition) map.get(pathdestination), true);
- }).sorted(Comparator.comparingInt(PathEntity::e));
- } else {
- stream = set.stream().map((pathdestination) -> {
- return this.a(pathdestination.d(), (BlockPosition) map.get(pathdestination), false);
- }).sorted(Comparator.comparingDouble(PathEntity::l).thenComparingInt(PathEntity::e));
+ // Paper start - remove streams
+ boolean result = false;
+ for (int i1 = 0, listSize = list.size(); i1 < listSize; i1++) {
+ PathDestination pathDestination = list.get(i1).getKey(); // Paper
+ if (pathDestination.f()) {
+ result = true;
+ break;
+ }
}
-
- Optional<PathEntity> optional = stream.findFirst();
-
- if (!optional.isPresent()) {
- return null;
+ List<PathEntity> candidates = new java.util.ArrayList<>();
+ if (result) {
+ for (int i1 = 0, listSize = list.size(); i1 < listSize; i1++) {
+ Map.Entry<PathDestination, BlockPosition> entry = list.get(i1);
+ PathDestination pathdestination = entry.getKey();
+ if (pathdestination.f()) {
+ PathEntity pathEntity = this.a(pathdestination.d(), entry.getValue(), true);
+ candidates.add(pathEntity);
+ }
+ }
+ if (candidates.isEmpty()) return null;
+ candidates.sort(Comparator.comparingInt(PathEntity::e));
} else {
- PathEntity pathentity = (PathEntity) optional.get();
-
- return pathentity;
+ for (int i1 = 0, listSize = list.size(); i1 < listSize; i1++) {
+ Map.Entry<PathDestination, BlockPosition> entry = list.get(i1);
+ PathDestination pathdestination = entry.getKey();
+ PathEntity pathEntity = this.a(pathdestination.d(), entry.getValue(), false);
+ candidates.add(pathEntity);
+ }
+ if (candidates.isEmpty()) return null;
+ candidates.sort(Comparator.comparingDouble(PathEntity::l).thenComparingInt(PathEntity::e));
}
+ return candidates.get(0);
+ // Paper end
}
- private float a(PathPoint pathpoint, Set<PathDestination> set) {
+ private float a(PathPoint pathpoint, java.util.List<java.util.Map.Entry<PathDestination, BlockPosition>> list) {
float f = Float.MAX_VALUE;
float f1;
- for (Iterator iterator = set.iterator(); iterator.hasNext(); f = Math.min(f1, f)) {
- PathDestination pathdestination = (PathDestination) iterator.next();
+ for (int i = 0, listSize = list.size(); i < listSize; f = Math.min(f1, f), i++) { // Paper
+ PathDestination pathdestination = list.get(i).getKey(); // Paper
f1 = pathpoint.a(pathdestination);
pathdestination.a(f1, pathpoint);

View file

@ -0,0 +1,79 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Mariell Hoversholm <proximyst@proximyst.com>
Date: Sat, 16 May 2020 10:05:30 +0200
Subject: [PATCH] Add permission for command blocks
diff --git a/src/main/java/net/minecraft/server/BlockCommand.java b/src/main/java/net/minecraft/server/BlockCommand.java
index d0f75c8f93d3ceb1ee358baeb51f64e7e575bbc0..48cc48bd999c02268fa3270401c0df77a327ab13 100644
--- a/src/main/java/net/minecraft/server/BlockCommand.java
+++ b/src/main/java/net/minecraft/server/BlockCommand.java
@@ -110,7 +110,7 @@ public class BlockCommand extends BlockTileEntity {
public EnumInteractionResult interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, MovingObjectPositionBlock movingobjectpositionblock) {
TileEntity tileentity = world.getTileEntity(blockposition);
- if (tileentity instanceof TileEntityCommand && entityhuman.isCreativeAndOp()) {
+ if (tileentity instanceof TileEntityCommand && (entityhuman.isCreativeAndOp() || (entityhuman.isCreative() && entityhuman.getBukkitEntity().hasPermission("minecraft.commandblock")))) { // Paper - command block permission
entityhuman.a((TileEntityCommand) tileentity);
return EnumInteractionResult.SUCCESS;
} else {
diff --git a/src/main/java/net/minecraft/server/CommandBlockListenerAbstract.java b/src/main/java/net/minecraft/server/CommandBlockListenerAbstract.java
index ef2a496eda45ae5ee8fe52ef09e77c2906069d2e..6a0d70033b4eec384795b5cccd76bce2265ffbfc 100644
--- a/src/main/java/net/minecraft/server/CommandBlockListenerAbstract.java
+++ b/src/main/java/net/minecraft/server/CommandBlockListenerAbstract.java
@@ -178,7 +178,7 @@ public abstract class CommandBlockListenerAbstract implements ICommandListener {
}
public boolean a(EntityHuman entityhuman) {
- if (!entityhuman.isCreativeAndOp()) {
+ if (!entityhuman.isCreativeAndOp() && !entityhuman.isCreative() && !entityhuman.getBukkitEntity().hasPermission("minecraft.commandblock")) { // Paper - command block permission
return false;
} else {
if (entityhuman.getWorld().isClientSide) {
diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java
index 1f9d231fb93e30286205f7a0a4c898a0e153bd95..d52fbda79fe1c52d3ddb53c0f1c1f521d7620702 100644
--- a/src/main/java/net/minecraft/server/PlayerConnection.java
+++ b/src/main/java/net/minecraft/server/PlayerConnection.java
@@ -612,7 +612,7 @@ public class PlayerConnection implements PacketListenerPlayIn {
PlayerConnectionUtils.ensureMainThread(packetplayinsetcommandblock, this, this.player.getWorldServer());
if (!this.minecraftServer.getEnableCommandBlock()) {
this.player.sendMessage(new ChatMessage("advMode.notEnabled", new Object[0]));
- } else if (!this.player.isCreativeAndOp()) {
+ } else if (!this.player.isCreativeAndOp() && !this.player.isCreative() && !this.player.getBukkitEntity().hasPermission("minecraft.commandblock")) { // Paper - command block permission
this.player.sendMessage(new ChatMessage("advMode.notAllowed", new Object[0]));
} else {
CommandBlockListenerAbstract commandblocklistenerabstract = null;
@@ -675,7 +675,7 @@ public class PlayerConnection implements PacketListenerPlayIn {
PlayerConnectionUtils.ensureMainThread(packetplayinsetcommandminecart, this, this.player.getWorldServer());
if (!this.minecraftServer.getEnableCommandBlock()) {
this.player.sendMessage(new ChatMessage("advMode.notEnabled", new Object[0]));
- } else if (!this.player.isCreativeAndOp()) {
+ } else if (!this.player.isCreativeAndOp() && !this.player.isCreative() && !this.player.getBukkitEntity().hasPermission("minecraft.commandblock")) { // Paper - command block permission
this.player.sendMessage(new ChatMessage("advMode.notAllowed", new Object[0]));
} else {
CommandBlockListenerAbstract commandblocklistenerabstract = packetplayinsetcommandminecart.a(this.player.world);
diff --git a/src/main/java/net/minecraft/server/PlayerInteractManager.java b/src/main/java/net/minecraft/server/PlayerInteractManager.java
index f11ef84df85c1e7ada9c62247b7882f19ae32089..6df8434612d4afe411b2c435f4c847b9183570f8 100644
--- a/src/main/java/net/minecraft/server/PlayerInteractManager.java
+++ b/src/main/java/net/minecraft/server/PlayerInteractManager.java
@@ -350,7 +350,7 @@ public class PlayerInteractManager {
TileEntity tileentity = this.world.getTileEntity(blockposition);
Block block = iblockdata.getBlock();
- if ((block instanceof BlockCommand || block instanceof BlockStructure || block instanceof BlockJigsaw) && !this.player.isCreativeAndOp()) {
+ if ((block instanceof BlockCommand || block instanceof BlockStructure || block instanceof BlockJigsaw) && !this.player.isCreativeAndOp() && !(block instanceof BlockCommand && (this.player.isCreative() && this.player.getBukkitEntity().hasPermission("minecraft.commandblock")))) { // Paper - command block permission
this.world.notify(blockposition, iblockdata, iblockdata, 3);
return false;
} else if (this.player.a((World) this.world, blockposition, this.gamemode)) {
diff --git a/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java b/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java
index 525ebf961e5da0687183a5e2ead23ed92cbd9d79..a4a809f302c5ff9c76cde5fc0add2ceec1bdf9b5 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java
@@ -16,6 +16,7 @@ public final class CraftDefaultPermissions {
DefaultPermissions.registerPermission(ROOT + ".nbt.copy", "Gives the user the ability to copy NBT in creative", org.bukkit.permissions.PermissionDefault.TRUE, parent);
DefaultPermissions.registerPermission(ROOT + ".debugstick", "Gives the user the ability to use the debug stick in creative", org.bukkit.permissions.PermissionDefault.OP, parent);
DefaultPermissions.registerPermission(ROOT + ".debugstick.always", "Gives the user the ability to use the debug stick in all game modes", org.bukkit.permissions.PermissionDefault.FALSE, parent);
+ DefaultPermissions.registerPermission(ROOT + ".commandblock", "Gives the user the ability to use command blocks.", org.bukkit.permissions.PermissionDefault.OP, parent); // Paper
// Spigot end
parent.recalculatePermissibles();
}

View file

@ -0,0 +1,273 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Tue, 26 May 2020 21:32:05 -0400
Subject: [PATCH] Optimize Villagers
This change reimplements the entire BehaviorFindPosition method to
get rid of all of the streams, and implement the logic in a more sane way.
We keep vanilla behavior 100% the same with this change, just wrote more
optimal, as we can abort iterating POI's as soon as we find a match....
One slight change is that Minecraft adds a random delay before a POI is
attempted again. I've increased the amount of that delay based on the distance
to said POI, so farther POI's will not be attempted as often.
Additionally, we spiral out, so we favor local POI's before we ever favor farther POI's.
We also try to pathfind 1 POI at a time instead of collecting multiple POI's then tossing them
all to the pathfinder, so that once we get a match we can return before even looking at other
POI's.
This benefits us in that ideally, a villager will constantly find the near POI's and
not even try to pathfind to the farther POI. Trying to pathfind to distant POI's is
what causes significant lag.
Other improvements here is to stop spamming the POI manager with empty nullables.
Vanilla used them to represent if they needed to load POI data off disk or not.
Well, we load POI data async on chunk load, so we have it, and we surely do not ever
want to load POI data sync either for unloaded chunks!
So this massively reduces object count in the POI hashmaps, resulting in less hash collions,
and also less memory use.
Additionally, unemployed villagers were using significant time due to major ineffeciency in
the code rebuilding data that is static every single invocation for every POI type...
So we cache that and only rebuild it if professions change, which should be never unless
a plugin manipulates and adds custom professions, which it will handle by rebuilding.
diff --git a/src/main/java/net/minecraft/server/BehaviorFindPosition.java b/src/main/java/net/minecraft/server/BehaviorFindPosition.java
index 35eb3a5a61145e94d5b0c77c0eb13bfa46fac23b..6861b1a345496a83900b0ef702ba34315a030ef6 100644
--- a/src/main/java/net/minecraft/server/BehaviorFindPosition.java
+++ b/src/main/java/net/minecraft/server/BehaviorFindPosition.java
@@ -29,8 +29,55 @@ public class BehaviorFindPosition extends Behavior<EntityCreature> {
protected void a(WorldServer worldserver, EntityCreature entitycreature, long i) {
this.f = 0;
- this.d = worldserver.getTime() + (long) worldserver.getRandom().nextInt(20);
+ this.d = worldserver.getTime() + (long) java.util.concurrent.ThreadLocalRandom.current().nextInt(20); // Paper
VillagePlace villageplace = worldserver.B();
+
+ // Paper start - replace implementation completely
+ BlockPosition blockposition2 = new BlockPosition(entitycreature);
+ int dist = 48;
+ int requiredDist = dist * dist;
+ int cdist = Math.floorDiv(dist, 16);
+ Predicate<VillagePlaceType> predicate = this.a.c();
+ int maxPoiAttempts = 4;
+ int poiAttempts = 0;
+ OUT:
+ for (ChunkCoordIntPair chunkcoordintpair : MCUtil.getSpiralOutChunks(blockposition2, cdist)) {
+ for (int i1 = 0; i1 < 16; i1++) {
+ java.util.Optional<VillagePlaceSection> section = villageplace.getSection(SectionPosition.a(chunkcoordintpair, i1).v());
+ if (section == null || !section.isPresent()) continue;
+ for (java.util.Map.Entry<VillagePlaceType, java.util.Set<VillagePlaceRecord>> e : section.get().getRecords().entrySet()) {
+ if (!predicate.test(e.getKey())) continue;
+ for (VillagePlaceRecord record : e.getValue()) {
+ if (!record.hasVacancy()) continue;
+
+ BlockPosition pos = record.getPosition();
+ long key = pos.asLong();
+ if (this.e.containsKey(key)) {
+ continue;
+ }
+ double poiDist = pos.distanceSquared(blockposition2);
+ if (poiDist <= (double) requiredDist) {
+ this.e.put(key, (long) (this.d + Math.sqrt(poiDist) * 4)); // use dist instead of 40 to blacklist longer if farther distance
+ ++poiAttempts;
+ PathEntity pathentity = entitycreature.getNavigation().a(com.google.common.collect.ImmutableSet.of(pos), 8, false, this.a.d());
+
+ if (pathentity != null && pathentity.h()) {
+ record.decreaseVacancy();
+ GlobalPos globalPos = GlobalPos.create(worldserver.getWorldProvider().getDimensionManager(), pos);
+ entitycreature.getBehaviorController().setMemory(this.b, globalPos);
+ break OUT;
+ }
+ if (poiAttempts >= maxPoiAttempts) {
+ break OUT;
+ }
+ }
+ }
+ }
+ }
+ }
+ // Clean up - vanilla does this only when it runs out, but that would push it to try farther POI's...
+ this.e.long2LongEntrySet().removeIf((entry) -> entry.getLongValue() < this.d);
+ /*
Predicate<BlockPosition> predicate = (blockposition) -> {
long j = blockposition.asLong();
@@ -61,6 +108,6 @@ public class BehaviorFindPosition extends Behavior<EntityCreature> {
return entry.getLongValue() < this.d;
});
}
-
+ */ // Paper end
}
}
diff --git a/src/main/java/net/minecraft/server/RegionFileSection.java b/src/main/java/net/minecraft/server/RegionFileSection.java
index a6d8ef5eb44f3f851a3a1be4032ca21ab1d7f2b2..c3e8a0145d63843736d2060f978cdf38df359563 100644
--- a/src/main/java/net/minecraft/server/RegionFileSection.java
+++ b/src/main/java/net/minecraft/server/RegionFileSection.java
@@ -51,29 +51,15 @@ public class RegionFileSection<R extends MinecraftSerializable> extends RegionFi
@Nullable
protected Optional<R> c(long i) {
- return (Optional) this.c.get(i);
+ return this.c.getOrDefault(i, Optional.empty()); // Paper
}
+ protected final Optional<R> getSection(long i) { return d(i); } // Paper - OBFHELPER
protected Optional<R> d(long i) {
- SectionPosition sectionposition = SectionPosition.a(i);
-
- if (this.b(sectionposition)) {
- return Optional.empty();
- } else {
- Optional<R> optional = this.c(i);
-
- if (optional != null) {
- return optional;
- } else {
- this.b(sectionposition.u());
- optional = this.c(i);
- if (optional == null) {
- throw (IllegalStateException) SystemUtils.c(new IllegalStateException());
- } else {
- return optional;
- }
- }
- }
+ // Paper start - replace method - never load POI data sync, we load this in chunk load already, reduce ops
+ // If it's an unloaded chunk, well too bad.
+ return this.c(i);
+ // Paper end
}
protected boolean b(SectionPosition sectionposition) {
@@ -117,7 +103,7 @@ public class RegionFileSection<R extends MinecraftSerializable> extends RegionFi
private <T> void a(ChunkCoordIntPair chunkcoordintpair, DynamicOps<T> dynamicops, @Nullable T t0) {
if (t0 == null) {
for (int i = 0; i < 16; ++i) {
- this.c.put(SectionPosition.a(chunkcoordintpair, i).v(), Optional.empty());
+ //this.c.put(SectionPosition.a(chunkcoordintpair, i).v(), Optional.empty()); // Paper - NO!!!
}
} else {
Dynamic<T> dynamic = new Dynamic(dynamicops, t0);
@@ -135,7 +121,7 @@ public class RegionFileSection<R extends MinecraftSerializable> extends RegionFi
}, dynamic2);
});
- this.c.put(i1, optional);
+ if (optional.isPresent()) this.c.put(i1, optional); // Paper - NO!!!
optional.ifPresent((minecraftserializable) -> {
this.b(i1);
if (flag) {
@@ -199,7 +185,7 @@ public class RegionFileSection<R extends MinecraftSerializable> extends RegionFi
if (optional != null && optional.isPresent()) {
this.d.add(i);
} else {
- RegionFileSection.LOGGER.warn("No data for position: {}", SectionPosition.a(i));
+ //RegionFileSection.LOGGER.warn("No data for position: {}", SectionPosition.a(i)); // Paper - hush
}
}
diff --git a/src/main/java/net/minecraft/server/VillagePlaceRecord.java b/src/main/java/net/minecraft/server/VillagePlaceRecord.java
index 1e9d7a3f902eb4571b93bb0e58cba966365f07b8..44535a3e2c3320aac472c5a7ee557fac7bab2530 100644
--- a/src/main/java/net/minecraft/server/VillagePlaceRecord.java
+++ b/src/main/java/net/minecraft/server/VillagePlaceRecord.java
@@ -32,6 +32,7 @@ public class VillagePlaceRecord implements MinecraftSerializable {
return dynamicops.createMap(ImmutableMap.of(dynamicops.createString("pos"), this.a.a(dynamicops), dynamicops.createString("type"), dynamicops.createString(IRegistry.POINT_OF_INTEREST_TYPE.getKey(this.b).toString()), dynamicops.createString("free_tickets"), dynamicops.createInt(this.c)));
}
+ protected final boolean decreaseVacancy() { return b(); } // Paper - OBFHELPER
protected boolean b() {
if (this.c <= 0) {
return false;
@@ -42,6 +43,7 @@ public class VillagePlaceRecord implements MinecraftSerializable {
}
}
+ protected final boolean increaseVacancy() { return c(); } // Paper - OBFHELPER
protected boolean c() {
if (this.c >= this.b.b()) {
return false;
@@ -52,14 +54,17 @@ public class VillagePlaceRecord implements MinecraftSerializable {
}
}
+ public final boolean hasVacancy() { return d(); } // Paper - OBFHELPER
public boolean d() {
return this.c > 0;
}
+ public final boolean isOccupied() { return e(); } // Paper - OBFHELPER
public boolean e() {
return this.c != this.b.b();
}
+ public final BlockPosition getPosition() { return f(); } // Paper
public BlockPosition f() {
return this.a;
}
diff --git a/src/main/java/net/minecraft/server/VillagePlaceSection.java b/src/main/java/net/minecraft/server/VillagePlaceSection.java
index 3f2602dbe0995f8d01d4a1428d919405d711a205..436b064c3b277143075386fc9a71027fb5962681 100644
--- a/src/main/java/net/minecraft/server/VillagePlaceSection.java
+++ b/src/main/java/net/minecraft/server/VillagePlaceSection.java
@@ -23,7 +23,7 @@ public class VillagePlaceSection implements MinecraftSerializable {
private static final Logger LOGGER = LogManager.getLogger();
private final Short2ObjectMap<VillagePlaceRecord> b = new Short2ObjectOpenHashMap();
- private final Map<VillagePlaceType, Set<VillagePlaceRecord>> c = Maps.newHashMap();
+ private final Map<VillagePlaceType, Set<VillagePlaceRecord>> c = Maps.newHashMap(); public final Map<VillagePlaceType, Set<VillagePlaceRecord>> getRecords() { return c; } // Paper - OBFHELPER
private final Runnable d;
private boolean e;
diff --git a/src/main/java/net/minecraft/server/VillagePlaceType.java b/src/main/java/net/minecraft/server/VillagePlaceType.java
index ab3e054cd2f38756a5d802d4d981022318ab047d..c1f293fc98d3efb4665cfb9036f208b842fc8e36 100644
--- a/src/main/java/net/minecraft/server/VillagePlaceType.java
+++ b/src/main/java/net/minecraft/server/VillagePlaceType.java
@@ -12,8 +12,14 @@ import java.util.stream.Stream;
public class VillagePlaceType {
+ static Set<VillagePlaceType> professionCache; // Paper
private static final Predicate<VillagePlaceType> v = (villageplacetype) -> {
- return ((Set) IRegistry.VILLAGER_PROFESSION.d().map(VillagerProfession::b).collect(Collectors.toSet())).contains(villageplacetype);
+ // Paper start
+ if (professionCache == null) {
+ professionCache = IRegistry.VILLAGER_PROFESSION.d().map(VillagerProfession::b).collect(Collectors.toSet());
+ }
+ return professionCache.contains(villageplacetype);
+ // Paper end
};
public static final Predicate<VillagePlaceType> a = (villageplacetype) -> {
return true;
@@ -89,11 +95,11 @@ public class VillagePlaceType {
}
private static VillagePlaceType a(String s, Set<IBlockData> set, int i, int j) {
- return a((VillagePlaceType) IRegistry.POINT_OF_INTEREST_TYPE.a(new MinecraftKey(s), (Object) (new VillagePlaceType(s, set, i, j))));
+ return a((VillagePlaceType) IRegistry.POINT_OF_INTEREST_TYPE.a(new MinecraftKey(s), (new VillagePlaceType(s, set, i, j)))); // Paper - decompile error
}
private static VillagePlaceType a(String s, Set<IBlockData> set, int i, Predicate<VillagePlaceType> predicate, int j) {
- return a((VillagePlaceType) IRegistry.POINT_OF_INTEREST_TYPE.a(new MinecraftKey(s), (Object) (new VillagePlaceType(s, set, i, predicate, j))));
+ return a((VillagePlaceType) IRegistry.POINT_OF_INTEREST_TYPE.a(new MinecraftKey(s), (new VillagePlaceType(s, set, i, predicate, j)))); // Paper - decompile error
}
private static VillagePlaceType a(VillagePlaceType villageplacetype) {
diff --git a/src/main/java/net/minecraft/server/VillagerProfession.java b/src/main/java/net/minecraft/server/VillagerProfession.java
index c38296165b33698bc15fe49a2de0d0d19cfb910a..f9d7a16c79a4e3ffe8b6e7ed469236a93892f01d 100644
--- a/src/main/java/net/minecraft/server/VillagerProfession.java
+++ b/src/main/java/net/minecraft/server/VillagerProfession.java
@@ -61,6 +61,7 @@ public class VillagerProfession {
}
static VillagerProfession a(String s, VillagePlaceType villageplacetype, ImmutableSet<Item> immutableset, ImmutableSet<Block> immutableset1, @Nullable SoundEffect soundeffect) {
+ VillagePlaceType.professionCache = null; // Paper
return (VillagerProfession) IRegistry.a((IRegistry) IRegistry.VILLAGER_PROFESSION, new MinecraftKey(s), (Object) (new VillagerProfession(s, villageplacetype, immutableset, immutableset1, soundeffect)));
}
}