mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-28 03:19:21 +00:00
Update changes from ver/1.21.4 branch
This commit is contained in:
@@ -5,7 +5,7 @@ Subject: [PATCH] Cache player profileResult
|
||||
|
||||
|
||||
diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
||||
index 9dce1d22c7de3a3dd0e0e8f117cfbb54d1b15042..c4d0372feb6299b785a7e569314cc91f07870036 100644
|
||||
index 9dce1d22c7de3a3dd0e0e8f117cfbb54d1b15042..50137b67b578b0f9f34bb11a6d572df99ee9fa37 100644
|
||||
--- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
||||
+++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
||||
@@ -70,6 +70,11 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
|
||||
@@ -20,7 +20,7 @@ index 9dce1d22c7de3a3dd0e0e8f117cfbb54d1b15042..c4d0372feb6299b785a7e569314cc91f
|
||||
|
||||
public ServerLoginPacketListenerImpl(MinecraftServer server, Connection connection, boolean transferred) {
|
||||
this.server = server;
|
||||
@@ -303,9 +308,23 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
|
||||
@@ -303,9 +308,25 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
|
||||
String string1 = Objects.requireNonNull(ServerLoginPacketListenerImpl.this.requestedUsername, "Player name not initialized");
|
||||
|
||||
try {
|
||||
@@ -36,7 +36,9 @@ index 9dce1d22c7de3a3dd0e0e8f117cfbb54d1b15042..c4d0372feb6299b785a7e569314cc91f
|
||||
+ profileResult = ServerLoginPacketListenerImpl.this.server
|
||||
+ .getSessionService()
|
||||
+ .hasJoinedServer(string1, string, this.getAddress());
|
||||
+ playerProfileResultCache.put(string1, profileResult);
|
||||
+ if (profileResult != null) {
|
||||
+ playerProfileResultCache.put(string1, profileResult);
|
||||
+ }
|
||||
+ }
|
||||
+ } else {
|
||||
+ profileResult = ServerLoginPacketListenerImpl.this.server
|
||||
|
||||
@@ -177,10 +177,10 @@ index f106373ef3ac4a8685c2939c9e8361688a285913..51ae390c68e7a3aa193329cc3bc47ca6
|
||||
public boolean visible = true;
|
||||
|
||||
diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java
|
||||
index 44d87997e1ce9b846ebed541634a4478334c920c..6b421f84e2d4e286ad4b7624670c90a76e86c8fe 100644
|
||||
index 44d87997e1ce9b846ebed541634a4478334c920c..87b032ad2ba3e3e0a2e5cfcf185533102247a946 100644
|
||||
--- a/net/minecraft/server/level/ServerEntity.java
|
||||
+++ b/net/minecraft/server/level/ServerEntity.java
|
||||
@@ -460,12 +460,15 @@ public class ServerEntity {
|
||||
@@ -460,15 +460,18 @@ public class ServerEntity {
|
||||
if (this.entity instanceof LivingEntity) {
|
||||
Set<AttributeInstance> attributesToSync = ((LivingEntity)this.entity).getAttributes().getAttributesToSync();
|
||||
if (!attributesToSync.isEmpty()) {
|
||||
@@ -197,7 +197,11 @@ index 44d87997e1ce9b846ebed541634a4478334c920c..6b421f84e2d4e286ad4b7624670c90a7
|
||||
+ // Leaf end - petal - Multithreaded tracker - send in main thread
|
||||
}
|
||||
|
||||
attributesToSync.clear();
|
||||
- attributesToSync.clear();
|
||||
+ ((LivingEntity)this.entity).getAttributes().getAttributesToSync().clear(); // Leaf - Multithreaded tracker
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index 5943b18f172fb1d77ef1fe768daa8e8f43c3c8c1..7b85a9ebdbe3e8bee0a8fc100ede8a3f07eee5ce 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -234,7 +238,7 @@ index c34cf83f79314198b0f7a747e4ae68b88d09d2cd..c8590517efe4124c2b1db2b927d131b8
|
||||
if (this.player.isRemoved()) {
|
||||
LOGGER.info("Attempt to teleport removed player {} restricted", player.getScoreboardName());
|
||||
diff --git a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
|
||||
index 3ac9f36eae87369354e992a1d9b5c5b2d87d17cb..d99bbf299af2b2d3a61761c5c3c33c4d371d1b9b 100644
|
||||
index 3ac9f36eae87369354e992a1d9b5c5b2d87d17cb..11520972f4fabde3be48edd296351113453b2869 100644
|
||||
--- a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
|
||||
+++ b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
|
||||
@@ -26,8 +26,11 @@ public class AttributeInstance {
|
||||
@@ -245,34 +249,86 @@ index 3ac9f36eae87369354e992a1d9b5c5b2d87d17cb..d99bbf299af2b2d3a61761c5c3c33c4d
|
||||
- private final Map<ResourceLocation, AttributeModifier> permanentModifiers = new Object2ObjectArrayMap<>();
|
||||
+ // Leaf start - Multithreaded tracker
|
||||
+ private final boolean multiThreadedTrackingEnabled = org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled;
|
||||
+ private final Map<ResourceLocation, AttributeModifier> modifierById = multiThreadedTrackingEnabled ? new java.util.concurrent.ConcurrentHashMap<>() : new Object2ObjectArrayMap<>();
|
||||
+ private final Map<ResourceLocation, AttributeModifier> permanentModifiers = multiThreadedTrackingEnabled ? new java.util.concurrent.ConcurrentHashMap<>() : new Object2ObjectArrayMap<>();
|
||||
+ private final Map<ResourceLocation, AttributeModifier> modifierById = multiThreadedTrackingEnabled ? it.unimi.dsi.fastutil.objects.Object2ObjectMaps.synchronize(new Object2ObjectArrayMap<>(), this) : new Object2ObjectArrayMap<>();
|
||||
+ private final Map<ResourceLocation, AttributeModifier> permanentModifiers = multiThreadedTrackingEnabled ? it.unimi.dsi.fastutil.objects.Object2ObjectMaps.synchronize(new Object2ObjectArrayMap<>(), this) : new Object2ObjectArrayMap<>();
|
||||
+ // Leaf end - Multithreaded tracker
|
||||
private double baseValue;
|
||||
private boolean dirty = true;
|
||||
private double cachedValue;
|
||||
@@ -116,7 +119,15 @@ public class AttributeInstance {
|
||||
}
|
||||
|
||||
protected void setDirty() {
|
||||
- this.dirty = true;
|
||||
+ // Leaf start - Multithreaded tracker
|
||||
+ if (multiThreadedTrackingEnabled) {
|
||||
+ synchronized (this) {
|
||||
+ this.dirty = true;
|
||||
+ }
|
||||
+ } else {
|
||||
+ this.dirty = true;
|
||||
+ }
|
||||
+ // Leaf end - Multithreaded tracker
|
||||
this.onDirty.accept(this);
|
||||
}
|
||||
|
||||
@@ -143,6 +154,17 @@ public class AttributeInstance {
|
||||
}
|
||||
|
||||
public double getValue() {
|
||||
+ // Leaf start - Multithreaded tracker
|
||||
+ if (multiThreadedTrackingEnabled) {
|
||||
+ synchronized (this) {
|
||||
+ if (this.dirty) {
|
||||
+ this.cachedValue = this.calculateValue();
|
||||
+ this.dirty = false;
|
||||
+ }
|
||||
+ return this.cachedValue;
|
||||
+ }
|
||||
+ }
|
||||
+ // Leaf end - Multithreaded tracker
|
||||
if (this.dirty) {
|
||||
this.cachedValue = this.calculateValue();
|
||||
this.dirty = false;
|
||||
diff --git a/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/net/minecraft/world/entity/ai/attributes/AttributeMap.java
|
||||
index 701025715e0aca3c1f920a66f9b3d03ec08eaf02..2b8b335cf5779d1b6eb639935d1b92d82aa85d7f 100644
|
||||
index 701025715e0aca3c1f920a66f9b3d03ec08eaf02..8a299c81799b3f0c353eecce56afd14b9150df5f 100644
|
||||
--- a/net/minecraft/world/entity/ai/attributes/AttributeMap.java
|
||||
+++ b/net/minecraft/world/entity/ai/attributes/AttributeMap.java
|
||||
@@ -14,11 +14,14 @@ import net.minecraft.nbt.ListTag;
|
||||
@@ -14,11 +14,11 @@ import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public class AttributeMap {
|
||||
+ // Leaf start - Multithreaded tracker
|
||||
+ private final boolean multiThreadedTrackingEnabled = org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled;
|
||||
// Gale start - Lithium - replace AI attributes with optimized collections
|
||||
- // Gale start - Lithium - replace AI attributes with optimized collections
|
||||
- private final Map<Holder<Attribute>, AttributeInstance> attributes = new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(0);
|
||||
- private final Set<AttributeInstance> attributesToSync = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0);
|
||||
- private final Set<AttributeInstance> attributesToUpdate = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0);
|
||||
+ private final Map<Holder<Attribute>, AttributeInstance> attributes = multiThreadedTrackingEnabled ? new java.util.concurrent.ConcurrentHashMap<>() : new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(0);
|
||||
+ private final Set<AttributeInstance> attributesToSync = multiThreadedTrackingEnabled ? com.google.common.collect.Sets.newConcurrentHashSet() : new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0);
|
||||
+ private final Set<AttributeInstance> attributesToUpdate = multiThreadedTrackingEnabled ? com.google.common.collect.Sets.newConcurrentHashSet() : new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0);
|
||||
// Gale end - Lithium - replace AI attributes with optimized collections
|
||||
- // Gale end - Lithium - replace AI attributes with optimized collections
|
||||
+ // Leaf start - Multithreaded tracker
|
||||
+ private final Map<Holder<Attribute>, AttributeInstance> attributes;
|
||||
+ private final Set<AttributeInstance> attributesToSync;
|
||||
+ private final Set<AttributeInstance> attributesToUpdate;
|
||||
+ // Leaf end - Multithreaded tracker
|
||||
private final AttributeSupplier supplier;
|
||||
private final java.util.function.Function<Holder<Attribute>, AttributeInstance> createInstance; // Gale - Airplane - reduce entity allocations
|
||||
private final net.minecraft.world.entity.LivingEntity entity; // Purpur - Ridables
|
||||
@@ -32,6 +32,17 @@ public class AttributeMap {
|
||||
// Purpur end - Ridables
|
||||
this.supplier = defaultAttributes;
|
||||
this.createInstance = holder -> this.supplier.createInstance(this::onAttributeModified, holder); // Gale - Airplane - reduce entity allocations
|
||||
+ // Leaf start - Multithreaded tracker
|
||||
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) {
|
||||
+ this.attributes = it.unimi.dsi.fastutil.objects.Reference2ReferenceMaps.synchronize(new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(0));
|
||||
+ this.attributesToSync = it.unimi.dsi.fastutil.objects.ReferenceSets.synchronize(new it.unimi.dsi.fastutil.objects.ReferenceArraySet<>(0));
|
||||
+ this.attributesToUpdate = it.unimi.dsi.fastutil.objects.ReferenceSets.synchronize(new it.unimi.dsi.fastutil.objects.ReferenceArraySet<>(0));
|
||||
+ } else {
|
||||
+ this.attributes = new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(0);
|
||||
+ this.attributesToSync = new it.unimi.dsi.fastutil.objects.ReferenceArraySet<>(0);
|
||||
+ this.attributesToUpdate = new it.unimi.dsi.fastutil.objects.ReferenceArraySet<>(0);
|
||||
+ }
|
||||
+ // Leaf end - Multithreaded tracker
|
||||
}
|
||||
|
||||
private void onAttributeModified(AttributeInstance instance) {
|
||||
diff --git a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java b/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
|
||||
index 7bbeed6c998c91e68376d3f17a510d68e3cd0b27..d62ff9ebd4b55e1a9a0b51e84be868d844e5a954 100644
|
||||
--- a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Taiyou06 <kaandindar21@gmail.com>
|
||||
Date: Sun, 16 Feb 2025 19:03:23 +0100
|
||||
Subject: [PATCH] Optimize AABB
|
||||
|
||||
Pretty minor stuff but, it improves AABB.intersect by around ~5%
|
||||
|
||||
diff --git a/net/minecraft/world/phys/AABB.java b/net/minecraft/world/phys/AABB.java
|
||||
index 939fe337c8c1fa52bc0d95cff6d6a735e1125738..1a8432a7ee220ba19327aaad007e9897628bb6cb 100644
|
||||
--- a/net/minecraft/world/phys/AABB.java
|
||||
+++ b/net/minecraft/world/phys/AABB.java
|
||||
@@ -221,13 +221,16 @@ public class AABB {
|
||||
}
|
||||
|
||||
public AABB intersect(AABB other) {
|
||||
- double max = Math.max(this.minX, other.minX);
|
||||
- double max1 = Math.max(this.minY, other.minY);
|
||||
- double max2 = Math.max(this.minZ, other.minZ);
|
||||
- double min = Math.min(this.maxX, other.maxX);
|
||||
- double min1 = Math.min(this.maxY, other.maxY);
|
||||
- double min2 = Math.min(this.maxZ, other.maxZ);
|
||||
- return new AABB(max, max1, max2, min, min1, min2);
|
||||
+ // Leaf start - Optimize AABB
|
||||
+ return new AABB(
|
||||
+ this.minX > other.minX ? this.minX : other.minX,
|
||||
+ this.minY > other.minY ? this.minY : other.minY,
|
||||
+ this.minZ > other.minZ ? this.minZ : other.minZ,
|
||||
+ this.maxX < other.maxX ? this.maxX : other.maxX,
|
||||
+ this.maxY < other.maxY ? this.maxY : other.maxY,
|
||||
+ this.maxZ < other.maxZ ? this.maxZ : other.maxZ
|
||||
+ );
|
||||
+ // Leaf end - Optimize AABB
|
||||
}
|
||||
|
||||
public AABB minmax(AABB other) {
|
||||
@@ -259,16 +262,39 @@ public class AABB {
|
||||
}
|
||||
|
||||
public boolean intersects(AABB other) {
|
||||
- return this.intersects(other.minX, other.minY, other.minZ, other.maxX, other.maxY, other.maxZ);
|
||||
+ // Leaf start - Optimize AABB
|
||||
+ // Removed redundant method call overhead
|
||||
+ return this.minX < other.maxX &&
|
||||
+ this.maxX > other.minX &&
|
||||
+ this.minY < other.maxY &&
|
||||
+ this.maxY > other.minY &&
|
||||
+ this.minZ < other.maxZ &&
|
||||
+ this.maxZ > other.minZ;
|
||||
+ // Leaf end - Optimize AABB
|
||||
}
|
||||
|
||||
public boolean intersects(double x1, double y1, double z1, double x2, double y2, double z2) {
|
||||
- return this.minX < x2 && this.maxX > x1 && this.minY < y2 && this.maxY > y1 && this.minZ < z2 && this.maxZ > z1;
|
||||
+ // Leaf start - Optimize AABB
|
||||
+ // No temporary variables needed, direct comparison
|
||||
+ return this.minX < x2 &&
|
||||
+ this.maxX > x1 &&
|
||||
+ this.minY < y2 &&
|
||||
+ this.maxY > y1 &&
|
||||
+ this.minZ < z2 &&
|
||||
+ this.maxZ > z1;
|
||||
+ // Leaf end - Optimize AABB
|
||||
}
|
||||
|
||||
public boolean intersects(Vec3 min, Vec3 max) {
|
||||
return this.intersects(
|
||||
- Math.min(min.x, max.x), Math.min(min.y, max.y), Math.min(min.z, max.z), Math.max(min.x, max.x), Math.max(min.y, max.y), Math.max(min.z, max.z)
|
||||
+ // Leaf start - Optimize AABB
|
||||
+ min.x < max.x ? min.x : max.x,
|
||||
+ min.y < max.y ? min.y : max.y,
|
||||
+ min.z < max.z ? min.z : max.z,
|
||||
+ min.x > max.x ? min.x : max.x,
|
||||
+ min.y > max.y ? min.y : max.y,
|
||||
+ min.z > max.z ? min.z : max.z
|
||||
+ // Leaf end - Optimize AABB
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Taiyou06 <kaandindar21@gmail.com>
|
||||
Date: Tue, 25 Feb 2025 21:13:54 +0100
|
||||
Subject: [PATCH] Some Optimizations on SerializableChunkData
|
||||
|
||||
|
||||
diff --git a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
|
||||
index 749096358fccbd5d1d13801092255c51096eb001..62a40e88fc03b7f383bd750d72c42747ddd591b4 100644
|
||||
--- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
|
||||
@@ -469,14 +469,16 @@ public record SerializableChunkData(
|
||||
throw new IllegalArgumentException("Chunk can't be serialized: " + chunk);
|
||||
} else {
|
||||
ChunkPos pos = chunk.getPos();
|
||||
- List<SerializableChunkData.SectionData> list = new ArrayList<>(); final List<SerializableChunkData.SectionData> sectionsList = list; // Paper - starlight - OBFHELPER
|
||||
- LevelChunkSection[] sections = chunk.getSections();
|
||||
- LevelLightEngine lightEngine = level.getChunkSource().getLightEngine();
|
||||
|
||||
// Paper start - starlight
|
||||
final int minLightSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinLightSection(level);
|
||||
final int maxLightSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMaxLightSection(level);
|
||||
final int minBlockSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection(level);
|
||||
+ // Leaf start - Some Optimizations on SerializableChunkData
|
||||
+ // Pre-allocate with correct capacity to avoid resizing
|
||||
+ final int expectedSectionCount = maxLightSection - minLightSection + 1;
|
||||
+ List<SerializableChunkData.SectionData> list = new ArrayList<>(expectedSectionCount);
|
||||
+ // Leaf end - Some Optimizations on SerializableChunkData
|
||||
|
||||
final LevelChunkSection[] chunkSections = chunk.getSections();
|
||||
final ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray[] blockNibbles = ((ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk)chunk).starlight$getBlockNibbles();
|
||||
@@ -508,10 +510,11 @@ public record SerializableChunkData(
|
||||
((ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData)(Object)sectionData).starlight$setSkyLightState(skyNibble.state);
|
||||
}
|
||||
|
||||
- sectionsList.add(sectionData);
|
||||
+ list.add(sectionData); // Leaf - Some Optimizations on SerializableChunkData
|
||||
}
|
||||
// Paper end - starlight
|
||||
|
||||
+ // Pre-allocate block entities list with exact size needed
|
||||
List<CompoundTag> list1 = new ArrayList<>(chunk.getBlockEntitiesPos().size());
|
||||
|
||||
for (BlockPos blockPos : chunk.getBlockEntitiesPos()) {
|
||||
@@ -521,7 +524,16 @@ public record SerializableChunkData(
|
||||
}
|
||||
}
|
||||
|
||||
- List<CompoundTag> list2 = new ArrayList<>();
|
||||
+ // Leaf start - Some Optimizations on SerializableChunkData
|
||||
+ // For entities, use an initial estimated capacity if it's a ProtoChunk
|
||||
+ int entityEstimate = 64; // Reasonable default size
|
||||
+ if (chunk.getPersistedStatus().getChunkType() == ChunkType.PROTOCHUNK) {
|
||||
+ ProtoChunk protoChunk = (ProtoChunk)chunk;
|
||||
+ entityEstimate = Math.max(16, protoChunk.getEntities().size());
|
||||
+ }
|
||||
+ List<CompoundTag> list2 = new ArrayList<>(entityEstimate);
|
||||
+ // Leaf end - Some Optimizations on SerializableChunkData
|
||||
+
|
||||
long[] longs = null;
|
||||
if (chunk.getPersistedStatus().getChunkType() == ChunkType.PROTOCHUNK) {
|
||||
ProtoChunk protoChunk = (ProtoChunk)chunk;
|
||||
@@ -537,14 +549,18 @@ public record SerializableChunkData(
|
||||
for (Entry<Heightmap.Types, Heightmap> entry : chunk.getHeightmaps()) {
|
||||
if (chunk.getPersistedStatus().heightmapsAfter().contains(entry.getKey())) {
|
||||
long[] rawData = entry.getValue().getRawData();
|
||||
- map.put(entry.getKey(), (long[])rawData.clone());
|
||||
+ map.put(entry.getKey(), Arrays.copyOf(rawData, rawData.length)); // Leaf - Some Optimizations on SerializableChunkData
|
||||
}
|
||||
}
|
||||
|
||||
ChunkAccess.PackedTicks ticksForSerialization = chunk.getTicksForSerialization(level.getGameTime());
|
||||
- ShortList[] lists = Arrays.stream(chunk.getPostProcessing())
|
||||
- .map(list3 -> list3 != null ? new ShortArrayList(list3) : null)
|
||||
- .toArray(ShortList[]::new);
|
||||
+ // Leaf start - Some Optimizations on SerializableChunkData
|
||||
+ ShortList[] postProcessing = chunk.getPostProcessing();
|
||||
+ ShortList[] lists = new ShortList[postProcessing.length];
|
||||
+ for (int i = 0; i < postProcessing.length; i++) {
|
||||
+ lists[i] = postProcessing[i] != null ? new ShortArrayList(postProcessing[i]) : null;
|
||||
+ }
|
||||
+ // Leaf end - Some Optimizations on SerializableChunkData
|
||||
CompoundTag compoundTag = packStructureData(
|
||||
StructurePieceSerializationContext.fromLevel(level), pos, chunk.getAllStarts(), chunk.getAllReferences()
|
||||
);
|
||||
@@ -1,133 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Taiyou06 <kaandindar21@gmail.com>
|
||||
Date: Thu, 27 Feb 2025 23:39:32 +0100
|
||||
Subject: [PATCH] Rework ChunkHolderManager
|
||||
|
||||
|
||||
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
|
||||
index ea4010df54dbd17cdae22d671ea1e4bd7b685b3e..921ac2a1d381268060b9df07c3b2958737e3d14a 100644
|
||||
--- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
|
||||
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
|
||||
@@ -821,24 +821,21 @@ public final class ChunkHolderManager {
|
||||
|
||||
final int sectionShift = ((ChunkSystemServerLevel)this.world).moonrise$getRegionChunkShift();
|
||||
|
||||
- final Predicate<Ticket> expireNow = (final Ticket ticket) -> {
|
||||
- long removeDelay = ((ChunkSystemTicket<?>)(Object)ticket).moonrise$getRemoveDelay();
|
||||
- if (removeDelay == NO_TIMEOUT_MARKER) {
|
||||
- return false;
|
||||
- }
|
||||
- --removeDelay;
|
||||
- ((ChunkSystemTicket<?>)(Object)ticket).moonrise$setRemoveDelay(removeDelay);
|
||||
- return removeDelay <= 0L;
|
||||
- };
|
||||
+ // Leaf start - Rework ChunkHolderManager
|
||||
+ // Collect sections to process first to avoid concurrent modification issues
|
||||
+ List<Long> sectionKeys = new ArrayList<>();
|
||||
|
||||
for (final PrimitiveIterator.OfLong iterator = this.sectionToChunkToExpireCount.keyIterator(); iterator.hasNext();) {
|
||||
- final long sectionKey = iterator.nextLong();
|
||||
+ sectionKeys.add(iterator.nextLong());
|
||||
+ }
|
||||
|
||||
+ for (final Long sectionKey : sectionKeys) {
|
||||
+ // Skip if section was removed concurrently
|
||||
if (!this.sectionToChunkToExpireCount.containsKey(sectionKey)) {
|
||||
- // removed concurrently
|
||||
continue;
|
||||
}
|
||||
|
||||
+ // Acquire lock for this section only
|
||||
final ReentrantAreaLock.Node ticketLock = this.ticketLockArea.lock(
|
||||
CoordinateUtils.getChunkX(sectionKey) << sectionShift,
|
||||
CoordinateUtils.getChunkZ(sectionKey) << sectionShift
|
||||
@@ -846,11 +843,15 @@ public final class ChunkHolderManager {
|
||||
|
||||
try {
|
||||
final Long2IntOpenHashMap chunkToExpireCount = this.sectionToChunkToExpireCount.get(sectionKey);
|
||||
- if (chunkToExpireCount == null) {
|
||||
- // lost to some race
|
||||
+ if (chunkToExpireCount == null || chunkToExpireCount.isEmpty()) {
|
||||
+ // Section was removed or is empty, clean up
|
||||
+ if (chunkToExpireCount != null && chunkToExpireCount.isEmpty()) {
|
||||
+ this.sectionToChunkToExpireCount.remove(sectionKey);
|
||||
+ }
|
||||
continue;
|
||||
}
|
||||
|
||||
+ // Process each chunk in this section
|
||||
for (final Iterator<Long2IntMap.Entry> iterator1 = chunkToExpireCount.long2IntEntrySet().fastIterator(); iterator1.hasNext();) {
|
||||
final Long2IntMap.Entry entry = iterator1.next();
|
||||
|
||||
@@ -858,33 +859,50 @@ public final class ChunkHolderManager {
|
||||
final int expireCount = entry.getIntValue();
|
||||
|
||||
final SortedArraySet<Ticket> tickets = this.tickets.get(chunkKey);
|
||||
+ if (tickets == null || tickets.isEmpty()) {
|
||||
+ iterator1.remove();
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
final int levelBefore = getTicketLevelAt(tickets);
|
||||
+ int expiredCount = 0;
|
||||
|
||||
- final int sizeBefore = tickets.size();
|
||||
- tickets.removeIf(expireNow);
|
||||
- final int sizeAfter = tickets.size();
|
||||
- final int levelAfter = getTicketLevelAt(tickets);
|
||||
+ // More efficient ticket processing - avoids creating a new predicate each time
|
||||
+ for (Iterator<Ticket> ticketIterator = tickets.iterator(); ticketIterator.hasNext(); ) {
|
||||
+ Ticket ticket = ticketIterator.next();
|
||||
+ long removeDelay = ((ChunkSystemTicket<?>) (Object) ticket).moonrise$getRemoveDelay();
|
||||
+
|
||||
+ if (removeDelay == NO_TIMEOUT_MARKER) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ --removeDelay;
|
||||
+ if (removeDelay <= 0) {
|
||||
+ ticketIterator.remove();
|
||||
+ expiredCount++;
|
||||
+ } else {
|
||||
+ ((ChunkSystemTicket<?>) (Object) ticket).moonrise$setRemoveDelay(removeDelay);
|
||||
+ }
|
||||
+ }
|
||||
|
||||
if (tickets.isEmpty()) {
|
||||
this.tickets.remove(chunkKey);
|
||||
}
|
||||
+ final int levelAfter = getTicketLevelAt(tickets);
|
||||
if (levelBefore != levelAfter) {
|
||||
this.updateTicketLevel(chunkKey, levelAfter);
|
||||
}
|
||||
|
||||
- final int newExpireCount = expireCount - (sizeBefore - sizeAfter);
|
||||
-
|
||||
- if (newExpireCount == expireCount) {
|
||||
- continue;
|
||||
- }
|
||||
-
|
||||
- if (newExpireCount != 0) {
|
||||
- entry.setValue(newExpireCount);
|
||||
- } else {
|
||||
+ // Update expire count
|
||||
+ final int newExpireCount = expireCount - expiredCount;
|
||||
+ if (newExpireCount <= 0) {
|
||||
iterator1.remove();
|
||||
+ } else if (newExpireCount != expireCount) {
|
||||
+ entry.setValue(newExpireCount);
|
||||
}
|
||||
}
|
||||
|
||||
+ // Remove empty sections
|
||||
if (chunkToExpireCount.isEmpty()) {
|
||||
this.sectionToChunkToExpireCount.remove(sectionKey);
|
||||
}
|
||||
@@ -892,6 +910,7 @@ public final class ChunkHolderManager {
|
||||
this.ticketLockArea.unlock(ticketLock);
|
||||
}
|
||||
}
|
||||
+ // Leaf end - Rework ChunkHolderManager
|
||||
|
||||
this.processTicketUpdates();
|
||||
}
|
||||
@@ -175,7 +175,7 @@ index 6683df8d0f5a61ab094393f761a3d3a22d6e0455..4fd9313ce2c87383685d80e2533b93d5
|
||||
// Paper start - rewrite chunk system
|
||||
private volatile ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray[] blockNibbles;
|
||||
diff --git a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
|
||||
index 62a40e88fc03b7f383bd750d72c42747ddd591b4..6f4f431d5197e8157908191709776ff6d406207b 100644
|
||||
index 749096358fccbd5d1d13801092255c51096eb001..ccec1b847ba1a3667206cbeac4ed541a9fb028ea 100644
|
||||
--- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
|
||||
@@ -92,6 +92,7 @@ public record SerializableChunkData(
|
||||
@@ -230,7 +230,7 @@ index 62a40e88fc03b7f383bd750d72c42747ddd591b4..6f4f431d5197e8157908191709776ff6
|
||||
if (chunkType == ChunkType.LEVELCHUNK) {
|
||||
return this.loadStarlightLightData(level, new ImposterProtoChunk((LevelChunk)chunkAccess, false)); // Paper - starlight
|
||||
} else {
|
||||
@@ -570,6 +594,7 @@ public record SerializableChunkData(
|
||||
@@ -554,6 +578,7 @@ public record SerializableChunkData(
|
||||
persistentDataContainer = chunk.persistentDataContainer.toTagCompound();
|
||||
}
|
||||
// CraftBukkit end
|
||||
@@ -238,7 +238,7 @@ index 62a40e88fc03b7f383bd750d72c42747ddd591b4..6f4f431d5197e8157908191709776ff6
|
||||
return new SerializableChunkData(
|
||||
level.registryAccess().lookupOrThrow(Registries.BIOME),
|
||||
pos,
|
||||
@@ -590,6 +615,7 @@ public record SerializableChunkData(
|
||||
@@ -574,6 +599,7 @@ public record SerializableChunkData(
|
||||
list1,
|
||||
compoundTag
|
||||
, persistentDataContainer // CraftBukkit - persistentDataContainer
|
||||
@@ -246,7 +246,7 @@ index 62a40e88fc03b7f383bd750d72c42747ddd591b4..6f4f431d5197e8157908191709776ff6
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -674,6 +700,21 @@ public record SerializableChunkData(
|
||||
@@ -658,6 +684,21 @@ public record SerializableChunkData(
|
||||
compoundTag.put("ChunkBukkitValues", this.persistentDataContainer);
|
||||
}
|
||||
// CraftBukkit end
|
||||
@@ -268,7 +268,7 @@ index 62a40e88fc03b7f383bd750d72c42747ddd591b4..6f4f431d5197e8157908191709776ff6
|
||||
// Paper start - starlight
|
||||
if (this.lightCorrect && !this.chunkStatus.isBefore(net.minecraft.world.level.chunk.status.ChunkStatus.LIGHT)) {
|
||||
// clobber vanilla value to force vanilla to relight
|
||||
@@ -881,4 +922,50 @@ public record SerializableChunkData(
|
||||
@@ -865,4 +906,50 @@ public record SerializableChunkData(
|
||||
}
|
||||
// Paper end - starlight - convert from record
|
||||
}
|
||||
@@ -5,7 +5,7 @@ Subject: [PATCH] Optimize addOrUpdateTransientModifier
|
||||
|
||||
|
||||
diff --git a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
|
||||
index d99bbf299af2b2d3a61761c5c3c33c4d371d1b9b..643a2005567e883a2ca545d0f65bc59914ddee00 100644
|
||||
index 11520972f4fabde3be48edd296351113453b2869..5fe88b105efd3546c675b3397be46bf42e830fb3 100644
|
||||
--- a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
|
||||
+++ b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
|
||||
@@ -90,8 +90,13 @@ public class AttributeInstance {
|
||||
@@ -1,183 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Taiyou06 <kaandindar21@gmail.com>
|
||||
Date: Sun, 23 Mar 2025 11:51:44 +0100
|
||||
Subject: [PATCH] Async Block Finding
|
||||
|
||||
|
||||
diff --git a/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java b/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
|
||||
index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..007da9cb39ff76285c52ce0abdff60997acdff0f 100644
|
||||
--- a/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
|
||||
+++ b/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
|
||||
@@ -20,6 +20,18 @@ public abstract class MoveToBlockGoal extends Goal {
|
||||
private final int verticalSearchRange;
|
||||
protected int verticalSearchStart;
|
||||
|
||||
+ // Leaf start - Async Block Finding
|
||||
+ private static final java.util.concurrent.ExecutorService BLOCK_FINDER_EXECUTOR =
|
||||
+ java.util.concurrent.Executors.newSingleThreadExecutor(
|
||||
+ new com.google.common.util.concurrent.ThreadFactoryBuilder()
|
||||
+ .setNameFormat("Leaf Block Finding Thread - %d")
|
||||
+ .setDaemon(true)
|
||||
+ .build());
|
||||
+
|
||||
+ private final java.util.concurrent.ConcurrentLinkedQueue<BlockPos> candidateBlocks = new java.util.concurrent.ConcurrentLinkedQueue<>();
|
||||
+ private boolean asyncSearchInProgress = false;
|
||||
+ // Leaf end - Async Block Finding
|
||||
+
|
||||
public MoveToBlockGoal(PathfinderMob mob, double speedModifier, int searchRange) {
|
||||
this(mob, speedModifier, searchRange, 1);
|
||||
}
|
||||
@@ -29,6 +41,10 @@ public abstract class MoveToBlockGoal extends Goal {
|
||||
super.stop();
|
||||
this.blockPos = BlockPos.ZERO;
|
||||
this.mob.movingTarget = null;
|
||||
+ // Leaf start - Async Block Finding - Reset async state on goal stop
|
||||
+ this.candidateBlocks.clear();
|
||||
+ this.asyncSearchInProgress = false;
|
||||
+ // Leaf end - Async Block Finding - Reset async state on goal stop
|
||||
}
|
||||
// Paper end
|
||||
|
||||
@@ -53,23 +69,23 @@ public abstract class MoveToBlockGoal extends Goal {
|
||||
}
|
||||
|
||||
protected int nextStartTick(PathfinderMob creature) {
|
||||
- return reducedTickDelay(200 + creature.getRandom().nextInt(200));
|
||||
+ return Goal.reducedTickDelay(200 + creature.getRandom().nextInt(200)); // Leaf - Async Block Finding - Use the static method from the Goal class directly
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canContinueToUse() {
|
||||
- return this.tryTicks >= -this.maxStayTicks && this.tryTicks <= 1200 && this.isValidTarget(this.mob.level(), this.blockPos);
|
||||
+ return this.tryTicks >= -this.maxStayTicks && this.tryTicks <= 1200 && this.blockPos != BlockPos.ZERO && this.isValidTarget(this.mob.level(), this.blockPos); // Leaf - Async Block Finding
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
- this.moveMobToBlock();
|
||||
+ if (this.blockPos != BlockPos.ZERO) this.moveMobToBlock(); // Leaf - Async Block Finding
|
||||
this.tryTicks = 0;
|
||||
this.maxStayTicks = this.mob.getRandom().nextInt(this.mob.getRandom().nextInt(1200) + 1200) + 1200;
|
||||
}
|
||||
|
||||
protected void moveMobToBlock() {
|
||||
- this.mob.getNavigation().moveTo(this.blockPos.getX() + 0.5, this.blockPos.getY() + 1, this.blockPos.getZ() + 0.5, this.speedModifier);
|
||||
+ if (this.blockPos != BlockPos.ZERO) this.mob.getNavigation().moveTo(this.blockPos.getX() + 0.5, this.blockPos.getY() + 1, this.blockPos.getZ() + 0.5, this.speedModifier); // Leaf - Async Block Finding
|
||||
}
|
||||
|
||||
public double acceptedDistance() {
|
||||
@@ -77,7 +93,7 @@ public abstract class MoveToBlockGoal extends Goal {
|
||||
}
|
||||
|
||||
protected BlockPos getMoveToTarget() {
|
||||
- return this.blockPos.above();
|
||||
+ return this.blockPos != BlockPos.ZERO ? this.blockPos.above() : BlockPos.ZERO; // Leaf - Async Block Finding
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -87,7 +103,10 @@ public abstract class MoveToBlockGoal extends Goal {
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
+ if (this.blockPos == BlockPos.ZERO) return; // Leaf - Async Block Finding
|
||||
BlockPos moveToTarget = this.getMoveToTarget();
|
||||
+ if (moveToTarget == BlockPos.ZERO) return; // Leaf - Async Block Finding
|
||||
+
|
||||
if (!moveToTarget.closerToCenterThan(this.mob.position(), this.acceptedDistance())) {
|
||||
this.reachedTarget = false;
|
||||
this.tryTicks++;
|
||||
@@ -109,20 +128,90 @@ public abstract class MoveToBlockGoal extends Goal {
|
||||
}
|
||||
|
||||
protected boolean findNearestBlock() {
|
||||
+ // Leaf start - Async Block Finding
|
||||
+ if (!org.dreeam.leaf.config.modules.async.AsyncBlockFinding.enabled) {
|
||||
+ return findNearestBlockSync();
|
||||
+ }
|
||||
+
|
||||
+ while (!candidateBlocks.isEmpty()) {
|
||||
+ BlockPos pos = candidateBlocks.poll();
|
||||
+ if (pos != null && this.mob.level().hasChunkAt(pos) &&
|
||||
+ this.mob.isWithinRestriction(pos) &&
|
||||
+ this.isValidTarget(this.mob.level(), pos)) {
|
||||
+
|
||||
+ this.blockPos = pos;
|
||||
+ this.mob.movingTarget = pos == BlockPos.ZERO ? null : pos;
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (asyncSearchInProgress) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ // Check again before starting, avoids tiny race condition if canUse is called rapidly
|
||||
+ if (!asyncSearchInProgress) {
|
||||
+ asyncSearchInProgress = true;
|
||||
+ final BlockPos centerPos = this.mob.blockPosition().immutable();
|
||||
+ final int searchRange = this.searchRange;
|
||||
+ final int verticalRange = this.verticalSearchRange;
|
||||
+ final int verticalStart = this.verticalSearchStart;
|
||||
+
|
||||
+ BLOCK_FINDER_EXECUTOR.execute(() -> {
|
||||
+ try {
|
||||
+ generateCandidateBlocks(centerPos, searchRange, verticalRange, verticalStart);
|
||||
+ } catch (Exception e) {
|
||||
+ e.printStackTrace(); // Keep basic error logging
|
||||
+ } finally {
|
||||
+ asyncSearchInProgress = false;
|
||||
+ }
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ private void generateCandidateBlocks(BlockPos center, int searchRange, int verticalRange, int verticalStart) {
|
||||
+ java.util.List<BlockPos> positions = new java.util.ArrayList<>();
|
||||
+
|
||||
+ for (int i2 = verticalStart; i2 <= verticalRange; i2 = i2 > 0 ? -i2 : 1 - i2) {
|
||||
+ for (int i3 = 0; i3 < searchRange; i3++) {
|
||||
+ for (int i4 = 0; i4 <= i3; i4 = i4 > 0 ? -i4 : 1 - i4) {
|
||||
+ for (int i5 = i4 < i3 && i4 > -i3 ? i3 : 0; i5 <= i3; i5 = i5 > 0 ? -i5 : 1 - i5) {
|
||||
+ BlockPos pos = center.offset(i4, i2 - 1, i5);
|
||||
+ positions.add(pos.immutable());
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ positions.sort((p1, p2) -> {
|
||||
+ double d1 = p1.distSqr(center);
|
||||
+ double d2 = p2.distSqr(center);
|
||||
+ return Double.compare(d1, d2);
|
||||
+ });
|
||||
+
|
||||
+ for (BlockPos pos : positions) {
|
||||
+ candidateBlocks.add(pos);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ protected boolean findNearestBlockSync() {
|
||||
+ // Leaf end - Async Block Finding
|
||||
int i = this.searchRange;
|
||||
int i1 = this.verticalSearchRange;
|
||||
- BlockPos blockPos = this.mob.blockPosition();
|
||||
+ BlockPos blockPosOrigin = this.mob.blockPosition(); // Leaf - Async Block Finding
|
||||
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
|
||||
|
||||
for (int i2 = this.verticalSearchStart; i2 <= i1; i2 = i2 > 0 ? -i2 : 1 - i2) {
|
||||
for (int i3 = 0; i3 < i; i3++) {
|
||||
for (int i4 = 0; i4 <= i3; i4 = i4 > 0 ? -i4 : 1 - i4) {
|
||||
for (int i5 = i4 < i3 && i4 > -i3 ? i3 : 0; i5 <= i3; i5 = i5 > 0 ? -i5 : 1 - i5) {
|
||||
- mutableBlockPos.setWithOffset(blockPos, i4, i2 - 1, i5);
|
||||
+ mutableBlockPos.setWithOffset(blockPosOrigin, i4, i2 - 1, i5); // Leaf - Async Block Finding
|
||||
if (!this.mob.level().hasChunkAt(mutableBlockPos)) continue; // Gale - Airplane - block goal does not load chunks - if this block isn't loaded, continue
|
||||
if (this.mob.isWithinRestriction(mutableBlockPos) && this.isValidTarget(this.mob.level(), mutableBlockPos)) {
|
||||
- this.blockPos = mutableBlockPos;
|
||||
- this.mob.movingTarget = mutableBlockPos == BlockPos.ZERO ? null : mutableBlockPos.immutable(); // Paper
|
||||
+ this.blockPos = mutableBlockPos.immutable(); // Leaf - Async Block Finding
|
||||
+ this.mob.movingTarget = this.blockPos == BlockPos.ZERO ? null : this.blockPos; // Paper // Leaf - Async Block Finding
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Taiyou06 <kaandindar21@gmail.com>
|
||||
Date: Sun, 13 Apr 2025 16:15:17 +0200
|
||||
Subject: [PATCH] Replace ConcurrentLong2ReferenceChainedHashTable with custom
|
||||
map
|
||||
|
||||
|
||||
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/queue/ChunkUnloadQueue.java b/ca/spottedleaf/moonrise/patches/chunk_system/queue/ChunkUnloadQueue.java
|
||||
index 7eafc5b7cba23d8dec92ecc1050afe3fd8c9e309..9b421f0681fe740520457951b1a1632ada59438a 100644
|
||||
--- a/ca/spottedleaf/moonrise/patches/chunk_system/queue/ChunkUnloadQueue.java
|
||||
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/queue/ChunkUnloadQueue.java
|
||||
@@ -16,7 +16,7 @@ public final class ChunkUnloadQueue {
|
||||
|
||||
public final int coordinateShift;
|
||||
private final AtomicLong orderGenerator = new AtomicLong();
|
||||
- private final ConcurrentLong2ReferenceChainedHashTable<UnloadSection> unloadSections = new ConcurrentLong2ReferenceChainedHashTable<>();
|
||||
+ private final org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<UnloadSection> unloadSections = new org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<>(); // Leaf - Replace ConcurrentLong2ReferenceChainedHashTable with custom map
|
||||
|
||||
/*
|
||||
* Note: write operations do not occur in parallel for any given section.
|
||||
@@ -32,8 +32,10 @@ public final class ChunkUnloadQueue {
|
||||
public List<SectionToUnload> retrieveForAllRegions() {
|
||||
final List<SectionToUnload> ret = new ArrayList<>();
|
||||
|
||||
- for (final Iterator<ConcurrentLong2ReferenceChainedHashTable.TableEntry<UnloadSection>> iterator = this.unloadSections.entryIterator(); iterator.hasNext();) {
|
||||
- final ConcurrentLong2ReferenceChainedHashTable.TableEntry<UnloadSection> entry = iterator.next();
|
||||
+ // Leaf start - Replace ConcurrentLong2ReferenceChainedHashTable with custom map
|
||||
+ for (final Iterator<org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable.TableEntry<UnloadSection>> iterator = this.unloadSections.entryIterator(); iterator.hasNext(); ) {
|
||||
+ final org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable.TableEntry<UnloadSection> entry = iterator.next();
|
||||
+ // Leaf end - Replace ConcurrentLong2ReferenceChainedHashTable with custom map
|
||||
final long key = entry.getKey();
|
||||
final UnloadSection section = entry.getValue();
|
||||
final int sectionX = CoordinateUtils.getChunkX(key);
|
||||
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
|
||||
index ea4010df54dbd17cdae22d671ea1e4bd7b685b3e..9f771d78f5401ce8776de38d11e453e8e5857572 100644
|
||||
--- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
|
||||
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
|
||||
@@ -72,11 +72,13 @@ public final class ChunkHolderManager {
|
||||
private static final long NO_TIMEOUT_MARKER = Long.MIN_VALUE;
|
||||
public final ReentrantAreaLock ticketLockArea;
|
||||
|
||||
- private final ConcurrentLong2ReferenceChainedHashTable<SortedArraySet<Ticket>> tickets = new ConcurrentLong2ReferenceChainedHashTable<>();
|
||||
- private final ConcurrentLong2ReferenceChainedHashTable<Long2IntOpenHashMap> sectionToChunkToExpireCount = new ConcurrentLong2ReferenceChainedHashTable<>();
|
||||
+ // Leaf start - Replace ConcurrentLong2ReferenceChainedHashTable with custom map
|
||||
+ private final org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<SortedArraySet<Ticket>> tickets = new org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<>();
|
||||
+ private final org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<Long2IntOpenHashMap> sectionToChunkToExpireCount = new org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<>();
|
||||
+ // Leaf end - Replace ConcurrentLong2ReferenceChainedHashTable with custom map
|
||||
final ChunkUnloadQueue unloadQueue;
|
||||
|
||||
- private final ConcurrentLong2ReferenceChainedHashTable<NewChunkHolder> chunkHolders = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(16384, 0.25f);
|
||||
+ private final org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<NewChunkHolder> chunkHolders = org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable.createWithCapacity(16384, 0.25f); // Leaf - Replace ConcurrentLong2ReferenceChainedHashTable with custom map
|
||||
private final ServerLevel world;
|
||||
private final ChunkTaskScheduler taskScheduler;
|
||||
private long currentTick;
|
||||
@@ -1502,9 +1504,9 @@ public final class ChunkHolderManager {
|
||||
final JsonArray allTicketsJson = new JsonArray();
|
||||
ret.add("tickets", allTicketsJson);
|
||||
|
||||
- for (final Iterator<ConcurrentLong2ReferenceChainedHashTable.TableEntry<SortedArraySet<Ticket>>> iterator = this.tickets.entryIterator();
|
||||
+ for (final Iterator<org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable.TableEntry<SortedArraySet<Ticket>>> iterator = this.tickets.entryIterator(); // Leaf - Replace ConcurrentLong2ReferenceChainedHashTable with custom map
|
||||
iterator.hasNext();) {
|
||||
- final ConcurrentLong2ReferenceChainedHashTable.TableEntry<SortedArraySet<Ticket>> coordinateTickets = iterator.next();
|
||||
+ final org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable.TableEntry<SortedArraySet<Ticket>> coordinateTickets = iterator.next(); // Leaf - Replace ConcurrentLong2ReferenceChainedHashTable with custom map
|
||||
final long coordinate = coordinateTickets.getKey();
|
||||
final SortedArraySet<Ticket> tickets = coordinateTickets.getValue();
|
||||
|
||||
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ThreadedTicketLevelPropagator.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ThreadedTicketLevelPropagator.java
|
||||
index 310a8f80debadd64c2d962ebf83b7d0505ce6e42..3a7fad46465cac8d2c1b0933b457f5b075586709 100644
|
||||
--- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ThreadedTicketLevelPropagator.java
|
||||
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ThreadedTicketLevelPropagator.java
|
||||
@@ -35,11 +35,11 @@ public abstract class ThreadedTicketLevelPropagator {
|
||||
}
|
||||
|
||||
private final UpdateQueue updateQueue;
|
||||
- private final ConcurrentLong2ReferenceChainedHashTable<Section> sections;
|
||||
+ private final org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<Section> sections; // Leaf - Replace ConcurrentLong2ReferenceChainedHashTable with custom map
|
||||
|
||||
public ThreadedTicketLevelPropagator() {
|
||||
this.updateQueue = new UpdateQueue();
|
||||
- this.sections = new ConcurrentLong2ReferenceChainedHashTable<>();
|
||||
+ this.sections = new org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<>(); // Leaf - Replace ConcurrentLong2ReferenceChainedHashTable with custom map
|
||||
}
|
||||
|
||||
// must hold ticket lock for:
|
||||
diff --git a/ca/spottedleaf/moonrise/patches/starlight/light/StarLightInterface.java b/ca/spottedleaf/moonrise/patches/starlight/light/StarLightInterface.java
|
||||
index e1812910d7c3941dec3d4f1c90f4cf966a631de3..87c229db79f6c6dc98811c7cbccbe5c549fd744f 100644
|
||||
--- a/ca/spottedleaf/moonrise/patches/starlight/light/StarLightInterface.java
|
||||
+++ b/ca/spottedleaf/moonrise/patches/starlight/light/StarLightInterface.java
|
||||
@@ -739,7 +739,7 @@ public final class StarLightInterface {
|
||||
|
||||
public static final class ServerLightQueue extends LightQueue {
|
||||
|
||||
- private final ConcurrentLong2ReferenceChainedHashTable<ServerChunkTasks> chunkTasks = new ConcurrentLong2ReferenceChainedHashTable<>();
|
||||
+ private final org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<ServerChunkTasks> chunkTasks = new org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<>(); // Leaf - Replace ConcurrentLong2ReferenceChainedHashTable with custom map
|
||||
|
||||
public ServerLightQueue(final StarLightInterface lightInterface) {
|
||||
super(lightInterface);
|
||||
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
|
||||
index 0f9d18dd29e210ad656da211a3cb1cb25cd4efb1..f89fb321e50338e7765476cb5d7bdf2f02a497b3 100644
|
||||
--- a/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -76,7 +76,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
||||
@VisibleForDebug
|
||||
private NaturalSpawner.SpawnState lastSpawnState;
|
||||
// Paper start
|
||||
- private final ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<net.minecraft.world.level.chunk.LevelChunk> fullChunks = new ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<>();
|
||||
+ private final org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<LevelChunk> fullChunks = new org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<>(); // Leaf - Replace ConcurrentLong2ReferenceChainedHashTable with custom map
|
||||
public int getFullChunksCount() {
|
||||
return this.fullChunks.size();
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,74 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Taiyou06 <kaandindar21@gmail.com>
|
||||
Date: Mon, 14 Apr 2025 14:36:57 +0200
|
||||
Subject: [PATCH] Optimize ThreadedTicketLevelPropagator
|
||||
|
||||
|
||||
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ThreadedTicketLevelPropagator.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ThreadedTicketLevelPropagator.java
|
||||
index 3a7fad46465cac8d2c1b0933b457f5b075586709..a2d76e6fabf2749a1a9f21fe6bdf6524af8bb9b7 100644
|
||||
--- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ThreadedTicketLevelPropagator.java
|
||||
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ThreadedTicketLevelPropagator.java
|
||||
@@ -998,6 +998,7 @@ public abstract class ThreadedTicketLevelPropagator {
|
||||
final int decodeOffsetZ = -this.encodeOffsetZ;
|
||||
final int encodeOffset = this.coordinateOffset;
|
||||
final int sectionOffset = this.sectionIndexOffset;
|
||||
+ final Section[] sectionsArray = this.sections; // Leaf - Optimize ThreadedTicketLevelPropagator
|
||||
|
||||
final Long2ByteLinkedOpenHashMap updatedPositions = this.updatedPositions;
|
||||
|
||||
@@ -1012,13 +1013,27 @@ public abstract class ThreadedTicketLevelPropagator {
|
||||
int propagateDirectionBitset = (int)(queueValue >>> (COORDINATE_BITS + COORDINATE_BITS + LEVEL_BITS)) & ((1 << 16) - 1);
|
||||
|
||||
if ((queueValue & FLAG_RECHECK_LEVEL) != 0L) {
|
||||
- if (this.getLevel(posX, posZ) != propagatedLevel) {
|
||||
+ // Leaf start - Optimize ThreadedTicketLevelPropagator
|
||||
+ final int sectionX = posX >> SECTION_SHIFT;
|
||||
+ final int sectionZ = posZ >> SECTION_SHIFT;
|
||||
+ final Section section = sectionsArray[sectionX + (sectionZ * SECTION_CACHE_WIDTH) + sectionOffset];
|
||||
+ final int localIdx = (posX & (SECTION_SIZE - 1)) | ((posZ & (SECTION_SIZE - 1)) << SECTION_SHIFT);
|
||||
+ if ((section.levels[localIdx] & 0xFF) != propagatedLevel) {
|
||||
+ // Leaf end - Optimize ThreadedTicketLevelPropagator
|
||||
// not at the level we expect, so something changed.
|
||||
continue;
|
||||
}
|
||||
} else if ((queueValue & FLAG_WRITE_LEVEL) != 0L) {
|
||||
// these are used to restore sources after a propagation decrease
|
||||
- this.setLevel(posX, posZ, propagatedLevel);
|
||||
+ // Leaf start - Optimize ThreadedTicketLevelPropagator
|
||||
+ final int sectionX = posX >> SECTION_SHIFT;
|
||||
+ final int sectionZ = posZ >> SECTION_SHIFT;
|
||||
+ final Section section = sectionsArray[sectionX + (sectionZ * SECTION_CACHE_WIDTH) + sectionOffset];
|
||||
+ final int localIdx = (posX & (SECTION_SIZE - 1)) | ((posZ & (SECTION_SIZE - 1)) << SECTION_SHIFT);
|
||||
+ final short currentLevel = section.levels[localIdx];
|
||||
+ section.levels[localIdx] = (short) ((currentLevel & ~0xFF) | (propagatedLevel & 0xFF));
|
||||
+ updatedPositions.put(CoordinateUtils.getChunkKey(posX, posZ), (byte) propagatedLevel);
|
||||
+ // Leaf end - Optimize ThreadedTicketLevelPropagator
|
||||
}
|
||||
|
||||
// this bitset represents the values that we have not propagated to
|
||||
@@ -1093,7 +1108,7 @@ public abstract class ThreadedTicketLevelPropagator {
|
||||
currentPropagation ^= (bitsetLine1 | bitsetLine2 | bitsetLine3);
|
||||
|
||||
// now try to propagate
|
||||
- final Section section = this.sections[sectionIndex];
|
||||
+ final Section section = sectionsArray[sectionIndex]; // Leaf - Optimize ThreadedTicketLevelPropagator
|
||||
|
||||
// lower 8 bits are current level, next upper 7 bits are source level, next 1 bit is updated source flag
|
||||
final short currentStoredLevel = section.levels[localIndex];
|
||||
@@ -1144,6 +1159,7 @@ public abstract class ThreadedTicketLevelPropagator {
|
||||
final int decodeOffsetZ = -this.encodeOffsetZ;
|
||||
final int encodeOffset = this.coordinateOffset;
|
||||
final int sectionOffset = this.sectionIndexOffset;
|
||||
+ final Section[] sectionsArray = this.sections; // Leaf - Optimize ThreadedTicketLevelPropagator
|
||||
|
||||
final Long2ByteLinkedOpenHashMap updatedPositions = this.updatedPositions;
|
||||
|
||||
@@ -1227,7 +1243,7 @@ public abstract class ThreadedTicketLevelPropagator {
|
||||
final long bitsetLine3 = currentPropagation & (7L << (start + (8 + 8)));
|
||||
|
||||
// now try to propagate
|
||||
- final Section section = this.sections[sectionIndex];
|
||||
+ final Section section = sectionsArray[sectionIndex]; // Leaf - Optimize ThreadedTicketLevelPropagator
|
||||
|
||||
// lower 8 bits are current level, next upper 7 bits are source level, next 1 bit is updated source flag
|
||||
final short currentStoredLevel = section.levels[localIndex];
|
||||
@@ -1,319 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Taiyou06 <kaandindar21@gmail.com>
|
||||
Date: Sat, 29 Mar 2025 13:40:46 +0100
|
||||
Subject: [PATCH] Async Target Finding
|
||||
|
||||
|
||||
diff --git a/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java b/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java
|
||||
index 024792900c8ab716e91ef512d2da22548075044d..7f256793232cfa9666728223cb9964e49ff8b6ba 100644
|
||||
--- a/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java
|
||||
+++ b/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java
|
||||
@@ -16,9 +16,37 @@ public class NearestAttackableTargetGoal<T extends LivingEntity> extends TargetG
|
||||
protected final Class<T> targetType;
|
||||
protected final int randomInterval;
|
||||
@Nullable
|
||||
- protected LivingEntity target;
|
||||
+ protected volatile LivingEntity target; // Leaf - Async Target Finding
|
||||
protected TargetingConditions targetConditions;
|
||||
|
||||
+ // Leaf start - Async Target Finding
|
||||
+ // Single thread executor to prevent overwhelming the server
|
||||
+ private static final java.util.concurrent.ExecutorService TARGET_FINDER_EXECUTOR = java.util.concurrent.Executors.newSingleThreadExecutor(r -> {
|
||||
+ Thread thread = new Thread(r, "Leaf - Target-Finder-Thread");
|
||||
+ thread.setDaemon(true);
|
||||
+ thread.setPriority(Thread.MIN_PRIORITY); // Lower priority to avoid competing with main thread
|
||||
+ return thread;
|
||||
+ });
|
||||
+
|
||||
+ // Flag to track if a search is in progress
|
||||
+ private final java.util.concurrent.atomic.AtomicBoolean isSearching = new java.util.concurrent.atomic.AtomicBoolean(false);
|
||||
+ private final java.util.concurrent.atomic.AtomicReference<LivingEntity> pendingTarget = new java.util.concurrent.atomic.AtomicReference<>(null);
|
||||
+ static {
|
||||
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
+ try {
|
||||
+ TARGET_FINDER_EXECUTOR.shutdown();
|
||||
+ TARGET_FINDER_EXECUTOR.awaitTermination(2, java.util.concurrent.TimeUnit.SECONDS);
|
||||
+ } catch (InterruptedException e) {
|
||||
+ Thread.currentThread().interrupt();
|
||||
+ } finally {
|
||||
+ if (!TARGET_FINDER_EXECUTOR.isTerminated()) {
|
||||
+ TARGET_FINDER_EXECUTOR.shutdownNow();
|
||||
+ }
|
||||
+ }
|
||||
+ }));
|
||||
+ }
|
||||
+ // Leaf end - Async Target Finding
|
||||
+
|
||||
public NearestAttackableTargetGoal(Mob mob, Class<T> targetType, boolean mustSee) {
|
||||
this(mob, targetType, 10, mustSee, false, null);
|
||||
}
|
||||
@@ -46,8 +74,14 @@ public class NearestAttackableTargetGoal<T extends LivingEntity> extends TargetG
|
||||
if (this.randomInterval > 0 && this.mob.getRandom().nextInt(this.randomInterval) != 0) {
|
||||
return false;
|
||||
} else {
|
||||
- this.findTarget();
|
||||
- return this.target != null;
|
||||
+ // Leaf start - Async Target Finding
|
||||
+ findTarget();
|
||||
+ LivingEntity pending = pendingTarget.getAndSet(null);
|
||||
+ if (pending != null && !pending.isRemoved() && pending.isAlive()) {
|
||||
+ this.target = pending;
|
||||
+ }
|
||||
+ return this.target != null && this.target.isAlive() && !this.target.isRemoved();
|
||||
+ // Leaf end - Async Target Finding
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,25 +89,239 @@ public class NearestAttackableTargetGoal<T extends LivingEntity> extends TargetG
|
||||
return this.mob.getBoundingBox().inflate(targetDistance, targetDistance, targetDistance);
|
||||
}
|
||||
|
||||
+ // Leaf start - Async Target Finding
|
||||
+ // Async find target implementation with safer entity handling
|
||||
protected void findTarget() {
|
||||
- ServerLevel serverLevel = getServerLevel(this.mob);
|
||||
- if (this.targetType != Player.class && this.targetType != ServerPlayer.class) {
|
||||
- this.target = serverLevel.getNearestEntity(
|
||||
- this.mob.level().getEntitiesOfClass(this.targetType, this.getTargetSearchArea(this.getFollowDistance()), entity -> true),
|
||||
- this.getTargetConditions(),
|
||||
- this.mob,
|
||||
- this.mob.getX(),
|
||||
- this.mob.getEyeY(),
|
||||
- this.mob.getZ()
|
||||
- );
|
||||
- } else {
|
||||
- this.target = serverLevel.getNearestPlayer(this.getTargetConditions(), this.mob, this.mob.getX(), this.mob.getEyeY(), this.mob.getZ());
|
||||
+ // If async is disabled or we're already searching, use sync method
|
||||
+ if (!org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled || !isSearching.compareAndSet(false, true)) {
|
||||
+ findTargetSync();
|
||||
+ return;
|
||||
}
|
||||
+
|
||||
+ // Capture mutable state to avoid race conditions
|
||||
+ final Mob mob = this.mob;
|
||||
+
|
||||
+ // Safety check
|
||||
+ if (mob == null || mob.isRemoved() || !mob.isAlive()) {
|
||||
+ isSearching.set(false);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ final double x = mob.getX();
|
||||
+ final double y = mob.getEyeY();
|
||||
+ final double z = mob.getZ();
|
||||
+ final double followDistance = this.getFollowDistance();
|
||||
+ final TargetingConditions targetConditions = this.getTargetConditions();
|
||||
+ final Class<T> targetType = this.targetType;
|
||||
+
|
||||
+ // Start async search with immutable captured state - using submit instead of runAsync
|
||||
+ java.util.concurrent.CompletableFuture.supplyAsync(() -> {
|
||||
+ try {
|
||||
+ ServerLevel serverLevel = getServerLevel(mob);
|
||||
+ if (serverLevel == null) {
|
||||
+ return null;
|
||||
+ }
|
||||
+ if (mob.isRemoved() || !mob.isAlive()) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ try {
|
||||
+ if (targetType != Player.class && targetType != ServerPlayer.class) {
|
||||
+ AABB searchArea = new AABB(
|
||||
+ x - followDistance, y - followDistance, z - followDistance,
|
||||
+ x + followDistance, y + followDistance, z + followDistance
|
||||
+ );
|
||||
+
|
||||
+ java.util.List<T> entities = null;
|
||||
+ try {
|
||||
+ entities = mob.level().getEntitiesOfClass(targetType, searchArea, entity -> true);
|
||||
+ } catch (Exception e) {
|
||||
+ System.err.println("Error getting entities: " + e.getMessage());
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ if (entities != null && !entities.isEmpty()) {
|
||||
+ return findNearestEntitySafely(entities, targetConditions, mob, x, y, z, serverLevel);
|
||||
+ }
|
||||
+ } else {
|
||||
+ return findNearestPlayerSafely(targetConditions, mob, x, y, z, serverLevel);
|
||||
+ }
|
||||
+ } catch (Exception e) {
|
||||
+ System.err.println("Error finding entities in async target finder: " + e.getMessage());
|
||||
+ }
|
||||
+
|
||||
+ return null;
|
||||
+ } catch (Exception e) {
|
||||
+ System.err.println("Error during async target finding: " + e.getMessage());
|
||||
+ return null;
|
||||
+ } finally {
|
||||
+ isSearching.set(false);
|
||||
+ }
|
||||
+ }, TARGET_FINDER_EXECUTOR).thenAccept(result -> {
|
||||
+ if (result != null && result.isAlive() && !result.isRemoved()) {
|
||||
+ pendingTarget.set(result);
|
||||
+ }
|
||||
+ });
|
||||
}
|
||||
|
||||
+ @Nullable
|
||||
+ private LivingEntity findNearestEntitySafely(
|
||||
+ java.util.List<? extends LivingEntity> entities,
|
||||
+ TargetingConditions conditions,
|
||||
+ Mob source,
|
||||
+ double x,
|
||||
+ double y,
|
||||
+ double z,
|
||||
+ ServerLevel level) {
|
||||
+
|
||||
+ if (entities == null || entities.isEmpty() || level == null) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ try {
|
||||
+ double closestDistSq = -1.0;
|
||||
+ LivingEntity closest = null;
|
||||
+
|
||||
+ for (int i = 0; i < entities.size(); i++) {
|
||||
+ try {
|
||||
+ LivingEntity entity = entities.get(i);
|
||||
+ if (entity == null || entity.isRemoved() || !entity.isAlive()) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (conditions.test(level, source, entity)) {
|
||||
+ double dx = entity.getX() - x;
|
||||
+ double dy = entity.getY() - y;
|
||||
+ double dz = entity.getZ() - z;
|
||||
+ double distSq = dx * dx + dy * dy + dz * dz;
|
||||
+
|
||||
+ if (closestDistSq == -1.0 || distSq < closestDistSq) {
|
||||
+ closestDistSq = distSq;
|
||||
+ closest = entity;
|
||||
+ }
|
||||
+ }
|
||||
+ } catch (IndexOutOfBoundsException e) {
|
||||
+ break;
|
||||
+ } catch (Exception e) {
|
||||
+ System.err.println("Error processing entity in findNearestEntitySafely: " + e.getMessage());
|
||||
+ continue;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return closest;
|
||||
+ } catch (Exception e) {
|
||||
+ System.err.println("Error in findNearestEntitySafely: " + e.getMessage());
|
||||
+ return null;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ @Nullable
|
||||
+ private Player findNearestPlayerSafely(
|
||||
+ TargetingConditions conditions,
|
||||
+ Mob source,
|
||||
+ double x,
|
||||
+ double y,
|
||||
+ double z,
|
||||
+ ServerLevel level) {
|
||||
+
|
||||
+ if (level == null) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ try {
|
||||
+ java.util.List<? extends Player> players = level.players();
|
||||
+ if (players == null || players.isEmpty()) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ double closestDistSq = -1.0;
|
||||
+ Player closest = null;
|
||||
+
|
||||
+ for (int i = 0; i < players.size(); i++) {
|
||||
+ try {
|
||||
+ Player player = players.get(i);
|
||||
+ if (player == null || player.isRemoved() || !player.isAlive()) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (conditions.test(level, source, player)) {
|
||||
+ double dx = player.getX() - x;
|
||||
+ double dy = player.getY() - y;
|
||||
+ double dz = player.getZ() - z;
|
||||
+ double distSq = dx * dx + dy * dy + dz * dz;
|
||||
+
|
||||
+ if (closestDistSq == -1.0 || distSq < closestDistSq) {
|
||||
+ closestDistSq = distSq;
|
||||
+ closest = player;
|
||||
+ }
|
||||
+ }
|
||||
+ } catch (IndexOutOfBoundsException e) {
|
||||
+ break;
|
||||
+ } catch (Exception e) {
|
||||
+ System.err.println("Error processing player in findNearestPlayerSafely: " + e.getMessage());
|
||||
+ continue;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return closest;
|
||||
+ } catch (Exception e) {
|
||||
+ System.err.println("Error in findNearestPlayerSafely: " + e.getMessage());
|
||||
+ return null;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Synchronous fallback method
|
||||
+ private void findTargetSync() {
|
||||
+ try {
|
||||
+ ServerLevel serverLevel = getServerLevel(this.mob);
|
||||
+ if (serverLevel == null) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (this.targetType != Player.class && this.targetType != ServerPlayer.class) {
|
||||
+ try {
|
||||
+ this.target = serverLevel.getNearestEntity(
|
||||
+ this.mob.level().getEntitiesOfClass(this.targetType, this.getTargetSearchArea(this.getFollowDistance()), entity -> true),
|
||||
+ this.getTargetConditions(),
|
||||
+ this.mob,
|
||||
+ this.mob.getX(),
|
||||
+ this.mob.getEyeY(),
|
||||
+ this.mob.getZ()
|
||||
+ );
|
||||
+ } catch (Exception e) {
|
||||
+ System.err.println("Error in sync entity finding: " + e.getMessage());
|
||||
+ this.target = null;
|
||||
+ }
|
||||
+ } else {
|
||||
+ try {
|
||||
+ this.target = serverLevel.getNearestPlayer(this.getTargetConditions(), this.mob, this.mob.getX(), this.mob.getEyeY(), this.mob.getZ());
|
||||
+ } catch (Exception e) {
|
||||
+ System.err.println("Error in sync player finding: " + e.getMessage());
|
||||
+ this.target = null;
|
||||
+ }
|
||||
+ }
|
||||
+ } catch (Exception e) {
|
||||
+ System.err.println("Error in findTargetSync: " + e.getMessage());
|
||||
+ this.target = null;
|
||||
+ }
|
||||
+ }
|
||||
+ // Leaf end - Async Target Finding
|
||||
+
|
||||
@Override
|
||||
public void start() {
|
||||
- this.mob.setTarget(this.target, this.target instanceof ServerPlayer ? org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_PLAYER : org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_ENTITY); // CraftBukkit - reason
|
||||
+ // Leaf start - Async Target Finding
|
||||
+ LivingEntity targetEntity = this.target;
|
||||
+ if (targetEntity != null && !targetEntity.isRemoved() && targetEntity.isAlive()) {
|
||||
+ try {
|
||||
+ this.mob.setTarget(targetEntity, targetEntity instanceof ServerPlayer ?
|
||||
+ org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_PLAYER :
|
||||
+ org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_ENTITY);
|
||||
+ } catch (Exception e) {
|
||||
+ System.err.println("Error in setTarget: " + e.getMessage());
|
||||
+ this.target = null;
|
||||
+ }
|
||||
+ }
|
||||
+ // Leaf end - Async Target Finding
|
||||
super.start();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Taiyou06 <kaandindar21@gmail.com>
|
||||
Date: Mon, 14 Apr 2025 18:07:21 +0200
|
||||
Subject: [PATCH] Optimise MobEffectUtil#getDigSpeedAmplification
|
||||
|
||||
|
||||
diff --git a/net/minecraft/world/effect/MobEffectUtil.java b/net/minecraft/world/effect/MobEffectUtil.java
|
||||
index 93a07a96f74e3ba73986324b39923c6a2802f8ee..e900c91c8eb36029726f7833df1d9be4030b3ad8 100644
|
||||
--- a/net/minecraft/world/effect/MobEffectUtil.java
|
||||
+++ b/net/minecraft/world/effect/MobEffectUtil.java
|
||||
@@ -27,17 +27,21 @@ public final class MobEffectUtil {
|
||||
}
|
||||
|
||||
public static int getDigSpeedAmplification(LivingEntity entity) {
|
||||
- int i = 0;
|
||||
- int i1 = 0;
|
||||
- if (entity.hasEffect(MobEffects.HASTE)) {
|
||||
- i = entity.getEffect(MobEffects.HASTE).getAmplifier();
|
||||
+ // Leaf start - Optimise MobEffectUtil#getDigSpeedAmplification
|
||||
+ int digAmplifier = 0;
|
||||
+ int conduitAmplifier = 0;
|
||||
+ MobEffectInstance digEffect = entity.getEffect(MobEffects.HASTE);
|
||||
+ if (digEffect != null) {
|
||||
+ digAmplifier = digEffect.getAmplifier();
|
||||
}
|
||||
|
||||
- if (entity.hasEffect(MobEffects.CONDUIT_POWER)) {
|
||||
- i1 = entity.getEffect(MobEffects.CONDUIT_POWER).getAmplifier();
|
||||
+ MobEffectInstance conduitEffect = entity.getEffect(MobEffects.CONDUIT_POWER);
|
||||
+ if (conduitEffect != null) {
|
||||
+ conduitAmplifier = conduitEffect.getAmplifier();
|
||||
}
|
||||
|
||||
- return Math.max(i, i1);
|
||||
+ return Math.max(digAmplifier, conduitAmplifier);
|
||||
+ // Leaf end - Optimise MobEffectUtil#getDigSpeedAmplification
|
||||
}
|
||||
|
||||
public static boolean hasWaterBreathing(LivingEntity entity) {
|
||||
@@ -1,7 +1,7 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Taiyou06 <kaandindar21@gmail.com>
|
||||
Date: Fri, 28 Feb 2025 01:35:49 +0100
|
||||
Subject: [PATCH] Optimize chunkUnload
|
||||
Date: Mon, 14 Apr 2025 20:07:52 +0200
|
||||
Subject: [PATCH] Optimise chunkUnloads
|
||||
|
||||
|
||||
diff --git a/ca/spottedleaf/moonrise/patches/starlight/light/SWMRNibbleArray.java b/ca/spottedleaf/moonrise/patches/starlight/light/SWMRNibbleArray.java
|
||||
@@ -211,3 +211,131 @@ index 36c033b0ee63dfc273d721fb4b614733e8fdef19..dc9f1bc6dd8b1057da3416e24f15f232
|
||||
return new LevelChunkSection(this);
|
||||
}
|
||||
}
|
||||
diff --git a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
|
||||
index ccec1b847ba1a3667206cbeac4ed541a9fb028ea..5c2d29e749fdd317f231489401601c82191552bf 100644
|
||||
--- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
|
||||
@@ -493,14 +493,16 @@ public record SerializableChunkData(
|
||||
throw new IllegalArgumentException("Chunk can't be serialized: " + chunk);
|
||||
} else {
|
||||
ChunkPos pos = chunk.getPos();
|
||||
- List<SerializableChunkData.SectionData> list = new ArrayList<>(); final List<SerializableChunkData.SectionData> sectionsList = list; // Paper - starlight - OBFHELPER
|
||||
- LevelChunkSection[] sections = chunk.getSections();
|
||||
- LevelLightEngine lightEngine = level.getChunkSource().getLightEngine();
|
||||
|
||||
// Paper start - starlight
|
||||
final int minLightSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinLightSection(level);
|
||||
final int maxLightSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMaxLightSection(level);
|
||||
final int minBlockSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection(level);
|
||||
+ // Leaf start - Optimize chunkUnload
|
||||
+ // Pre-allocate with correct capacity to avoid resizing
|
||||
+ final int expectedSectionCount = maxLightSection - minLightSection + 1;
|
||||
+ List<SerializableChunkData.SectionData> list = new ArrayList<>(expectedSectionCount);
|
||||
+ // Leaf end - Optimize chunkUnload
|
||||
|
||||
final LevelChunkSection[] chunkSections = chunk.getSections();
|
||||
final ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray[] blockNibbles = ((ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk)chunk).starlight$getBlockNibbles();
|
||||
@@ -518,10 +520,20 @@ public record SerializableChunkData(
|
||||
continue;
|
||||
}
|
||||
|
||||
+ // Leaf start - Optimize chunkUnload
|
||||
+ DataLayer blockDataLayer = null;
|
||||
+ if (blockNibble != null && blockNibble.data != null) {
|
||||
+ blockDataLayer = new DataLayer(blockNibble.data);
|
||||
+ }
|
||||
+
|
||||
+ DataLayer skyDataLayer = null;
|
||||
+ if (skyNibble != null && skyNibble.data != null) {
|
||||
+ skyDataLayer = new DataLayer(skyNibble.data);
|
||||
+ }
|
||||
+ // Leaf end - Optimize chunkUnload
|
||||
+
|
||||
final SerializableChunkData.SectionData sectionData = new SerializableChunkData.SectionData(
|
||||
- lightSection, chunkSection,
|
||||
- blockNibble == null ? null : (blockNibble.data == null ? null : new DataLayer(blockNibble.data)),
|
||||
- skyNibble == null ? null : (skyNibble.data == null ? null : new DataLayer(skyNibble.data))
|
||||
+ lightSection, chunkSection, blockDataLayer, skyDataLayer // Leaf - Optimize chunkUnload
|
||||
);
|
||||
|
||||
if (blockNibble != null) {
|
||||
@@ -532,12 +544,16 @@ public record SerializableChunkData(
|
||||
((ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData)(Object)sectionData).starlight$setSkyLightState(skyNibble.state);
|
||||
}
|
||||
|
||||
- sectionsList.add(sectionData);
|
||||
+ list.add(sectionData); // Leaf - Optimize chunkUnload
|
||||
}
|
||||
// Paper end - starlight
|
||||
|
||||
- List<CompoundTag> list1 = new ArrayList<>(chunk.getBlockEntitiesPos().size());
|
||||
+ // Leaf start - Optimize chunkUnload
|
||||
+ // Pre-allocate block entities list with exact size needed
|
||||
+ final int blockEntityCount = chunk.getBlockEntitiesPos().size();
|
||||
+ List<CompoundTag> list1 = blockEntityCount > 0 ? new ArrayList<>(blockEntityCount) : java.util.Collections.emptyList();
|
||||
|
||||
+ if (blockEntityCount > 0) // Leaf - Optimize chunkUnload
|
||||
for (BlockPos blockPos : chunk.getBlockEntitiesPos()) {
|
||||
CompoundTag blockEntityNbtForSaving = chunk.getBlockEntityNbtForSaving(blockPos, level.registryAccess());
|
||||
if (blockEntityNbtForSaving != null) {
|
||||
@@ -545,15 +561,27 @@ public record SerializableChunkData(
|
||||
}
|
||||
}
|
||||
|
||||
- List<CompoundTag> list2 = new ArrayList<>();
|
||||
+ // Leaf start - Optimize chunkUnload
|
||||
+ // For entities, use an initial estimated capacity if it's a ProtoChunk
|
||||
+ List<CompoundTag> list2;
|
||||
long[] longs = null;
|
||||
if (chunk.getPersistedStatus().getChunkType() == ChunkType.PROTOCHUNK) {
|
||||
ProtoChunk protoChunk = (ProtoChunk)chunk;
|
||||
- list2.addAll(protoChunk.getEntities());
|
||||
+ // Leaf start - Optimize chunkUnload
|
||||
+ int entitySize = protoChunk.getEntities().size();
|
||||
+ if (entitySize > 0) {
|
||||
+ list2 = new ArrayList<>(Math.max(16, entitySize));
|
||||
+ list2.addAll(protoChunk.getEntities());
|
||||
+ } else {
|
||||
+ list2 = java.util.Collections.emptyList();
|
||||
+ }
|
||||
+ // Leaf end - Optimize chunkUnload
|
||||
CarvingMask carvingMask = protoChunk.getCarvingMask();
|
||||
if (carvingMask != null) {
|
||||
longs = carvingMask.toArray();
|
||||
}
|
||||
+ } else {
|
||||
+ list2 = java.util.Collections.emptyList(); // Leaf - Optimize chunkUnload
|
||||
}
|
||||
|
||||
Map<Heightmap.Types, long[]> map = new EnumMap<>(Heightmap.Types.class);
|
||||
@@ -561,14 +589,26 @@ public record SerializableChunkData(
|
||||
for (Entry<Heightmap.Types, Heightmap> entry : chunk.getHeightmaps()) {
|
||||
if (chunk.getPersistedStatus().heightmapsAfter().contains(entry.getKey())) {
|
||||
long[] rawData = entry.getValue().getRawData();
|
||||
- map.put(entry.getKey(), (long[])rawData.clone());
|
||||
+ map.put(entry.getKey(), Arrays.copyOf(rawData, rawData.length)); // Leaf - Optimize chunkUnload
|
||||
}
|
||||
}
|
||||
|
||||
ChunkAccess.PackedTicks ticksForSerialization = chunk.getTicksForSerialization(level.getGameTime());
|
||||
- ShortList[] lists = Arrays.stream(chunk.getPostProcessing())
|
||||
- .map(list3 -> list3 != null ? new ShortArrayList(list3) : null)
|
||||
- .toArray(ShortList[]::new);
|
||||
+ // Leaf start - Optimize chunkUnload - remove stream
|
||||
+ ShortList[] postProcessing = chunk.getPostProcessing();
|
||||
+ ShortList[] lists = new ShortList[postProcessing.length];
|
||||
+ for (int i = 0; i < postProcessing.length; i++) {
|
||||
+ ShortList source = postProcessing[i];
|
||||
+ // Only create a new list if there's actual data to copy
|
||||
+ if (source != null) {
|
||||
+ int size = source.size();
|
||||
+ if (size > 0) {
|
||||
+ lists[i] = new ShortArrayList(size);
|
||||
+ lists[i].addAll(source);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ // Leaf end - Optimize chunkUnload - remove stream
|
||||
CompoundTag compoundTag = packStructureData(
|
||||
StructurePieceSerializationContext.fromLevel(level), pos, chunk.getAllStarts(), chunk.getAllReferences()
|
||||
);
|
||||
@@ -0,0 +1,46 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: hayanesuru <mc@jvavav.com>
|
||||
Date: Sat, 19 Apr 2025 22:12:19 +0800
|
||||
Subject: [PATCH] Optimize BlockEntityType#isValid
|
||||
|
||||
|
||||
diff --git a/net/minecraft/world/level/block/entity/BlockEntityType.java b/net/minecraft/world/level/block/entity/BlockEntityType.java
|
||||
index 386e6a48701b4c9256e33174123381a93d61e292..2bc620ceb8368e9a74b831de698de94ac9c47c3b 100644
|
||||
--- a/net/minecraft/world/level/block/entity/BlockEntityType.java
|
||||
+++ b/net/minecraft/world/level/block/entity/BlockEntityType.java
|
||||
@@ -256,6 +256,14 @@ public class BlockEntityType<T extends BlockEntity> {
|
||||
private BlockEntityType(BlockEntityType.BlockEntitySupplier<? extends T> factory, Set<Block> validBlocks) {
|
||||
this.factory = factory;
|
||||
this.validBlocks = validBlocks;
|
||||
+ // Leaf start - Optimize BlockEntityType#isValid
|
||||
+ for (Block validBlock : validBlocks) {
|
||||
+ if (validBlock.blockEntityType != null) {
|
||||
+ throw new IllegalStateException("Duplicate block entity type");
|
||||
+ }
|
||||
+ validBlock.blockEntityType = this;
|
||||
+ }
|
||||
+ // Leaf end - Optimize BlockEntityType#isValid
|
||||
}
|
||||
|
||||
public T create(BlockPos pos, BlockState state) {
|
||||
@@ -263,7 +271,7 @@ public class BlockEntityType<T extends BlockEntity> {
|
||||
}
|
||||
|
||||
public boolean isValid(BlockState state) {
|
||||
- return this.validBlocks.contains(state.getBlock());
|
||||
+ return state.getBlock().blockEntityType == this; // Leaf - Optimize BlockEntityType#isValid - remove hash lookup
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
diff --git a/net/minecraft/world/level/block/state/BlockBehaviour.java b/net/minecraft/world/level/block/state/BlockBehaviour.java
|
||||
index 3219b9caa654c7a64e5e4a92178528e1104b34c7..6d522e9485fadd6fc0f350cb30ba5224aa046d4f 100644
|
||||
--- a/net/minecraft/world/level/block/state/BlockBehaviour.java
|
||||
+++ b/net/minecraft/world/level/block/state/BlockBehaviour.java
|
||||
@@ -101,6 +101,7 @@ public abstract class BlockBehaviour implements FeatureElement {
|
||||
protected final BlockBehaviour.Properties properties;
|
||||
protected final Optional<ResourceKey<LootTable>> drops;
|
||||
protected final String descriptionId;
|
||||
+ @Nullable public net.minecraft.world.level.block.entity.BlockEntityType blockEntityType = null; // Leaf - Optimize BlockEntityType#isValid
|
||||
|
||||
public BlockBehaviour(BlockBehaviour.Properties properties) {
|
||||
this.hasCollision = properties.hasCollision;
|
||||
@@ -0,0 +1,46 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
|
||||
Date: Fri, 13 Sep 2024 14:32:32 -0700
|
||||
Subject: [PATCH] PaperPR: Add ticket on player join to avoid chunk
|
||||
load-unload-load cycle
|
||||
|
||||
Original license: GPLv3
|
||||
Original project: https://github.com/PaperMC/Paper
|
||||
Paper pull request: https://github.com/PaperMC/Paper/pull/11398
|
||||
|
||||
Adding the entity will add and then immediately remove an entity load ticket, which would result in the chunk loading and then unloading before being loaded again once the player chunk loader reacts (delay can vary based on rate limit configs)
|
||||
By adding a ticket with a short removal delay we attempt to keep the chunk loaded until the player chunk loader reacts, but this is not a guarantee due to the aforementioned rate limit configs. Plugins should still handle load/unload events as normal, however this will reduce redundant calls.
|
||||
The delay is currently set to 2 seconds, however, we may want to adjust this before merging (for example the player chunk unload delay is 5 seconds)
|
||||
|
||||
fixes Paper#9581
|
||||
|
||||
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
|
||||
index 67275e803e0287306b163f4eec17388b9c701a8c..4344e5d9eda940849352e08734f95de2036bd87a 100644
|
||||
--- a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
|
||||
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
|
||||
@@ -48,6 +48,7 @@ public final class RegionizedPlayerChunkLoader {
|
||||
|
||||
public static final TicketType PLAYER_TICKET = ChunkSystemTicketType.create("chunk_system:player_ticket", Long::compareTo);
|
||||
public static final TicketType PLAYER_TICKET_DELAYED = ChunkSystemTicketType.create("chunk_system:player_ticket_delayed", Long::compareTo, 5L * 20L);
|
||||
+ public static final TicketType PLAYER_JOIN = ChunkSystemTicketType.create("chunk_system:player_join", (a, b) -> 0, 5 * 20); // Paper - Add ticket on player join to avoid chunk load-unload-load cycle
|
||||
|
||||
public static final int GENERATED_TICKET_LEVEL = ChunkHolderManager.FULL_LOADED_TICKET_LEVEL;
|
||||
public static final int LOADED_TICKET_LEVEL = ChunkTaskScheduler.getTicketLevel(ChunkStatus.EMPTY);
|
||||
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
|
||||
index 07dafa0e12fd51e4b9e968e22b20f000d04f6dbc..8173242d149709f092e6d609f6e1d831eca0a884 100644
|
||||
--- a/net/minecraft/server/players/PlayerList.java
|
||||
+++ b/net/minecraft/server/players/PlayerList.java
|
||||
@@ -330,6 +330,13 @@ public abstract class PlayerList {
|
||||
// this.broadcastAll(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(List.of(player))); // CraftBukkit - replaced with loop below
|
||||
// Paper start - Fire PlayerJoinEvent when Player is actually ready; correctly register player BEFORE PlayerJoinEvent, so the entity is valid and doesn't require tick delay hacks
|
||||
player.supressTrackerForLogin = true;
|
||||
+ // Paper start - Add ticket on player join to avoid chunk load-unload-load cycle
|
||||
+ serverLevel.moonrise$getChunkTaskScheduler().chunkHolderManager.addTicketAtLevel(
|
||||
+ ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.PLAYER_JOIN,
|
||||
+ player.chunkPosition(),
|
||||
+ ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.TICK_TICKET_LEVEL,
|
||||
+ net.minecraft.util.Unit.INSTANCE);
|
||||
+ // Paper end - Add ticket on player join to avoid chunk load-unload-load cycle
|
||||
serverLevel.addNewPlayer(player);
|
||||
this.server.getCustomBossEvents().onPlayerConnect(player); // see commented out section below serverLevel.addPlayerJoin(player);
|
||||
// Paper end - Fire PlayerJoinEvent when Player is actually ready
|
||||
@@ -0,0 +1,199 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com>
|
||||
Date: Thu, 24 Apr 2025 16:36:16 -0400
|
||||
Subject: [PATCH] paw optimization
|
||||
|
||||
Some random optimizations
|
||||
|
||||
- Remove Paper's dead code
|
||||
- Only set shuffle random seed if is really used
|
||||
- Secret patches (WIP)
|
||||
|
||||
diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java
|
||||
index f3e9de8716f5e1a72ec465ee897c8f0413f7b1c3..f998cf8d70302a21289de4d84b46d322d0b8a8fe 100644
|
||||
--- a/net/minecraft/network/Connection.java
|
||||
+++ b/net/minecraft/network/Connection.java
|
||||
@@ -617,13 +617,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
if (!(this.packetListener instanceof net.minecraft.server.network.ServerLoginPacketListenerImpl loginPacketListener)
|
||||
|| loginPacketListener.state != net.minecraft.server.network.ServerLoginPacketListenerImpl.State.VERIFYING
|
||||
|| Connection.joinAttemptsThisTick++ < MAX_PER_TICK) {
|
||||
- // Paper start - detailed watchdog information
|
||||
- net.minecraft.network.protocol.PacketUtils.packetProcessing.push(this.packetListener);
|
||||
- try {
|
||||
tickablePacketListener.tick();
|
||||
- } finally {
|
||||
- net.minecraft.network.protocol.PacketUtils.packetProcessing.pop();
|
||||
- } // Paper end - detailed watchdog information
|
||||
} // Paper end - Buffer joins to world
|
||||
}
|
||||
|
||||
diff --git a/net/minecraft/network/protocol/PacketUtils.java b/net/minecraft/network/protocol/PacketUtils.java
|
||||
index 4535858701b2bb232b9d2feb2af6551526232ddc..e65c62dbe4c1560ae153e4c4344e9194c783a2f4 100644
|
||||
--- a/net/minecraft/network/protocol/PacketUtils.java
|
||||
+++ b/net/minecraft/network/protocol/PacketUtils.java
|
||||
@@ -21,8 +21,6 @@ public class PacketUtils {
|
||||
public static <T extends PacketListener> void ensureRunningOnSameThread(Packet<T> packet, T processor, BlockableEventLoop<?> executor) throws RunningOnDifferentThreadException {
|
||||
if (!executor.isSameThread()) {
|
||||
executor.executeIfPossible(() -> {
|
||||
- packetProcessing.push(processor); // Paper - detailed watchdog information
|
||||
- try { // Paper - detailed watchdog information
|
||||
if (processor instanceof net.minecraft.server.network.ServerCommonPacketListenerImpl serverCommonPacketListener && serverCommonPacketListener.processedDisconnect) return; // Paper - Don't handle sync packets for kicked players
|
||||
if (processor.shouldHandleMessage(packet)) {
|
||||
try {
|
||||
@@ -37,12 +35,6 @@ public class PacketUtils {
|
||||
} else {
|
||||
LOGGER.debug("Ignoring packet due to disconnection: {}", packet);
|
||||
}
|
||||
- // Paper start - detailed watchdog information
|
||||
- } finally {
|
||||
- totalMainThreadPacketsProcessed.getAndIncrement();
|
||||
- packetProcessing.pop();
|
||||
- }
|
||||
- // Paper end - detailed watchdog information
|
||||
});
|
||||
throw RunningOnDifferentThreadException.RUNNING_ON_DIFFERENT_THREAD;
|
||||
}
|
||||
@@ -69,22 +61,4 @@ public class PacketUtils {
|
||||
|
||||
packetListener.fillCrashReport(crashReport);
|
||||
}
|
||||
-
|
||||
- // Paper start - detailed watchdog information
|
||||
- public static final java.util.concurrent.ConcurrentLinkedDeque<PacketListener> packetProcessing = new java.util.concurrent.ConcurrentLinkedDeque<>();
|
||||
- static final java.util.concurrent.atomic.AtomicLong totalMainThreadPacketsProcessed = new java.util.concurrent.atomic.AtomicLong();
|
||||
-
|
||||
- public static long getTotalProcessedPackets() {
|
||||
- return totalMainThreadPacketsProcessed.get();
|
||||
- }
|
||||
-
|
||||
- public static java.util.List<PacketListener> getCurrentPacketProcessors() {
|
||||
- java.util.List<PacketListener> listeners = new java.util.ArrayList<>(4);
|
||||
- for (PacketListener listener : packetProcessing) {
|
||||
- listeners.add(listener);
|
||||
- }
|
||||
-
|
||||
- return listeners;
|
||||
- }
|
||||
- // Paper end - detailed watchdog information
|
||||
}
|
||||
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
|
||||
index f89fb321e50338e7765476cb5d7bdf2f02a497b3..83fcef85ed2a5410275e4419e4356994016f21d1 100644
|
||||
--- a/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -622,8 +622,10 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
||||
try {
|
||||
this.chunkMap.collectSpawningChunks(list);
|
||||
// Paper start - chunk tick iteration optimisation
|
||||
- this.shuffleRandom.setSeed(this.level.random.nextLong());
|
||||
- if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) Util.shuffle(list, this.shuffleRandom); // Paper - Optional per player mob spawns; do not need this when per-player is enabled
|
||||
+ if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) {
|
||||
+ this.shuffleRandom.setSeed(this.level.random.nextLong()); // Leaf - paw optimization - Only set seed if is really used
|
||||
+ Util.shuffle(list, this.shuffleRandom); // Paper - Optional per player mob spawns; do not need this when per-player is enabled
|
||||
+ }
|
||||
// Paper end - chunk tick iteration optimisation
|
||||
|
||||
for (LevelChunk levelChunk : list) {
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index 8ade900d016026cde482ccbca7a411993d9eadd9..414a70c352263a1cd2bfb938053990ea2be2b2c3 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -1367,13 +1367,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
// Paper end - log detailed entity tick information
|
||||
|
||||
public void tickNonPassenger(Entity entity) {
|
||||
- // Paper start - log detailed entity tick information
|
||||
ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread("Cannot tick an entity off-main");
|
||||
- try {
|
||||
- if (currentlyTickingEntity.get() == null) {
|
||||
- currentlyTickingEntity.lazySet(entity);
|
||||
- }
|
||||
- // Paper end - log detailed entity tick information
|
||||
entity.setOldPosAndRot();
|
||||
entity.tickCount++;
|
||||
entity.totalEntityAge++; // Paper - age-like counter for all entities
|
||||
@@ -1386,13 +1380,6 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
for (Entity entity1 : entity.getPassengers()) {
|
||||
this.tickPassenger(entity, entity1, isActive); // Paper - EAR 2
|
||||
}
|
||||
- // Paper start - log detailed entity tick information
|
||||
- } finally {
|
||||
- if (currentlyTickingEntity.get() == entity) {
|
||||
- currentlyTickingEntity.lazySet(null);
|
||||
- }
|
||||
- }
|
||||
- // Paper end - log detailed entity tick information
|
||||
}
|
||||
|
||||
private void tickPassenger(Entity ridingEntity, Entity passengerEntity, final boolean isActive) { // Paper - EAR 2
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index 960ae487a5a88a5fe9899c16b9553cea0fbfba37..1ff8522b81eb4bc6297b44a1f2a48c8021104185 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -1138,16 +1138,6 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
return this.onGround;
|
||||
}
|
||||
|
||||
- // Paper start - detailed watchdog information
|
||||
- public final Object posLock = new Object(); // Paper - log detailed entity tick information
|
||||
-
|
||||
- @Nullable
|
||||
- private Vec3 moveVector;
|
||||
- private double moveStartX;
|
||||
- private double moveStartY;
|
||||
- private double moveStartZ;
|
||||
- // Paper end - detailed watchdog information
|
||||
-
|
||||
public void move(MoverType type, Vec3 movement) {
|
||||
// Gale start - VMP - skip entity move if movement is zero
|
||||
if (!this.boundingBoxChanged && movement.equals(Vec3.ZERO)) {
|
||||
@@ -1155,16 +1145,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
}
|
||||
// Gale end - VMP - skip entity move if movement is zero
|
||||
final Vec3 originalMovement = movement; // Paper - Expose pre-collision velocity
|
||||
- // Paper start - detailed watchdog information
|
||||
ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread("Cannot move an entity off-main");
|
||||
- synchronized (this.posLock) {
|
||||
- this.moveStartX = this.getX();
|
||||
- this.moveStartY = this.getY();
|
||||
- this.moveStartZ = this.getZ();
|
||||
- this.moveVector = movement;
|
||||
- }
|
||||
- try {
|
||||
- // Paper end - detailed watchdog information
|
||||
if (this.noPhysics) {
|
||||
this.setPos(this.getX() + movement.x, this.getY() + movement.y, this.getZ() + movement.z);
|
||||
} else {
|
||||
@@ -1298,13 +1279,6 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
// Gale end - skip negligible planar movement multiplication
|
||||
}
|
||||
}
|
||||
- // Paper start - detailed watchdog information
|
||||
- } finally {
|
||||
- synchronized (this.posLock) { // Paper
|
||||
- this.moveVector = null;
|
||||
- } // Paper
|
||||
- }
|
||||
- // Paper end - detailed watchdog information
|
||||
}
|
||||
|
||||
private void applyMovementEmissionAndPlaySound(Entity.MovementEmission movementEmission, Vec3 movement, BlockPos pos, BlockState state) {
|
||||
@@ -4771,9 +4745,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
}
|
||||
|
||||
public void setDeltaMovement(Vec3 deltaMovement) {
|
||||
- synchronized (this.posLock) { // Paper - detailed watchdog information
|
||||
this.deltaMovement = deltaMovement;
|
||||
- } // Paper - detailed watchdog information
|
||||
}
|
||||
|
||||
public void addDeltaMovement(Vec3 addend) {
|
||||
@@ -4881,9 +4853,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
}
|
||||
// Paper end - Fix MC-4
|
||||
if (this.position.x != x || this.position.y != y || this.position.z != z) {
|
||||
- synchronized (this.posLock) { // Paper - detailed watchdog information
|
||||
this.position = new Vec3(x, y, z);
|
||||
- } // Paper - detailed watchdog information
|
||||
int floor = Mth.floor(x);
|
||||
int floor1 = Mth.floor(y);
|
||||
int floor2 = Mth.floor(z);
|
||||
@@ -0,0 +1,81 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Samsuik <kfian294ma4@gmail.com>
|
||||
Date: Thu, 1 May 2025 22:28:50 +0200
|
||||
Subject: [PATCH] Sakura: copy-EntityList-implementation-to-BasicEntityList
|
||||
|
||||
|
||||
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java
|
||||
index 7b686d834e4eb36be5758b0e0a846a70d1e2294b..37930d1b87378ac3e8c7f5ebd79148bb66771f48 100644
|
||||
--- a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java
|
||||
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java
|
||||
@@ -382,6 +382,13 @@ public final class ChunkEntitySlices {
|
||||
|
||||
private E[] storage;
|
||||
private int size;
|
||||
+ // Sakura start - use methods from EntityList
|
||||
+ private it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap entityToIndex = null;
|
||||
+ private void setupIndexMap() {
|
||||
+ this.entityToIndex = new it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap(2, 0.8f);
|
||||
+ this.entityToIndex.defaultReturnValue(Integer.MIN_VALUE);
|
||||
+ }
|
||||
+ // Sakura end - use methods from EntityList
|
||||
|
||||
public BasicEntityList() {
|
||||
this(0);
|
||||
@@ -402,6 +409,7 @@ public final class ChunkEntitySlices {
|
||||
private void resize() {
|
||||
if (this.storage == me.titaniumtown.ArrayConstants.emptyEntityArray) { // Gale - JettPack - reduce array allocations
|
||||
this.storage = (E[])new Entity[DEFAULT_CAPACITY];
|
||||
+ this.setupIndexMap(); // Sakura - use methods from EntityList
|
||||
} else {
|
||||
this.storage = Arrays.copyOf(this.storage, this.storage.length * 2);
|
||||
}
|
||||
@@ -415,6 +423,7 @@ public final class ChunkEntitySlices {
|
||||
} else {
|
||||
this.storage[idx] = entity;
|
||||
}
|
||||
+ this.entityToIndex.put(entity.getId(), idx); // Sakura - use methods from EntityList
|
||||
}
|
||||
|
||||
public int indexOf(final E entity) {
|
||||
@@ -430,24 +439,32 @@ public final class ChunkEntitySlices {
|
||||
}
|
||||
|
||||
public boolean remove(final E entity) {
|
||||
- final int idx = this.indexOf(entity);
|
||||
- if (idx == -1) {
|
||||
+ // Sakura start - use methods from EntityList
|
||||
+ if (this.entityToIndex == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
- final int size = --this.size;
|
||||
- final E[] storage = this.storage;
|
||||
- if (idx != size) {
|
||||
- System.arraycopy(storage, idx + 1, storage, idx, size - idx);
|
||||
+ final int index = this.entityToIndex.remove(entity.getId());
|
||||
+ if (index == Integer.MIN_VALUE) {
|
||||
+ return false;
|
||||
}
|
||||
|
||||
- storage[size] = null;
|
||||
+ // move the entity at the end to this index
|
||||
+ final int endIndex = --this.size;
|
||||
+ final E end = this.storage[endIndex];
|
||||
+ if (index != endIndex) {
|
||||
+ // not empty after this call
|
||||
+ this.entityToIndex.put(end.getId(), index); // update index
|
||||
+ }
|
||||
+ this.storage[index] = end;
|
||||
+ this.storage[endIndex] = null;
|
||||
+ // Sakura end - use methods from EntityList
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean has(final E entity) {
|
||||
- return this.indexOf(entity) != -1;
|
||||
+ return this.entityToIndex != null && this.entityToIndex.containsKey(entity.getId()); // Sakura - use methods from EntityList
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ and, in my opinion, worth the low risk of minor mob-spawning-related
|
||||
inconsistencies.
|
||||
|
||||
diff --git a/src/main/java/ca/spottedleaf/moonrise/common/list/IteratorSafeOrderedReferenceSet.java b/src/main/java/ca/spottedleaf/moonrise/common/list/IteratorSafeOrderedReferenceSet.java
|
||||
index ece6db7b9a0dfd535141c0c756947c4898140503..82738ad42853e328532d854e3369e6a475ad729d 100644
|
||||
index ece6db7b9a0dfd535141c0c756947c4898140503..13df153d7b63a5c36a2fd14d3292e7eb32a394df 100644
|
||||
--- a/src/main/java/ca/spottedleaf/moonrise/common/list/IteratorSafeOrderedReferenceSet.java
|
||||
+++ b/src/main/java/ca/spottedleaf/moonrise/common/list/IteratorSafeOrderedReferenceSet.java
|
||||
@@ -11,7 +11,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
|
||||
@@ -31,7 +31,7 @@ index ece6db7b9a0dfd535141c0c756947c4898140503..82738ad42853e328532d854e3369e6a4
|
||||
|
||||
private final Reference2IntLinkedOpenHashMap<E> indexMap;
|
||||
- private int firstInvalidIndex = -1;
|
||||
+ private volatile int firstInvalidIndex = -1; // Leaf - Async mob spawning - volatile
|
||||
+ private final java.util.concurrent.atomic.AtomicInteger firstInvalidIndex = new java.util.concurrent.atomic.AtomicInteger(-1); // Leaf - Pufferfish - Async mob spawning - atomic
|
||||
|
||||
/* list impl */
|
||||
private E[] listElements;
|
||||
@@ -44,7 +44,7 @@ index ece6db7b9a0dfd535141c0c756947c4898140503..82738ad42853e328532d854e3369e6a4
|
||||
|
||||
public IteratorSafeOrderedReferenceSet() {
|
||||
this(Object.class);
|
||||
@@ -99,7 +99,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
|
||||
@@ -99,11 +99,11 @@ public final class IteratorSafeOrderedReferenceSet<E> {
|
||||
}
|
||||
|
||||
public int createRawIterator() {
|
||||
@@ -53,6 +53,11 @@ index ece6db7b9a0dfd535141c0c756947c4898140503..82738ad42853e328532d854e3369e6a4
|
||||
if (this.indexMap.isEmpty()) {
|
||||
return Integer.MAX_VALUE;
|
||||
} else {
|
||||
- return this.firstInvalidIndex == 0 ? this.indexMap.getInt(this.indexMap.firstKey()) : 0;
|
||||
+ return this.firstInvalidIndex.get() == 0 ? this.indexMap.getInt(this.indexMap.firstKey()) : 0; // Leaf - Pufferfish - Async mob spawning
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
|
||||
}
|
||||
|
||||
@@ -62,7 +67,19 @@ index ece6db7b9a0dfd535141c0c756947c4898140503..82738ad42853e328532d854e3369e6a4
|
||||
if (this.getFragFactor() >= this.maxFragFactor) {
|
||||
this.defrag();
|
||||
}
|
||||
@@ -137,7 +137,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
|
||||
@@ -130,14 +130,17 @@ public final class IteratorSafeOrderedReferenceSet<E> {
|
||||
public boolean remove(final E element) {
|
||||
final int index = this.indexMap.removeInt(element);
|
||||
if (index >= 0) {
|
||||
- if (this.firstInvalidIndex < 0 || index < this.firstInvalidIndex) {
|
||||
- this.firstInvalidIndex = index;
|
||||
+ // Leaf start - Pufferfish - Async mob spawning
|
||||
+ int firstInvalidIndex = this.firstInvalidIndex.get();
|
||||
+ if (firstInvalidIndex < 0 || index < firstInvalidIndex) {
|
||||
+ this.firstInvalidIndex.set(index);
|
||||
}
|
||||
+ // Leaf end - Pufferfish - Async mob spawning
|
||||
if (this.listElements[index] != element) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
this.listElements[index] = null;
|
||||
@@ -71,7 +88,49 @@ index ece6db7b9a0dfd535141c0c756947c4898140503..82738ad42853e328532d854e3369e6a4
|
||||
this.defrag();
|
||||
}
|
||||
//this.check();
|
||||
@@ -235,7 +235,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
|
||||
@@ -169,14 +172,16 @@ public final class IteratorSafeOrderedReferenceSet<E> {
|
||||
}
|
||||
|
||||
private void defrag() {
|
||||
- if (this.firstInvalidIndex < 0) {
|
||||
+ // Leaf start - Pufferfish - Async mob spawning
|
||||
+ int firstInvalidIndex = this.firstInvalidIndex.get();
|
||||
+ if (firstInvalidIndex < 0) {
|
||||
return; // nothing to do
|
||||
}
|
||||
|
||||
if (this.indexMap.isEmpty()) {
|
||||
Arrays.fill(this.listElements, 0, this.listSize, null);
|
||||
this.listSize = 0;
|
||||
- this.firstInvalidIndex = -1;
|
||||
+ this.firstInvalidIndex.set(-1); // Leaf - Pufferfish - Async mob spawning
|
||||
//this.check();
|
||||
return;
|
||||
}
|
||||
@@ -186,11 +191,11 @@ public final class IteratorSafeOrderedReferenceSet<E> {
|
||||
int lastValidIndex;
|
||||
java.util.Iterator<Reference2IntMap.Entry<E>> iterator;
|
||||
|
||||
- if (this.firstInvalidIndex == 0) {
|
||||
+ if (firstInvalidIndex == 0) { // Leaf - Pufferfish - Async mob spawning
|
||||
iterator = this.indexMap.reference2IntEntrySet().fastIterator();
|
||||
lastValidIndex = 0;
|
||||
} else {
|
||||
- lastValidIndex = this.firstInvalidIndex;
|
||||
+ lastValidIndex = firstInvalidIndex; // Leaf - Pufferfish - Async mob spawning
|
||||
final E key = backingArray[lastValidIndex - 1];
|
||||
iterator = this.indexMap.reference2IntEntrySet().fastIterator(new Reference2IntMap.Entry<E>() {
|
||||
@Override
|
||||
@@ -221,7 +226,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
|
||||
// cleanup end
|
||||
Arrays.fill(backingArray, lastValidIndex, this.listSize, null);
|
||||
this.listSize = lastValidIndex;
|
||||
- this.firstInvalidIndex = -1;
|
||||
+ this.firstInvalidIndex.set(-1); // Leaf - Pufferfish - Async mob spawning
|
||||
//this.check();
|
||||
}
|
||||
|
||||
@@ -235,7 +240,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
|
||||
}
|
||||
|
||||
public IteratorSafeOrderedReferenceSet.Iterator<E> iterator(final int flags) {
|
||||
@@ -80,3 +139,12 @@ index ece6db7b9a0dfd535141c0c756947c4898140503..82738ad42853e328532d854e3369e6a4
|
||||
return new BaseIterator<>(this, true, (flags & ITERATOR_FLAG_SEE_ADDITIONS) != 0 ? Integer.MAX_VALUE : this.listSize);
|
||||
}
|
||||
|
||||
@@ -322,7 +327,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
|
||||
}
|
||||
this.lastReturned = null;
|
||||
this.finished = true;
|
||||
- this.set.finishRawIterator();
|
||||
+ this.set.finishRawIterator(); // Pufferfish - async mob spawning - diff on change
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,23 +5,56 @@ Subject: [PATCH] PlayerInventoryOverflowEvent
|
||||
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java
|
||||
index 19180c08f41db939c1a9f0caeb62e5beb1117f69..c765118189cbf8db7291c1d50214a7f31acc3a49 100644
|
||||
index 19180c08f41db939c1a9f0caeb62e5beb1117f69..9b5c8c4c39657c18e67ad654bd0e5a30c09232c6 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java
|
||||
@@ -340,6 +340,16 @@ public class CraftInventory implements Inventory {
|
||||
@@ -340,9 +340,49 @@ public class CraftInventory implements Inventory {
|
||||
}
|
||||
}
|
||||
}
|
||||
+
|
||||
+ // Leaf start - PlayerInventoryOverflowEvent
|
||||
+ if (org.dreeam.leaf.event.player.PlayerInventoryOverflowEvent.getHandlerList().getRegisteredListeners().length > 0
|
||||
+ if (isListeningInventoryOverflowEvent()
|
||||
+ && !leftover.isEmpty() && this.inventory instanceof net.minecraft.world.entity.player.Inventory && this.inventory.getOwner() instanceof org.bukkit.entity.Player player) {
|
||||
+ new org.dreeam.leaf.event.player.PlayerInventoryOverflowEvent(player, leftover).callEvent();
|
||||
+
|
||||
+ leftover = new HashMap<>();
|
||||
+ return new HashMap<>();
|
||||
+ }
|
||||
+ // Leaf end - PlayerInventoryOverflowEvent
|
||||
+
|
||||
return leftover;
|
||||
}
|
||||
|
||||
+ // Leaf start - PlayerInventoryOverflowEvent
|
||||
+ private static boolean isListeningInventoryOverflowEvent() {
|
||||
+ if (org.dreeam.leaf.event.player.PlayerInventoryOverflowEvent.isListeningInvOverflowCached == -1) {
|
||||
+ org.dreeam.leaf.event.player.PlayerInventoryOverflowEvent.isListeningInvOverflowCached = 0;
|
||||
+
|
||||
+ if (!org.dreeam.leaf.config.modules.gameplay.ConfigurableInventoryOverflowEvent.enabled) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ org.bukkit.plugin.RegisteredListener[] listeners = org.dreeam.leaf.event.player.PlayerInventoryOverflowEvent.getHandlerList().getRegisteredListeners();
|
||||
+ if (listeners.length == 1) {
|
||||
+ if (listeners[0].getListener().getClass().getName().equals(org.dreeam.leaf.config.modules.gameplay.ConfigurableInventoryOverflowEvent.listenerClass)) {
|
||||
+ org.dreeam.leaf.event.player.PlayerInventoryOverflowEvent.isListeningInvOverflowCached = 1;
|
||||
+ return true;
|
||||
+ }
|
||||
+ } else if (listeners.length > 1) {
|
||||
+ for (org.bukkit.plugin.RegisteredListener registeredListener : listeners) {
|
||||
+ if (registeredListener.getListener().getClass().getName().equals(org.dreeam.leaf.config.modules.gameplay.ConfigurableInventoryOverflowEvent.listenerClass)) {
|
||||
+ org.dreeam.leaf.event.player.PlayerInventoryOverflowEvent.isListeningInvOverflowCached = 1;
|
||||
+ return true;
|
||||
+ }
|
||||
+ ;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return org.dreeam.leaf.event.player.PlayerInventoryOverflowEvent.isListeningInvOverflowCached == 1;
|
||||
+ }
|
||||
+ // Leaf end - PlayerInventoryOverflowEvent
|
||||
+
|
||||
@Override
|
||||
public HashMap<Integer, ItemStack> removeItem(ItemStack... items) {
|
||||
// Paper start
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
package org.dreeam.leaf.async.ai;
|
||||
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dreeam.leaf.config.modules.async.AsyncTargetFinding;
|
||||
import org.dreeam.leaf.util.queue.SpscIntQueue;
|
||||
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
public class AsyncGoalExecutor {
|
||||
|
||||
public static final Logger LOGGER = LogManager.getLogger("Leaf Async Goal");
|
||||
|
||||
protected final SpscIntQueue queue;
|
||||
protected final SpscIntQueue wake;
|
||||
private final AsyncGoalThread thread;
|
||||
private final ServerLevel serverLevel;
|
||||
private boolean dirty = false;
|
||||
private long tickCount = 0L;
|
||||
private static final int SPIN_LIMIT = 100;
|
||||
|
||||
public AsyncGoalExecutor(AsyncGoalThread thread, ServerLevel serverLevel) {
|
||||
this.serverLevel = serverLevel;
|
||||
this.queue = new SpscIntQueue(AsyncTargetFinding.queueSize);
|
||||
this.wake = new SpscIntQueue(AsyncTargetFinding.queueSize);
|
||||
this.thread = thread;
|
||||
}
|
||||
|
||||
boolean wake(int id) {
|
||||
Entity entity = this.serverLevel.getEntities().get(id);
|
||||
if (entity == null || entity.isRemoved() || !(entity instanceof Mob mob)) {
|
||||
return false;
|
||||
}
|
||||
mob.goalSelector.wake();
|
||||
mob.targetSelector.wake();
|
||||
return true;
|
||||
}
|
||||
|
||||
public final void submit(int entityId) {
|
||||
if (!this.queue.send(entityId)) {
|
||||
int spinCount = 0;
|
||||
while (!this.queue.send(entityId)) {
|
||||
spinCount++;
|
||||
// Unpark the thread after some spinning to help clear the queue
|
||||
if (spinCount > SPIN_LIMIT) {
|
||||
unpark();
|
||||
spinCount = 0;
|
||||
}
|
||||
Thread.onSpinWait();
|
||||
}
|
||||
}
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
public final void unpark() {
|
||||
if (dirty) LockSupport.unpark(thread);
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
public final void midTick() {
|
||||
boolean didWork = false;
|
||||
while (true) {
|
||||
int id = this.wake.recv();
|
||||
if (id == Integer.MAX_VALUE) {
|
||||
break;
|
||||
}
|
||||
didWork = true;
|
||||
Entity entity = this.serverLevel.getEntities().get(id);
|
||||
if (entity == null || !entity.isAlive() || !(entity instanceof Mob mob)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mob.tickingTarget = true;
|
||||
boolean a = mob.targetSelector.poll();
|
||||
mob.tickingTarget = false;
|
||||
boolean b = mob.goalSelector.poll();
|
||||
if (a || b) {
|
||||
submit(id);
|
||||
}
|
||||
}
|
||||
if (didWork || (tickCount & 15L) == 0L) unpark();
|
||||
tickCount += 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package org.dreeam.leaf.async.ai;
|
||||
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
public class AsyncGoalThread extends Thread {
|
||||
|
||||
private static final int SPIN_TRIES = 1000;
|
||||
|
||||
public AsyncGoalThread(final MinecraftServer server) {
|
||||
super(() -> run(server), "Leaf Async Goal Thread");
|
||||
this.setDaemon(false);
|
||||
this.setUncaughtExceptionHandler(Util::onThreadException);
|
||||
this.setPriority(Thread.NORM_PRIORITY - 1);
|
||||
this.start();
|
||||
}
|
||||
|
||||
private static void run(MinecraftServer server) {
|
||||
int emptySpins = 0;
|
||||
|
||||
while (server.isRunning()) {
|
||||
boolean didWork = false;
|
||||
for (ServerLevel level : server.getAllLevels()) {
|
||||
var exec = level.asyncGoalExecutor;
|
||||
boolean levelWork = false;
|
||||
while (true) {
|
||||
int id = exec.queue.recv();
|
||||
if (id == Integer.MAX_VALUE) {
|
||||
break;
|
||||
}
|
||||
levelWork = true;
|
||||
if (exec.wake(id)) {
|
||||
while (!exec.wake.send(id)) {
|
||||
Thread.onSpinWait();
|
||||
}
|
||||
}
|
||||
}
|
||||
didWork |= levelWork;
|
||||
}
|
||||
// Adaptive parking
|
||||
if (didWork) {
|
||||
emptySpins = 0; // Reset counter when work was done
|
||||
} else {
|
||||
emptySpins++;
|
||||
if (emptySpins > SPIN_TRIES) {
|
||||
LockSupport.park(); // Only park after several empty spins
|
||||
emptySpins = 0;
|
||||
} else {
|
||||
Thread.onSpinWait(); // Yield to other threads but don't park
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.dreeam.leaf.async.ai;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class Waker {
|
||||
|
||||
@Nullable
|
||||
public volatile Runnable wake = null;
|
||||
@Nullable
|
||||
public volatile Object result = null;
|
||||
public volatile boolean state = true;
|
||||
|
||||
public final @Nullable Object result() {
|
||||
Object result = this.result;
|
||||
this.result = null;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,8 @@ import java.util.Locale;
|
||||
|
||||
public enum PathfindTaskRejectPolicy {
|
||||
FLUSH_ALL,
|
||||
CALLER_RUNS;
|
||||
CALLER_RUNS,
|
||||
DISCARD;
|
||||
|
||||
public static PathfindTaskRejectPolicy fromString(String policy) {
|
||||
try {
|
||||
|
||||
@@ -46,6 +46,13 @@ public abstract class ConfigModules extends LeafConfig {
|
||||
for (ConfigModules module : MODULES) {
|
||||
module.onPostLoaded();
|
||||
}
|
||||
|
||||
// Save config to disk
|
||||
try {
|
||||
LeafConfig.config().saveConfig();
|
||||
} catch (Exception e) {
|
||||
LeafConfig.LOGGER.error("Failed to save config file!", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Field> getAnnotatedStaticFields(Class<?> clazz, Class<? extends Annotation> annotation) {
|
||||
|
||||
@@ -58,6 +58,7 @@ public class LeafConfig {
|
||||
|
||||
/* Load & Reload */
|
||||
|
||||
// Reload config (async)
|
||||
public static @NotNull CompletableFuture<Void> reloadAsync(CommandSender sender) {
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
@@ -76,6 +77,7 @@ public class LeafConfig {
|
||||
}, Util.ioPool());
|
||||
}
|
||||
|
||||
// Init config
|
||||
public static void loadConfig() {
|
||||
try {
|
||||
long begin = System.nanoTime();
|
||||
@@ -100,9 +102,6 @@ public class LeafConfig {
|
||||
|
||||
// Load config modules
|
||||
ConfigModules.initModules();
|
||||
|
||||
// Save config to disk
|
||||
leafGlobalConfig.saveConfig();
|
||||
}
|
||||
|
||||
public static LeafGlobalConfig config() {
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
package org.dreeam.leaf.config.modules.async;
|
||||
|
||||
import org.dreeam.leaf.config.ConfigModules;
|
||||
import org.dreeam.leaf.config.EnumConfigCategory;
|
||||
|
||||
public class AsyncBlockFinding extends ConfigModules {
|
||||
|
||||
public String getBasePath() {
|
||||
return EnumConfigCategory.ASYNC.getBaseKeyName() + ".async-block-finding";
|
||||
}
|
||||
|
||||
public static boolean enabled = false;
|
||||
public static boolean asyncBlockFindingInitialized;
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
config.addCommentRegionBased(getBasePath(), """
|
||||
This moves the expensive search calculations to a background thread while
|
||||
keeping the actual block validation on the main thread.""",
|
||||
"""
|
||||
这会将昂贵的搜索计算移至后台线程, 同时在主线程上保持实际的方块验证.""");
|
||||
|
||||
if (!asyncBlockFindingInitialized) {
|
||||
asyncBlockFindingInitialized = true;
|
||||
enabled = config.getBoolean(getBasePath() + ".enabled", enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,7 @@ public class AsyncChunkSend extends ConfigModules {
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
config.addCommentRegionBased(getBasePath(),
|
||||
"""
|
||||
config.addCommentRegionBased(getBasePath(), """
|
||||
Makes chunk packet preparation and sending asynchronous to improve server performance.
|
||||
This can significantly reduce main thread load when many players are loading chunks.""",
|
||||
"""
|
||||
|
||||
@@ -10,7 +10,7 @@ public class AsyncMobSpawning extends ConfigModules {
|
||||
}
|
||||
|
||||
public static boolean enabled = true;
|
||||
public static boolean asyncMobSpawningInitialized;
|
||||
private static boolean asyncMobSpawningInitialized;
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
@@ -22,13 +22,16 @@ public class AsyncMobSpawning extends ConfigModules {
|
||||
This just offloads some expensive calculations that are required for mob spawning.""",
|
||||
"""
|
||||
是否异步化生物生成.
|
||||
在实体较多的服务器上, 异步生成可最高带来15%的性能提升.
|
||||
在实体较多的服务器上, 异步生成可最高带来 15% 的性能提升.
|
||||
须在Paper配置文件中打开 per-player-mob-spawns 才能生效.""");
|
||||
|
||||
// This prevents us from changing the value during a reload.
|
||||
if (!asyncMobSpawningInitialized) {
|
||||
asyncMobSpawningInitialized = true;
|
||||
enabled = config.getBoolean(getBasePath() + ".enabled", enabled);
|
||||
if (asyncMobSpawningInitialized) {
|
||||
config.getConfigSection(getBasePath());
|
||||
return;
|
||||
}
|
||||
asyncMobSpawningInitialized = true;
|
||||
|
||||
enabled = config.getBoolean(getBasePath() + ".enabled", enabled);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,27 @@ public class AsyncPathfinding extends ConfigModules {
|
||||
public static int asyncPathfindingKeepalive = 60;
|
||||
public static int asyncPathfindingQueueSize = 0;
|
||||
public static PathfindTaskRejectPolicy asyncPathfindingRejectPolicy = PathfindTaskRejectPolicy.FLUSH_ALL;
|
||||
private static boolean asyncPathfindingInitialized;
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
config.addCommentRegionBased(getBasePath() + ".reject-policy", """
|
||||
The policy to use when the queue is full and a new task is submitted.
|
||||
FLUSH_ALL: All pending tasks will be run on server thread.
|
||||
CALLER_RUNS: Newly submitted task will be run on server thread.
|
||||
DISCARD: Newly submitted task will be dropped directly.""",
|
||||
"""
|
||||
当队列满时, 新提交的任务将使用以下策略处理.
|
||||
FLUSH_ALL: 所有等待中的任务都将在主线程上运行.
|
||||
CALLER_RUNS: 新提交的任务将在主线程上运行.
|
||||
DISCARD: 新提交的任务会被直接丢弃."""
|
||||
);
|
||||
if (asyncPathfindingInitialized) {
|
||||
config.getConfigSection(getBasePath());
|
||||
return;
|
||||
}
|
||||
asyncPathfindingInitialized = true;
|
||||
|
||||
final int availableProcessors = Runtime.getRuntime().availableProcessors();
|
||||
enabled = config.getBoolean(getBasePath() + ".enabled", enabled);
|
||||
asyncPathfindingMaxThreads = config.getInt(getBasePath() + ".max-threads", asyncPathfindingMaxThreads);
|
||||
@@ -37,15 +55,10 @@ public class AsyncPathfinding extends ConfigModules {
|
||||
if (asyncPathfindingQueueSize <= 0)
|
||||
asyncPathfindingQueueSize = asyncPathfindingMaxThreads * 256;
|
||||
|
||||
asyncPathfindingRejectPolicy = PathfindTaskRejectPolicy.fromString(config.getString(getBasePath() + ".reject-policy", availableProcessors >= 12 && asyncPathfindingQueueSize < 512 ? PathfindTaskRejectPolicy.FLUSH_ALL.toString() : PathfindTaskRejectPolicy.CALLER_RUNS.toString(), config.pickStringRegionBased(
|
||||
"""
|
||||
The policy to use when the queue is full and a new task is submitted.
|
||||
FLUSH_ALL: All pending tasks will be run on server thread.
|
||||
CALLER_RUNS: Newly submitted task will be run on server thread.""",
|
||||
"""
|
||||
当队列满时, 新提交的任务将使用以下策略处理.
|
||||
FLUSH_ALL: 所有等待中的任务都将在主线程上运行.
|
||||
CALLER_RUNS: 新提交的任务将在主线程上运行."""
|
||||
)));
|
||||
asyncPathfindingRejectPolicy = PathfindTaskRejectPolicy.fromString(config.getString(getBasePath() + ".reject-policy",
|
||||
availableProcessors >= 12 && asyncPathfindingQueueSize < 512
|
||||
? PathfindTaskRejectPolicy.FLUSH_ALL.toString()
|
||||
: PathfindTaskRejectPolicy.CALLER_RUNS.toString())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,17 +10,13 @@ public class AsyncPlayerDataSave extends ConfigModules {
|
||||
return EnumConfigCategory.ASYNC.getBaseKeyName() + ".async-playerdata-save";
|
||||
}
|
||||
|
||||
@Experimental
|
||||
public static boolean enabled = false;
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
config.addCommentRegionBased(getBasePath(),
|
||||
"""
|
||||
**Experimental feature, may have data lost in some circumstances!**
|
||||
config.addCommentRegionBased(getBasePath(), """
|
||||
Make PlayerData saving asynchronously.""",
|
||||
"""
|
||||
**实验性功能, 在部分场景下可能丢失玩家数据!**
|
||||
异步保存玩家数据.""");
|
||||
|
||||
enabled = config.getBoolean(getBasePath() + ".enabled", enabled);
|
||||
|
||||
@@ -11,22 +11,40 @@ public class AsyncTargetFinding extends ConfigModules {
|
||||
return EnumConfigCategory.ASYNC.getBaseKeyName() + ".async-target-finding";
|
||||
}
|
||||
|
||||
@Experimental
|
||||
public static boolean enabled = false;
|
||||
public static boolean asyncTargetFindingInitialized;
|
||||
public static boolean alertOther = true;
|
||||
public static boolean searchBlock = true;
|
||||
public static boolean searchEntity = true;
|
||||
public static int queueSize = 4096;
|
||||
private static boolean asyncTargetFindingInitialized;
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
config.addCommentRegionBased(getBasePath(), """
|
||||
**Experimental feature**
|
||||
This moves the expensive entity target search calculations to a background thread while
|
||||
keeping the actual entity validation on the main thread.""",
|
||||
This moves the expensive entity and block search calculations to background thread while
|
||||
keeping the actual validation on the main thread.""",
|
||||
"""
|
||||
这会将昂贵的实体目标搜索计算移至后台线程, 同时在主线程上保持实际的实体验证.""");
|
||||
|
||||
if (!asyncTargetFindingInitialized) {
|
||||
asyncTargetFindingInitialized = true;
|
||||
enabled = config.getBoolean(getBasePath() + ".enabled", enabled);
|
||||
if (asyncTargetFindingInitialized) {
|
||||
config.getConfigSection(getBasePath());
|
||||
return;
|
||||
}
|
||||
asyncTargetFindingInitialized = true;
|
||||
|
||||
enabled = config.getBoolean(getBasePath() + ".enabled", enabled);
|
||||
alertOther = config.getBoolean(getBasePath() + ".async-alert-other", true);
|
||||
searchBlock = config.getBoolean(getBasePath() + ".async-search-block", true);
|
||||
searchEntity = config.getBoolean(getBasePath() + ".async-search-entity", true);
|
||||
queueSize = config.getInt(getBasePath() + ".queue-size", 4096);
|
||||
|
||||
if (queueSize <= 0) {
|
||||
queueSize = 4096;
|
||||
}
|
||||
if (!enabled) {
|
||||
alertOther = false;
|
||||
searchEntity = false;
|
||||
searchBlock = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ public class MultithreadedTracker extends ConfigModules {
|
||||
public static int asyncEntityTrackerMaxThreads = 0;
|
||||
public static int asyncEntityTrackerKeepalive = 60;
|
||||
public static int asyncEntityTrackerQueueSize = 0;
|
||||
private static boolean asyncMultithreadedTrackerInitialized;
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
@@ -24,15 +25,22 @@ public class MultithreadedTracker extends ConfigModules {
|
||||
"""
|
||||
异步实体跟踪,
|
||||
在实体数量多且密集的情况下效果明显.""");
|
||||
|
||||
enabled = config.getBoolean(getBasePath() + ".enabled", enabled);
|
||||
compatModeEnabled = config.getBoolean(getBasePath() + ".compat-mode", compatModeEnabled, config.pickStringRegionBased("""
|
||||
config.addCommentRegionBased(getBasePath() + ".compat-mode", """
|
||||
Enable compat mode ONLY if Citizens or NPC plugins using real entity has installed,
|
||||
Compat mode fixed visible issue with player type NPCs of Citizens,
|
||||
But still recommend to use packet based / virtual entity NPC plugin, e.g. ZNPC Plus, Adyeshach, Fancy NPC or else.""",
|
||||
"""
|
||||
是否启用兼容模式,
|
||||
如果你的服务器安装了 Citizens 或其他类似非发包 NPC 插件, 请开启此项."""));
|
||||
如果你的服务器安装了 Citizens 或其他类似非发包 NPC 插件, 请开启此项.""");
|
||||
|
||||
if (asyncMultithreadedTrackerInitialized) {
|
||||
config.getConfigSection(getBasePath());
|
||||
return;
|
||||
}
|
||||
asyncMultithreadedTrackerInitialized = true;
|
||||
|
||||
enabled = config.getBoolean(getBasePath() + ".enabled", enabled);
|
||||
compatModeEnabled = config.getBoolean(getBasePath() + ".compat-mode", compatModeEnabled);
|
||||
asyncEntityTrackerMaxThreads = config.getInt(getBasePath() + ".max-threads", asyncEntityTrackerMaxThreads);
|
||||
asyncEntityTrackerKeepalive = config.getInt(getBasePath() + ".keepalive", asyncEntityTrackerKeepalive);
|
||||
asyncEntityTrackerQueueSize = config.getInt(getBasePath() + ".queue-size", asyncEntityTrackerQueueSize);
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.dreeam.leaf.config.modules.async;
|
||||
|
||||
import org.dreeam.leaf.config.ConfigModules;
|
||||
import org.dreeam.leaf.config.EnumConfigCategory;
|
||||
import org.dreeam.leaf.config.LeafConfig;
|
||||
import org.dreeam.leaf.config.annotations.Experimental;
|
||||
|
||||
public class SparklyPaperParallelWorldTicking extends ConfigModules {
|
||||
@@ -19,22 +20,29 @@ public class SparklyPaperParallelWorldTicking extends ConfigModules {
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
config.addCommentRegionBased(getBasePath(),
|
||||
"""
|
||||
config.addCommentRegionBased(getBasePath(), """
|
||||
**Experimental feature**
|
||||
Enables parallel world ticking to improve performance on multi-core systems..""",
|
||||
Enables parallel world ticking to improve performance on multi-core systems.""",
|
||||
"""
|
||||
**实验性功能**
|
||||
启用并行世界处理以提高多核系统的性能.""");
|
||||
启用并行世界处理以提高多核 CPU 使用率.""");
|
||||
|
||||
enabled = config.getBoolean(getBasePath() + ".enabled", enabled);
|
||||
threads = config.getInt(getBasePath() + ".threads", threads);
|
||||
threads = enabled ? threads : 0;
|
||||
if (enabled) {
|
||||
if (threads <= 0) threads = 8;
|
||||
} else {
|
||||
threads = 0;
|
||||
}
|
||||
logContainerCreationStacktraces = config.getBoolean(getBasePath() + ".log-container-creation-stacktraces", logContainerCreationStacktraces);
|
||||
logContainerCreationStacktraces = enabled && logContainerCreationStacktraces;
|
||||
disableHardThrow = config.getBoolean(getBasePath() + ".disable-hard-throw", disableHardThrow);
|
||||
disableHardThrow = enabled && disableHardThrow;
|
||||
runAsyncTasksSync = config.getBoolean(getBasePath() + ".run-async-tasks-sync", runAsyncTasksSync);
|
||||
runAsyncTasksSync = enabled && runAsyncTasksSync;
|
||||
|
||||
if (enabled) {
|
||||
LeafConfig.LOGGER.info("Using {} threads for Parallel World Ticking", threads);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.dreeam.leaf.config.modules.gameplay;
|
||||
|
||||
import org.dreeam.leaf.config.ConfigModules;
|
||||
import org.dreeam.leaf.config.EnumConfigCategory;
|
||||
|
||||
public class ConfigurableInventoryOverflowEvent extends ConfigModules {
|
||||
|
||||
public String getBasePath() {
|
||||
return EnumConfigCategory.GAMEPLAY.getBaseKeyName() + ".inventory-overflow-event";
|
||||
}
|
||||
|
||||
public static boolean enabled = false;
|
||||
public static String listenerClass = "com.example.package.PlayerInventoryOverflowEvent" ;
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
enabled = config.getBoolean(getBasePath() + ".enabled", enabled, config.pickStringRegionBased("""
|
||||
The event called when used plugin to Inventory#addItem
|
||||
into player's inventory, and the inventory is full.
|
||||
This is not recommended to use, please re-design to use the
|
||||
returned map of Inventory#addItem method as soon as possible!""",
|
||||
"""
|
||||
此事件将在插件使用 Inventory#addItem 方法
|
||||
添加物品到玩家背包, 但是背包已满时调用.
|
||||
不建议使用此事件,请尽快迁移至使用 Inventory#addItem 方法
|
||||
返回的 map"""));
|
||||
listenerClass = config.getString(getBasePath() + ".listener-class", listenerClass, config.pickStringRegionBased("""
|
||||
The full class name of the listener which listens to this inventory overflow event.""",
|
||||
"""
|
||||
监听此物品栏物品溢出事件的完整类名."""));
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,7 @@ public class SmoothTeleport extends ConfigModules {
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
enabled = config.getBoolean(getBasePath(), enabled, config.pickStringRegionBased(
|
||||
"""
|
||||
enabled = config.getBoolean(getBasePath(), enabled, config.pickStringRegionBased("""
|
||||
**Experimental feature**
|
||||
Whether to make a "smooth teleport" when players changing dimension.
|
||||
This requires original world and target world have same logical height to work.""",
|
||||
|
||||
@@ -10,7 +10,7 @@ public class ServerBrand extends ConfigModules {
|
||||
}
|
||||
|
||||
public static String serverModName = io.papermc.paper.ServerBuildInfo.buildInfo().brandName();
|
||||
public static String serverGUIName = "Leaf Console";
|
||||
public static String serverGUIName = io.papermc.paper.ServerBuildInfo.buildInfo().brandName() + " Console";
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
|
||||
@@ -17,8 +17,7 @@ public class ChatMessageSignature extends ConfigModules {
|
||||
Whether or not enable chat message signature,
|
||||
disable will prevent players to report chat messages.
|
||||
And also disables the popup when joining a server without
|
||||
'secure chat', such as offline-mode servers.
|
||||
""",
|
||||
'secure chat', such as offline-mode servers.""",
|
||||
"""
|
||||
是否启用聊天签名, 禁用后玩家无法进行聊天举报."""));
|
||||
}
|
||||
|
||||
@@ -20,8 +20,7 @@ public class OptimizeNonFlushPacketSending extends ConfigModules {
|
||||
Optimizes non-flush packet sending by using Netty's lazyExecute method to avoid
|
||||
expensive thread wakeup calls when scheduling packet operations.
|
||||
|
||||
Requires server restart to take effect.
|
||||
""",
|
||||
Requires server restart to take effect.""",
|
||||
"""
|
||||
警告: 此选项与 ProtocolLib 不兼容, 并可能导致与其他修改数据包
|
||||
处理的插件出现问题.
|
||||
@@ -29,7 +28,6 @@ public class OptimizeNonFlushPacketSending extends ConfigModules {
|
||||
通过使用 Netty 的 lazyExecute 方法来优化非刷新数据包的发送,
|
||||
避免在调度数据包操作时进行昂贵的线程唤醒调用.
|
||||
|
||||
需要重启服务器才能生效.
|
||||
"""));
|
||||
需要重启服务器才能生效."""));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,7 @@ public class DontSaveEntity extends ConfigModules {
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
dontSavePrimedTNT = config.getBoolean(getBasePath() + ".dont-save-primed-tnt", dontSavePrimedTNT,
|
||||
config.pickStringRegionBased(
|
||||
"""
|
||||
config.pickStringRegionBased("""
|
||||
Disable save primed tnt on chunk unloads.
|
||||
Useful for redstone/technical servers, can prevent machines from being exploded by TNT,
|
||||
when player disconnected caused by Internet issue.""",
|
||||
|
||||
@@ -67,8 +67,8 @@ public class FastRNG extends ConfigModules {
|
||||
Use direct random implementation instead of delegating to Java's RandomGenerator.
|
||||
This may improve performance but potentially changes RNG behavior.""",
|
||||
"""
|
||||
使用直接随机实现而不是委托给Java的RandomGenerator.
|
||||
这可能会提高性能,但可能会改变RNG行为。"""));
|
||||
使用直接随机实现而不是委派给 RandomGenerator.
|
||||
这可能会提高性能, 但可能会改变 RNG 行为."""));
|
||||
|
||||
if (enabled) {
|
||||
try {
|
||||
|
||||
@@ -10,7 +10,7 @@ public class ThrottleHopperWhenFull extends ConfigModules {
|
||||
}
|
||||
|
||||
public static boolean enabled = false;
|
||||
public static int skipTicks = 0;
|
||||
public static int skipTicks = 8;
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package org.dreeam.leaf.util.map;
|
||||
|
||||
import com.google.common.collect.AbstractIterator;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.BlockPos.MutableBlockPos;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
@NullMarked
|
||||
public final class BlockPosIterator extends AbstractIterator<BlockPos> {
|
||||
|
||||
private final int startX;
|
||||
private final int startY;
|
||||
private final int startZ;
|
||||
private final int endX;
|
||||
private final int endY;
|
||||
private final int endZ;
|
||||
private @Nullable MutableBlockPos pos = null;
|
||||
|
||||
public static Iterable<BlockPos> iterable(AABB bb) {
|
||||
return () -> new BlockPosIterator(bb);
|
||||
}
|
||||
|
||||
public static Iterable<BlockPos> traverseArea(Vec3 vec, AABB boundingBox) {
|
||||
double toTravel = Math.min(16.0 / vec.length(), 1.0);
|
||||
Vec3 movement = vec.scale(toTravel);
|
||||
AABB fromBB = boundingBox.move(-vec.x, -vec.y, -vec.z);
|
||||
AABB searchArea = fromBB.expandTowards(movement);
|
||||
return BlockPosIterator.iterable(searchArea);
|
||||
}
|
||||
|
||||
public BlockPosIterator(AABB bb) {
|
||||
this.startX = Mth.floor(bb.minX);
|
||||
this.startY = Mth.floor(bb.minY);
|
||||
this.startZ = Mth.floor(bb.minZ);
|
||||
this.endX = Mth.floor(bb.maxX);
|
||||
this.endY = Mth.floor(bb.maxY);
|
||||
this.endZ = Mth.floor(bb.maxZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BlockPos computeNext() {
|
||||
MutableBlockPos pos = this.pos;
|
||||
if (pos == null) {
|
||||
return this.pos = new MutableBlockPos(this.startX, this.startY, this.startZ);
|
||||
} else {
|
||||
int x = pos.getX();
|
||||
int y = pos.getY();
|
||||
int z = pos.getZ();
|
||||
|
||||
if (y < this.endY) {
|
||||
y += 1;
|
||||
} else if (x < this.endX) {
|
||||
x += 1;
|
||||
y = this.startY;
|
||||
} else if (z < this.endZ) {
|
||||
z += 1;
|
||||
x = this.startX;
|
||||
} else {
|
||||
return this.endOfData();
|
||||
}
|
||||
|
||||
pos.set(x, y, z);
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
package org.dreeam.leaf.util.map;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.*;
|
||||
import it.unimi.dsi.fastutil.longs.LongCollection;
|
||||
import it.unimi.dsi.fastutil.longs.LongIterator;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,20 +20,18 @@ public class FasterRandomSource implements BitRandomSource {
|
||||
private static final RandomGeneratorFactory<RandomGenerator> RANDOM_GENERATOR_FACTORY = RandomGeneratorFactory.of(FastRNG.randomGenerator);
|
||||
private static final boolean isSplittableGenerator = RANDOM_GENERATOR_FACTORY.isSplittable();
|
||||
private long seed;
|
||||
private boolean useDirectImpl;
|
||||
private static final boolean useDirectImpl = FastRNG.useDirectImpl;
|
||||
private RandomGenerator randomGenerator;
|
||||
public static final FasterRandomSource SHARED_INSTANCE = new FasterRandomSource(ThreadLocalRandom.current().nextLong());
|
||||
|
||||
public FasterRandomSource(long seed) {
|
||||
this.seed = seed;
|
||||
this.randomGenerator = RANDOM_GENERATOR_FACTORY.create(seed);
|
||||
this.useDirectImpl = FastRNG.useDirectImpl; // Get the value from config
|
||||
}
|
||||
|
||||
private FasterRandomSource(long seed, RandomGenerator.SplittableGenerator randomGenerator) {
|
||||
this.seed = seed;
|
||||
this.randomGenerator = randomGenerator;
|
||||
this.useDirectImpl = FastRNG.useDirectImpl;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -59,7 +57,6 @@ public class FasterRandomSource implements BitRandomSource {
|
||||
@Override
|
||||
public final int next(int bits) {
|
||||
if (useDirectImpl) {
|
||||
// Direct
|
||||
return (int) ((seed = seed * MULTIPLIER + INCREMENT & SEED_MASK) >>> (INT_BITS - bits));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package org.dreeam.leaf.util.queue;
|
||||
|
||||
/// Lock-free Single Producer Single Consumer Queue
|
||||
public class SpscIntQueue {
|
||||
|
||||
private final int[] data;
|
||||
private final PaddedAtomicInteger producerIdx = new PaddedAtomicInteger();
|
||||
private final PaddedAtomicInteger producerCachedIdx = new PaddedAtomicInteger();
|
||||
private final PaddedAtomicInteger consumerIdx = new PaddedAtomicInteger();
|
||||
private final PaddedAtomicInteger consumerCachedIdx = new PaddedAtomicInteger();
|
||||
|
||||
public SpscIntQueue(int size) {
|
||||
this.data = new int[size + 1];
|
||||
}
|
||||
|
||||
public final boolean send(int e) {
|
||||
final int idx = producerIdx.getOpaque();
|
||||
int nextIdx = idx + 1;
|
||||
if (nextIdx == data.length) {
|
||||
nextIdx = 0;
|
||||
}
|
||||
int cachedIdx = consumerCachedIdx.getPlain();
|
||||
if (nextIdx == cachedIdx) {
|
||||
cachedIdx = consumerIdx.getAcquire();
|
||||
consumerCachedIdx.setPlain(cachedIdx);
|
||||
if (nextIdx == cachedIdx) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
data[idx] = e;
|
||||
producerIdx.setRelease(nextIdx);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public final int recv() {
|
||||
final int idx = consumerIdx.getOpaque();
|
||||
int cachedIdx = producerCachedIdx.getPlain();
|
||||
if (idx == cachedIdx) {
|
||||
cachedIdx = producerIdx.getAcquire();
|
||||
producerCachedIdx.setPlain(cachedIdx);
|
||||
if (idx == cachedIdx) {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
int e = data[idx];
|
||||
int nextIdx = idx + 1;
|
||||
if (nextIdx == data.length) {
|
||||
nextIdx = 0;
|
||||
}
|
||||
consumerIdx.setRelease(nextIdx);
|
||||
return e;
|
||||
}
|
||||
|
||||
public final int size() {
|
||||
return this.data.length;
|
||||
}
|
||||
|
||||
static class PaddedAtomicInteger extends java.util.concurrent.atomic.AtomicInteger {
|
||||
@SuppressWarnings("unused")
|
||||
private int i1, i2, i3, i4, i5, i6, i7, i8,
|
||||
i9, i10, i11, i12, i13, i14, i15;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user