9
0
mirror of https://github.com/BX-Team/DivineMC.git synced 2025-12-19 14:59:25 +00:00

apply a lot of patches

This commit is contained in:
NONPLAYT
2025-10-11 00:28:45 +03:00
parent ebc9c017cf
commit 275de5a367
75 changed files with 1093 additions and 1214 deletions

View File

@@ -0,0 +1,146 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sun, 23 Mar 2025 16:47:24 +0300
Subject: [PATCH] Paper PR: Add FillBottleEvents for player and dispenser
diff --git a/net/minecraft/core/cauldron/CauldronInteraction.java b/net/minecraft/core/cauldron/CauldronInteraction.java
index c58b4a5426dc2e502e240e9b9270ead2ef65ecbd..1618e1d985ea798c1d7485b30fad5b45a285a4b5 100644
--- a/net/minecraft/core/cauldron/CauldronInteraction.java
+++ b/net/minecraft/core/cauldron/CauldronInteraction.java
@@ -63,7 +63,12 @@ public interface CauldronInteraction {
}
// CraftBukkit end
Item item = stack.getItem();
- player.setItemInHand(hand, ItemUtils.createFilledResult(stack, player, new ItemStack(Items.GLASS_BOTTLE)));
+ // DivineMC start - Paper PR: Add FillBottleEvents for player and dispenser
+ final io.papermc.paper.event.player.PlayerFillBottleEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerFillBottleEvent(player, hand, stack, ItemUtils.createFilledResult(stack, player, new ItemStack(Items.GLASS_BOTTLE)));
+ if (event.isCancelled()) {
+ return InteractionResult.PASS;
+ }
+ // DivineMC end - Paper PR: Add FillBottleEvents for player and dispenser
player.awardStat(Stats.USE_CAULDRON);
player.awardStat(Stats.ITEM_USED.get(item));
// level.setBlockAndUpdate(pos, Blocks.WATER_CAULDRON.defaultBlockState()); // CraftBukkit
diff --git a/net/minecraft/core/dispenser/DispenseItemBehavior.java b/net/minecraft/core/dispenser/DispenseItemBehavior.java
index 91a1ae527589ecab1322e97efd9e863a84fcb4fe..181fdb493f64442c659165c10e237ebc198fb6e2 100644
--- a/net/minecraft/core/dispenser/DispenseItemBehavior.java
+++ b/net/minecraft/core/dispenser/DispenseItemBehavior.java
@@ -578,13 +578,25 @@ public interface DispenseItemBehavior {
blockStateBase -> blockStateBase.hasProperty(BeehiveBlock.HONEY_LEVEL) && blockStateBase.getBlock() instanceof BeehiveBlock
)
&& blockState.getValue(BeehiveBlock.HONEY_LEVEL) >= 5) {
+ // DivineMC start - Paper PR: Add FillBottleEvents for player and dispenser
+ final io.papermc.paper.event.block.BlockFillBottleEvent bottleEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFillBottleEvent(serverLevel, blockSource.pos(), item, new ItemStack(Items.HONEY_BOTTLE));
+ if (bottleEvent.isCancelled()) {
+ return item;
+ }
+ // DivineMC end - Paper PR: Add FillBottleEvents for player and dispenser
((BeehiveBlock)blockState.getBlock())
.releaseBeesAndResetHoneyLevel(serverLevel, blockState, blockPos, null, BeehiveBlockEntity.BeeReleaseStatus.BEE_RELEASED);
this.setSuccess(true);
- return this.takeLiquid(blockSource, item, new ItemStack(Items.HONEY_BOTTLE));
+ return this.takeLiquid(blockSource, item, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(bottleEvent.getResultItem())); // DivineMC - Paper PR: Add FillBottleEvents for player and dispenser
} else if (serverLevel.getFluidState(blockPos).is(FluidTags.WATER)) {
+ // DivineMC start - Paper PR: Add FillBottleEvents for player and dispenser
+ final io.papermc.paper.event.block.BlockFillBottleEvent bottleEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFillBottleEvent(serverLevel, blockSource.pos(), item, PotionContents.createItemStack(Items.POTION, Potions.WATER));
+ if (bottleEvent.isCancelled()) {
+ return item;
+ }
+ // DivineMC end - Paper PR: Add FillBottleEvents for player and dispenser
this.setSuccess(true);
- return this.takeLiquid(blockSource, item, PotionContents.createItemStack(Items.POTION, Potions.WATER));
+ return this.takeLiquid(blockSource, item, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(bottleEvent.getResultItem())); // DivineMC - Paper PR: Add FillBottleEvents for player and dispenser
} else {
return super.execute(blockSource, item);
}
diff --git a/net/minecraft/world/item/BottleItem.java b/net/minecraft/world/item/BottleItem.java
index 105f9166297de2bfa6bdcfa9f6a0ffb00c0242ac..111f43fc5c74577f8f3067a4f84be7a6f96fdfb2 100644
--- a/net/minecraft/world/item/BottleItem.java
+++ b/net/minecraft/world/item/BottleItem.java
@@ -35,6 +35,18 @@ public class BottleItem extends Item {
);
ItemStack itemInHand = player.getItemInHand(hand);
if (!entitiesOfClass.isEmpty()) {
+ // DivineMC start - Paper PR: Add FillBottleEvents for player and dispenser
+ final io.papermc.paper.event.player.PlayerFillBottleEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerFillBottleEvent(player, hand, itemInHand, new ItemStack(Items.DRAGON_BREATH));
+ //noinspection DuplicatedCode
+ if (event.isCancelled()) {
+ player.containerMenu.sendAllDataToRemote();
+ return InteractionResult.PASS;
+ }
+ final ItemStack resultItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getResultItem());
+ if (resultItem.is(itemInHand.getItem())) {
+ player.containerMenu.sendAllDataToRemote();
+ }
+ // DivineMC end - Paper PR: Add FillBottleEvents for player and dispenser
AreaEffectCloud areaEffectCloud = entitiesOfClass.get(0);
areaEffectCloud.setRadius(areaEffectCloud.getRadius() - 0.5F);
level.playSound(null, player.getX(), player.getY(), player.getZ(), SoundEvents.BOTTLE_FILL_DRAGONBREATH, SoundSource.NEUTRAL, 1.0F, 1.0F);
@@ -43,7 +55,7 @@ public class BottleItem extends Item {
CriteriaTriggers.PLAYER_INTERACTED_WITH_ENTITY.trigger(serverPlayer, itemInHand, areaEffectCloud);
}
- return InteractionResult.SUCCESS.heldItemTransformedTo(this.turnBottleIntoItem(itemInHand, player, new ItemStack(Items.DRAGON_BREATH)));
+ return InteractionResult.SUCCESS.heldItemTransformedTo(this.turnBottleIntoItem(itemInHand, player, resultItem)); // DivineMC - Paper PR: Add FillBottleEvents for player and dispenser
} else {
BlockHitResult playerPovHitResult = getPlayerPOVHitResult(level, player, ClipContext.Fluid.SOURCE_ONLY);
if (playerPovHitResult.getType() == HitResult.Type.MISS) {
@@ -56,10 +68,22 @@ public class BottleItem extends Item {
}
if (level.getFluidState(blockPos).is(FluidTags.WATER)) {
+ // DivineMC start - Paper PR: Add FillBottleEvents for player and dispenser
+ final io.papermc.paper.event.player.PlayerFillBottleEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerFillBottleEvent(player, hand, itemInHand, PotionContents.createItemStack(Items.POTION, Potions.WATER));
+ //noinspection DuplicatedCode
+ if (event.isCancelled()) {
+ player.containerMenu.sendAllDataToRemote();
+ return InteractionResult.PASS;
+ }
+ final ItemStack resultItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getResultItem());
+ if (resultItem.is(itemInHand.getItem())) {
+ player.containerMenu.sendAllDataToRemote();
+ }
+ // DivineMC end - Paper PR: Add FillBottleEvents for player and dispenser
level.playSound(player, player.getX(), player.getY(), player.getZ(), SoundEvents.BOTTLE_FILL, SoundSource.NEUTRAL, 1.0F, 1.0F);
level.gameEvent(player, GameEvent.FLUID_PICKUP, blockPos);
return InteractionResult.SUCCESS
- .heldItemTransformedTo(this.turnBottleIntoItem(itemInHand, player, PotionContents.createItemStack(Items.POTION, Potions.WATER)));
+ .heldItemTransformedTo(this.turnBottleIntoItem(itemInHand, player, resultItem)); // DivineMC - Paper PR: Add FillBottleEvents for player and dispenser
}
}
diff --git a/net/minecraft/world/level/block/BeehiveBlock.java b/net/minecraft/world/level/block/BeehiveBlock.java
index 0768619454a88f555dd81a609a61f49dc2578f06..4eaec4533497ec354646b75c77f74170cac672bb 100644
--- a/net/minecraft/world/level/block/BeehiveBlock.java
+++ b/net/minecraft/world/level/block/BeehiveBlock.java
@@ -159,12 +159,26 @@ public class BeehiveBlock extends BaseEntityBlock {
flag = true;
level.gameEvent(player, GameEvent.SHEAR, pos);
} else if (stack.is(Items.GLASS_BOTTLE)) {
+ // DivineMC start - Paper PR: Add FillBottleEvents for player and dispenser
+ final io.papermc.paper.event.player.PlayerFillBottleEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerFillBottleEvent(player, hand, stack, new ItemStack(Items.HONEY_BOTTLE));
+ //noinspection DuplicatedCode
+ if (event.isCancelled()) {
+ player.containerMenu.sendAllDataToRemote();
+ return InteractionResult.PASS;
+ }
+ final ItemStack resultItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getResultItem());
+ if (resultItem.is(stack.getItem())) {
+ player.containerMenu.sendAllDataToRemote();
+ }
+ // DivineMC end - Paper PR: Add FillBottleEvents for player and dispenser
stack.shrink(1);
level.playSound(player, player.getX(), player.getY(), player.getZ(), SoundEvents.BOTTLE_FILL, SoundSource.BLOCKS, 1.0F, 1.0F);
if (stack.isEmpty()) {
- player.setItemInHand(hand, new ItemStack(Items.HONEY_BOTTLE));
- } else if (!player.getInventory().add(new ItemStack(Items.HONEY_BOTTLE))) {
- player.drop(new ItemStack(Items.HONEY_BOTTLE), false);
+ // DivineMC start - Paper PR: Add FillBottleEvents for player and dispenser
+ player.setItemInHand(hand, resultItem);
+ } else if (!player.getInventory().add(resultItem)) {
+ player.drop(resultItem, false);
+ // DivineMC end - Paper PR: Add FillBottleEvents for player and dispenser
}
flag = true;

View File

@@ -0,0 +1,323 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Thu, 27 Mar 2025 00:04:19 +0300
Subject: [PATCH] Paper PR: Throttle failed spawn attempts
Original license: GPLv3
Original project: https://github.com/PaperMC/Paper
Paper pull request: https://github.com/PaperMC/Paper/pull/11099
Example config in paper-world-defaults.yml:
```
spawning-throttle:
failed-attempts-threshold: 1200
throttled-ticks-per-spawn:
ambient: 10 # default value in bukkit.yml tickers-per * 10
axolotls: 10
creature: 4000
monster: 10
underground_water_creature: 10
water_ambient: 10
water_creature: 10
```
diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java
index b3649c97e5452d8cc6b7230891e827c6b460af44..f233841c43a0b3f71ed5a90b4df7d8b4902a642d 100644
--- a/net/minecraft/world/level/NaturalSpawner.java
+++ b/net/minecraft/world/level/NaturalSpawner.java
@@ -160,29 +160,52 @@ public final class NaturalSpawner {
// Copied from getFilteredSpawningCategories
int limit = mobCategory.getMaxInstancesPerChunk();
org.bukkit.entity.SpawnCategory spawnCategory = org.bukkit.craftbukkit.util.CraftSpawnCategory.toBukkit(mobCategory);
+ // Paper start - throttle failed spawn attempts
+ boolean spawnThisTick = true;
+ long ticksPerSpawn = level.ticksPerSpawnCategory.getLong(spawnCategory);
+ long ticksPerSpawnTmp = ticksPerSpawn;
+ io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.SpawningThrottle spawningThrottle = level.paperConfig().entities.spawning.spawningThrottle;
+ if (spawningThrottle.failedAttemptsThreshold.test(threshold -> chunk.failedSpawnAttempts[mobCategory.ordinal()] > threshold)) {
+ ticksPerSpawn = Math.max(ticksPerSpawn, spawningThrottle.throttledTicksPerSpawn.getOrDefault(mobCategory, -1));
+ }
+ // Paper end - throttle failed spawn attempts
if (org.bukkit.craftbukkit.util.CraftSpawnCategory.isValidForLimits(spawnCategory)) {
+ spawnThisTick = ticksPerSpawnTmp != 0 && level.getGameTime() % ticksPerSpawn == 0; // Paper - throttle failed spawn attempts
limit = level.getWorld().getSpawnLimit(spawnCategory);
}
- // Apply per-player limit
- int minDiff = Integer.MAX_VALUE;
- final ca.spottedleaf.moonrise.common.list.ReferenceList<net.minecraft.server.level.ServerPlayer> inRange =
- level.moonrise$getNearbyPlayers().getPlayers(chunk.getPos(), ca.spottedleaf.moonrise.common.misc.NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE);
- if (inRange != null) {
- final net.minecraft.server.level.ServerPlayer[] backingSet = inRange.getRawDataUnchecked();
- for (int k = 0, len = inRange.size(); k < len; k++) {
- minDiff = Math.min(limit - level.getChunkSource().chunkMap.getMobCountNear(backingSet[k], mobCategory), minDiff);
+ // Paper start - throttle failed spawn attempts
+ if (!spawningThrottle.failedAttemptsThreshold.enabled() || spawnThisTick) {
+ // Apply per-player limit
+ int minDiff = Integer.MAX_VALUE;
+ final ca.spottedleaf.moonrise.common.list.ReferenceList<net.minecraft.server.level.ServerPlayer> inRange =
+ level.moonrise$getNearbyPlayers().getPlayers(chunk.getPos(), ca.spottedleaf.moonrise.common.misc.NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE);
+ if (inRange != null) {
+ final net.minecraft.server.level.ServerPlayer[] backingSet = inRange.getRawDataUnchecked();
+ for (int k = 0, len = inRange.size(); k < len; k++) {
+ minDiff = Math.min(limit - level.getChunkSource().chunkMap.getMobCountNear(backingSet[k], mobCategory), minDiff);
+ }
}
- }
- maxSpawns = (minDiff == Integer.MAX_VALUE) ? 0 : minDiff;
- canSpawn = maxSpawns > 0;
+ maxSpawns = (minDiff == Integer.MAX_VALUE) ? 0 : minDiff;
+ canSpawn = maxSpawns > 0;
+ } else {
+ canSpawn = false;
+ }
+ // Paper end - throttle failed spawn attempts
} else {
canSpawn = spawnState.canSpawnForCategoryLocal(mobCategory, chunk.getPos());
}
if (canSpawn) {
- spawnCategoryForChunk(mobCategory, level, chunk, spawnState::canSpawn, spawnState::afterSpawn,
+ // Paper start - throttle failed spawn attempts
+ int spawnCount = spawnCategoryForChunk(mobCategory, level, chunk, spawnState::canSpawn, spawnState::afterSpawn,
maxSpawns, level.paperConfig().entities.spawning.perPlayerMobSpawns ? level.getChunkSource().chunkMap::updatePlayerMobTypeMap : null);
+ if (spawnCount == 0) {
+ chunk.failedSpawnAttempts[mobCategory.ordinal()]++;
+ } else {
+ chunk.failedSpawnAttempts[mobCategory.ordinal()] = 0;
+ }
+ // Paper end - throttle failed spawn attempts
// Paper end - Optional per player mob spawns
}
}
@@ -204,14 +227,16 @@ public final class NaturalSpawner {
// Paper start - Optional per player mob spawns
spawnCategoryForChunk(category, level, chunk, filter, callback, Integer.MAX_VALUE, null);
}
- public static void spawnCategoryForChunk(
+ public static int spawnCategoryForChunk( // Paper - throttle failed spawn attempts
MobCategory category, ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback, final int maxSpawns, final Consumer<Entity> trackEntity
) {
// Paper end - Optional per player mob spawns
BlockPos randomPosWithin = getRandomPosWithin(level, chunk);
if (randomPosWithin.getY() >= level.getMinY() + 1) {
- spawnCategoryForPosition(category, level, chunk, randomPosWithin, filter, callback, maxSpawns, trackEntity); // Paper - Optional per player mob spawns
+ return spawnCategoryForPosition(category, level, chunk, randomPosWithin, filter, callback, maxSpawns, trackEntity); // Paper - Optional per player mob spawns // Paper - throttle failed spawn attempts
}
+
+ return 0; // Paper - throttle failed spawn attempts
}
@VisibleForDebug
@@ -229,18 +254,17 @@ public final class NaturalSpawner {
) {
spawnCategoryForPosition(category, level, chunk, pos, filter, callback, Integer.MAX_VALUE, null);
}
- public static void spawnCategoryForPosition(
+ public static int spawnCategoryForPosition( // Paper - throttle failed spawn attempts
MobCategory category, ServerLevel level, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback, final int maxSpawns, final @Nullable Consumer<Entity> trackEntity
) {
// Paper end - Optional per player mob spawns
StructureManager structureManager = level.structureManager();
ChunkGenerator generator = level.getChunkSource().getGenerator();
int y = pos.getY();
+ int i = 0; // Paper - throttle failed spawn attempts
BlockState blockState = level.getBlockStateIfLoadedAndInBounds(pos); // Paper - don't load chunks for mob spawn
if (blockState != null && !blockState.isRedstoneConductor(chunk, pos)) { // Paper - don't load chunks for mob spawn
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
- int i = 0;
-
for (int i1 = 0; i1 < 3; i1++) {
int x = pos.getX();
int z = pos.getZ();
@@ -280,14 +304,14 @@ public final class NaturalSpawner {
}
// Paper end - per player mob count backoff
if (doSpawning == PreSpawnStatus.ABORT) {
- return;
+ return i; // Paper - throttle failed spawn attempts
}
if (doSpawning == PreSpawnStatus.SUCCESS
// Paper end - PreCreatureSpawnEvent
&& filter.test(spawnerData.type(), mutableBlockPos, chunk)) {
Mob mobForSpawn = getMobForSpawn(level, spawnerData.type());
if (mobForSpawn == null) {
- return;
+ return i; // Paper - throttle failed spawn attempts
}
mobForSpawn.snapTo(d, y, d1, level.random.nextFloat() * 360.0F, 0.0F);
@@ -310,7 +334,7 @@ public final class NaturalSpawner {
}
// CraftBukkit end
if (i >= mobForSpawn.getMaxSpawnClusterSize() || i >= maxSpawns) { // Paper - Optional per player mob spawns
- return;
+ return i; // Paper - throttle failed spawn attempts
}
if (mobForSpawn.isMaxGroupSizeReached(i3)) {
@@ -323,6 +347,8 @@ public final class NaturalSpawner {
}
}
}
+
+ return i; // Paper - throttle failed spawn attempts
}
private static boolean isRightDistanceToPlayerAndSpawnPoint(ServerLevel level, ChunkAccess chunk, BlockPos.MutableBlockPos pos, double distance) {
diff --git a/net/minecraft/world/level/chunk/ChunkAccess.java b/net/minecraft/world/level/chunk/ChunkAccess.java
index 9889485b15501c1adf1a73bb4603d3477860482d..2457247d2bc1c3e3b042a091c3a8290d55203da8 100644
--- a/net/minecraft/world/level/chunk/ChunkAccess.java
+++ b/net/minecraft/world/level/chunk/ChunkAccess.java
@@ -86,6 +86,7 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh
private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry();
public org.bukkit.craftbukkit.persistence.DirtyCraftPersistentDataContainer persistentDataContainer = new org.bukkit.craftbukkit.persistence.DirtyCraftPersistentDataContainer(ChunkAccess.DATA_TYPE_REGISTRY);
// CraftBukkit end
+ public final long[] failedSpawnAttempts = new long[net.minecraft.world.entity.MobCategory.values().length]; // Paper - throttle failed spawn attempts
// 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 84840f50d1bcebc7b0bdb1d2f79b53fd6fc98abb..38cdc0f8ce03cfd99658177f609ba3a6590cf000 100644
--- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
+++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
@@ -93,6 +93,7 @@ public record SerializableChunkData(
List<CompoundTag> blockEntities,
CompoundTag structureData
, @Nullable net.minecraft.nbt.Tag persistentDataContainer // CraftBukkit - persistentDataContainer
+ , @Nullable long[] failedSpawnAttempts // Paper - throttle failed spawn attempts
) {
private static final Codec<List<SavedTick<Block>>> BLOCK_TICKS_CODEC = SavedTick.codec(BuiltInRegistries.BLOCK.byNameCodec()).listOf();
private static final Codec<List<SavedTick<Fluid>>> FLUID_TICKS_CODEC = SavedTick.codec(BuiltInRegistries.FLUID.byNameCodec()).listOf();
@@ -186,6 +187,19 @@ public record SerializableChunkData(
lists[i] = list2;
}
+ // Paper start - throttle failed spawn attempts
+ long[] failedSpawnAttemptsData = null;
+ if (tag.contains("Paper.FailedSpawnAttempts")) {
+ failedSpawnAttemptsData = new long[net.minecraft.world.entity.MobCategory.values().length];
+ CompoundTag failedSpawnAttemptsTag = tag.getCompoundOrEmpty("Paper.FailedSpawnAttempts");
+ for (net.minecraft.world.entity.MobCategory mobCategory : net.minecraft.world.level.NaturalSpawner.SPAWNING_CATEGORIES) {
+ if (failedSpawnAttemptsTag.contains(mobCategory.getSerializedName())) {
+ failedSpawnAttemptsData[mobCategory.ordinal()] = failedSpawnAttemptsTag.getLongOr(mobCategory.getSerializedName(), 0);
+ }
+ }
+ }
+ // Paper end - throttle failed spawn attempts
+
List<CompoundTag> list3 = tag.getList("entities").stream().flatMap(ListTag::compoundStream).toList();
List<CompoundTag> list4 = tag.getList("block_entities").stream().flatMap(ListTag::compoundStream).toList();
CompoundTag compoundOrEmpty = tag.getCompoundOrEmpty("structures");
@@ -258,6 +272,7 @@ public record SerializableChunkData(
list4,
compoundOrEmpty
, tag.get("ChunkBukkitValues") // CraftBukkit - ChunkBukkitValues
+ , failedSpawnAttemptsData // Paper - throttle failed spawn attempts
);
}
}
@@ -421,6 +436,15 @@ public record SerializableChunkData(
chunkAccess.addPackedPostProcess(this.postProcessingSections[i], i);
}
+ // Paper start - throttle failed spawn attempts
+ long[] failedSpawnAttemptsData = this.failedSpawnAttempts;
+ if (failedSpawnAttemptsData != null) {
+ for (net.minecraft.world.entity.MobCategory mobCategory : net.minecraft.world.entity.MobCategory.values()) {
+ System.arraycopy(failedSpawnAttemptsData, 0, chunkAccess.failedSpawnAttempts, 0, failedSpawnAttemptsData.length);
+ }
+ }
+ // Paper end - throttle failed spawn attempts
+
if (chunkType == ChunkType.LEVELCHUNK) {
return this.loadStarlightLightData(level, new ImposterProtoChunk((LevelChunk)chunkAccess, false)); // Paper - starlight
} else {
@@ -536,6 +560,7 @@ public record SerializableChunkData(
persistentDataContainer = chunk.persistentDataContainer.toTagCompound();
}
// CraftBukkit end
+ final long[] failedSpawnAttemptsData = chunk.failedSpawnAttempts; // Paper - throttle failed spawn attempts
return new SerializableChunkData(
level.palettedContainerFactory(),
pos,
@@ -556,6 +581,7 @@ public record SerializableChunkData(
list1,
compoundTag
, persistentDataContainer // CraftBukkit - persistentDataContainer
+ , failedSpawnAttemptsData // Paper - throttle failed spawn attempts
);
}
}
@@ -641,6 +667,21 @@ public record SerializableChunkData(
compoundTag.put("ChunkBukkitValues", this.persistentDataContainer);
}
// CraftBukkit end
+ // Paper start - throttle failed spawn attempts
+ CompoundTag failedSpawnAttemptsTag = new CompoundTag();
+ long[] failedSpawnAttemptsData = this.failedSpawnAttempts;
+ if (failedSpawnAttemptsData != null) {
+ for (net.minecraft.world.entity.MobCategory mobCategory : net.minecraft.world.entity.MobCategory.values()) {
+ long failedAttempts = failedSpawnAttemptsData[mobCategory.ordinal()];
+ if (failedAttempts > 0) {
+ failedSpawnAttemptsTag.putLong(mobCategory.getSerializedName(), failedAttempts);
+ }
+ }
+ }
+ if (!failedSpawnAttemptsTag.isEmpty()) {
+ compoundTag.put("Paper.FailedSpawnAttempts", failedSpawnAttemptsTag);
+ }
+ // Paper end - throttle failed spawn attempts
// 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
@@ -856,4 +897,49 @@ public record SerializableChunkData(
}
// Paper end - starlight - convert from record
}
+
+ // Paper start - throttle failed spawn attempts - for plugin compatibility
+ public SerializableChunkData(
+ Registry<Biome> biomeRegistry,
+ ChunkPos chunkPos,
+ int minSectionY,
+ long lastUpdateTime,
+ long inhabitedTime,
+ ChunkStatus chunkStatus,
+ @Nullable BlendingData.Packed blendingData,
+ @Nullable BelowZeroRetrogen belowZeroRetrogen,
+ UpgradeData upgradeData,
+ @Nullable long[] carvingMask,
+ Map<Heightmap.Types, long[]> heightmaps,
+ ChunkAccess.PackedTicks packedTicks,
+ ShortList[] postProcessingSections,
+ boolean lightCorrect,
+ List<net.minecraft.world.level.chunk.storage.SerializableChunkData.SectionData> sectionData,
+ List<CompoundTag> entities,
+ List<CompoundTag> blockEntities,
+ CompoundTag structureData,
+ @Nullable net.minecraft.nbt.Tag persistentDataContainer // CraftBukkit - persistentDataContainer
+ ) {
+ this(biomeRegistry,
+ chunkPos,
+ minSectionY,
+ lastUpdateTime,
+ inhabitedTime,
+ chunkStatus,
+ blendingData,
+ belowZeroRetrogen,
+ upgradeData,
+ carvingMask,
+ heightmaps,
+ packedTicks,
+ postProcessingSections,
+ lightCorrect,
+ sectionData,
+ entities,
+ blockEntities,
+ structureData,
+ persistentDataContainer,
+ null);
+ }
+ // Paper end - throttle failed spawn attempts
}

View File

@@ -0,0 +1,43 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sun, 27 Apr 2025 14:24:19 +0300
Subject: [PATCH] Raytrace AntiXray SDK integration
Integration with Imanity Software's Raytrace AntiXray for better use of this plugin
Original project: https://github.com/Imanity-Software/raytrace-antixray-spigot-sdk
diff --git a/net/minecraft/server/level/ServerPlayerGameMode.java b/net/minecraft/server/level/ServerPlayerGameMode.java
index 599d6a4f31e05369caf3ef9f5e54e83396743ec0..07dd9b8088e363110ecab24026a20485484710c4 100644
--- a/net/minecraft/server/level/ServerPlayerGameMode.java
+++ b/net/minecraft/server/level/ServerPlayerGameMode.java
@@ -316,6 +316,12 @@ public class ServerPlayerGameMode {
org.bukkit.craftbukkit.event.CraftEventFactory.callBlockDamageAbortEvent(this.player, pos, this.player.getInventory().getSelectedItem()); // CraftBukkit
}
}
+ // Imanity start - AntiXraySDK integration
+ dev.imanity.antixray.sdk.AntiXrayAdapter adapter = dev.imanity.antixray.sdk.AntiXraySDK.getAdapter();
+ if (adapter != null) {
+ adapter.callPlayerLeftClickBlock(this.level.getWorld(), this.player.getBukkitEntity(), pos.getX(), pos.getY(), pos.getZ());
+ }
+ // Imanity end - AntiXraySDK integration
this.level.chunkPacketBlockController.onPlayerLeftClickBlock(this, pos, action, face, maxBuildHeight, sequence); // Paper - Anti-Xray
}
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index 5b50b86c735fa2ba1b5452aacda7db5e64012076..b94b946986258fed3c6d68d9972a657e176d08a4 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -1125,6 +1125,12 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
snapshot.setFlags(flags); // Paper - always set the flag of the most recent call to mitigate issues with multiple update at the same pos with different flags
}
BlockState blockState = chunkAt.setBlockState(pos, state, flags);
+ // Imanity start - AntiXraySDK integration
+ dev.imanity.antixray.sdk.AntiXrayAdapter adapter = dev.imanity.antixray.sdk.AntiXraySDK.getAdapter();
+ if (adapter != null) {
+ adapter.callBlockChange(world, pos.getX(), pos.getY(), pos.getZ(), state.getBukkitMaterial());
+ }
+ // Imanity end - AntiXraySDK integration
this.chunkPacketBlockController.onBlockChange(this, pos, state, blockState, flags, recursionLeft); // Paper - Anti-Xray
// CraftBukkit end
if (blockState == null) {

View File

@@ -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] Paper PR: 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)
This patch fixes PaperMC/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 8697528095ebed543ee1bb3b6b617a26a800bd5d..4fc29f7aae32180f86af971f7f80a37aa6e797e4 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, 0L, TicketType.FLAG_LOADING | TicketType.FLAG_SIMULATION | TicketType.FLAG_KEEP_DIMENSION_ACTIVE);
public static final TicketType PLAYER_TICKET_DELAYED = ChunkSystemTicketType.create("chunk_system:player_ticket_delayed", Long::compareTo, 1L, TicketType.FLAG_LOADING | TicketType.FLAG_SIMULATION | TicketType.FLAG_KEEP_DIMENSION_ACTIVE);
+ 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 561165ccc8c4d66ef590fb8deb65782a214d9611..79210997e56ec6798d249608049e108724670cde 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -220,6 +220,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.suppressTrackerForLogin = 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
this.sendLevelInfo(player, serverLevel);
serverLevel.addNewPlayer(player);
this.server.getCustomBossEvents().onPlayerConnect(player); // see commented out section below serverLevel.addPlayerJoin(player);

View File

@@ -0,0 +1,49 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sun, 29 Jun 2025 15:12:40 +0300
Subject: [PATCH] Smooth teleport API
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index 161dbd49495c33fb938a6312b6e24912474864c5..dd933185b1afadae52b51c95bb566bb453a9bfed 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -443,6 +443,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
private boolean tpsBar = false; // Purpur - Implement TPSBar
private boolean compassBar = false; // Purpur - Add compass command
private boolean ramBar = false; // Purpur - Implement rambar commands
+ public boolean smoothWorldTeleport; // DivineMC - Smooth teleport API
// Paper start - rewrite chunk system
private ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.PlayerChunkLoaderData chunkLoader;
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index 79210997e56ec6798d249608049e108724670cde..6c6213323df278391b30dd9f22c408a34ec052c8 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -664,11 +664,11 @@ public abstract class PlayerList {
byte b = (byte)(keepInventory ? 1 : 0);
ServerLevel serverLevel = serverPlayer.level();
LevelData levelData = serverLevel.getLevelData();
- serverPlayer.connection.send(new ClientboundRespawnPacket(serverPlayer.createCommonSpawnInfo(serverLevel), b));
+ if (!serverPlayer.smoothWorldTeleport || !isSameLogicalHeight((ServerLevel) fromWorld, level)) serverPlayer.connection.send(new ClientboundRespawnPacket(serverPlayer.createCommonSpawnInfo(serverLevel), b)); // DivineMC - Smooth teleport API
// serverPlayer.connection.teleport(serverPlayer.getX(), serverPlayer.getY(), serverPlayer.getZ(), serverPlayer.getYRot(), serverPlayer.getXRot());
serverPlayer.connection.send(new ClientboundSetChunkCacheRadiusPacket(serverLevel.spigotConfig.viewDistance)); // Spigot
serverPlayer.connection.send(new ClientboundSetSimulationDistancePacket(serverLevel.spigotConfig.simulationDistance)); // Spigot
- serverPlayer.connection.teleport(org.bukkit.craftbukkit.util.CraftLocation.toBukkit(serverPlayer.position(), serverLevel, serverPlayer.getYRot(), serverPlayer.getXRot())); // CraftBukkit
+ if (!serverPlayer.smoothWorldTeleport || !isSameLogicalHeight((ServerLevel) fromWorld, level)) serverPlayer.connection.teleport(org.bukkit.craftbukkit.util.CraftLocation.toBukkit(serverPlayer.position(), serverLevel, serverPlayer.getYRot(), serverPlayer.getXRot())); // DivineMC - Smooth teleport API
serverPlayer.connection.send(new ClientboundSetDefaultSpawnPositionPacket(level.getRespawnData()));
serverPlayer.connection.send(new ClientboundChangeDifficultyPacket(levelData.getDifficulty(), levelData.isDifficultyLocked()));
serverPlayer.connection
@@ -756,6 +756,12 @@ public abstract class PlayerList {
return serverPlayer;
}
+ // DivineMC start - Smooth teleport API
+ public static boolean isSameLogicalHeight(ServerLevel fromLevel, ServerLevel toLevel) {
+ return fromLevel.getLogicalHeight() == toLevel.getLogicalHeight();
+ }
+ // DivineMC end - Smooth teleport API
+
public void sendActivePlayerEffects(ServerPlayer player) {
this.sendActiveEffects(player, player.connection);
}

View File

@@ -0,0 +1,90 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Mon, 24 Feb 2025 19:10:17 +0300
Subject: [PATCH] lithium: fast_util
This patch is based on the following mixins:
* "net/caffeinemc/mods/lithium/mixin/math/fast_util/DirectionMixin.java"
* "net/caffeinemc/mods/lithium/mixin/math/fast_util/AABBMixin.java"
By: 2No2Name <2No2Name@web.de>
As part of: Lithium (https://github.com/CaffeineMC/lithium)
Licensed under: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html)
diff --git a/net/minecraft/core/Direction.java b/net/minecraft/core/Direction.java
index 6e2b67350dd23a76a9f702ed192c8ae064d13915..f89d261b5ddd8413bc82eeade26a180010b5d6b1 100644
--- a/net/minecraft/core/Direction.java
+++ b/net/minecraft/core/Direction.java
@@ -225,7 +225,7 @@ public enum Direction implements StringRepresentable, ca.spottedleaf.moonrise.pa
}
public Direction getOpposite() {
- return this.opposite; // Paper - optimise collisions
+ return VALUES[this.oppositeIndex]; // DivineMC - lithium: fast_util
}
public Direction getClockWise(Direction.Axis axis) {
@@ -358,7 +358,7 @@ public enum Direction implements StringRepresentable, ca.spottedleaf.moonrise.pa
}
public static Direction getRandom(RandomSource random) {
- return Util.getRandom(VALUES, random);
+ return VALUES[random.nextInt(VALUES.length)]; // DivineMC - lithium: fast_util
}
public static Direction getApproximateNearest(double x, double y, double z) {
diff --git a/net/minecraft/world/phys/AABB.java b/net/minecraft/world/phys/AABB.java
index 9987e2e8278cd43c6c52063132670486ac189677..5f745afa7f30cd61e45d60d877e13c50cf818de7 100644
--- a/net/minecraft/world/phys/AABB.java
+++ b/net/minecraft/world/phys/AABB.java
@@ -19,6 +19,15 @@ public class AABB {
public final double maxY;
public final double maxZ;
+ // DivineMC start - lithium: fast_util
+ static {
+ assert Direction.Axis.X.ordinal() == 0;
+ assert Direction.Axis.Y.ordinal() == 1;
+ assert Direction.Axis.Z.ordinal() == 2;
+ assert Direction.Axis.values().length == 3;
+ }
+ // DivineMC end - lithium: fast_util
+
public AABB(double x1, double y1, double z1, double x2, double y2, double z2) {
this.minX = Math.min(x1, x2);
this.minY = Math.min(y1, y2);
@@ -80,11 +89,33 @@ public class AABB {
}
public double min(Direction.Axis axis) {
- return axis.choose(this.minX, this.minY, this.minZ);
+ // DivineMC start - lithium: fast_util
+ switch (axis.ordinal()) {
+ case 0: // X
+ return this.minX;
+ case 1: // Y
+ return this.minY;
+ case 2: // Z
+ return this.minZ;
+ }
+
+ throw new IllegalArgumentException();
+ // DivineMC end - lithium: fast_util
}
public double max(Direction.Axis axis) {
- return axis.choose(this.maxX, this.maxY, this.maxZ);
+ // DivineMC start - lithium: fast_util
+ switch (axis.ordinal()) {
+ case 0: // X
+ return this.maxX;
+ case 1: // Y
+ return this.maxY;
+ case 2: // Z
+ return this.maxZ;
+ }
+
+ throw new IllegalArgumentException();
+ // DivineMC end - lithium: fast_util
}
@Override

View File

@@ -0,0 +1,67 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sun, 6 Jul 2025 01:55:37 +0300
Subject: [PATCH] C2ME: Optimize world gen math
This patch is based on following mixins:
* "com/ishland/c2me/opts/math/mixin/MixinChunkPos.java"
* "com/ishland/c2me/opts/worldgen/vanilla/mixin/structure_weight_sampler/MixinStructureWeightSampler.java"
By: ishland <ishlandmc@yeah.net>
As part of: C2ME-fabric (https://github.com/RelativityMC/C2ME-fabric)
Licensed under: MIT
diff --git a/net/minecraft/world/level/ChunkPos.java b/net/minecraft/world/level/ChunkPos.java
index 4caaed6d36a6d12279d68e974a2e8381ba84a951..16b5dac678790cebc8fb577fa2f1c7a05aaeb85b 100644
--- a/net/minecraft/world/level/ChunkPos.java
+++ b/net/minecraft/world/level/ChunkPos.java
@@ -110,7 +110,12 @@ public class ChunkPos {
@Override
public boolean equals(Object other) {
- return this == other || other instanceof ChunkPos chunkPos && this.x == chunkPos.x && this.z == chunkPos.z;
+ // DivineMC start - C2ME: Optimize world gen math
+ if (other == this) return true;
+ if (other == null || other.getClass() != this.getClass()) return false;
+ ChunkPos thatPos = (ChunkPos) other;
+ return this.x == thatPos.x && this.z == thatPos.z;
+ // DivineMC end - C2ME: Optimize world gen math
}
public int getMiddleBlockX() {
diff --git a/net/minecraft/world/level/levelgen/Beardifier.java b/net/minecraft/world/level/levelgen/Beardifier.java
index 5e1d88ef91e585a34e9213da923df796dd866e03..c9ddfc8670614c2d8629066b0cc805d18e4f662f 100644
--- a/net/minecraft/world/level/levelgen/Beardifier.java
+++ b/net/minecraft/world/level/levelgen/Beardifier.java
@@ -168,8 +168,14 @@ public class Beardifier implements DensityFunctions.BeardifierOrMarker {
}
private static double getBuryContribution(double x, double y, double z) {
- double len = Mth.length(x, y, z);
- return Mth.clampedMap(len, 0.0, 6.0, 1.0, 0.0);
+ // DivineMC start - C2ME: Optimize world gen math
+ double len = Math.sqrt(x * x + y * y + z * z);
+ if (len > 6.0) {
+ return 0.0;
+ } else {
+ return 1.0 - len / 6.0;
+ }
+ // DivineMC end - C2ME: Optimize world gen math
}
private static double getBeardContribution(int x, int y, int z, int height) {
diff --git a/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java b/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java
index 199a9c4fba5fc80f04524a188270fdc9014f7950..952d00151cc6c295f37bcc499ddcb8155185c79c 100644
--- a/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java
+++ b/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java
@@ -73,7 +73,10 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator {
if (SharedConstants.DEBUG_DISABLE_FLUID_GENERATION) {
return fluidStatus2;
} else {
- return y < Math.min(-54, seaLevel) ? fluidStatus : fluidStatus1;
+ // DivineMC start - C2ME: Optimize world gen math
+ final int min = Math.min(-54, seaLevel);
+ return y < min ? fluidStatus : fluidStatus1;
+ // DivineMC end - C2ME: Optimize world gen math
}
};
}

View File

@@ -0,0 +1,129 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Wed, 29 Jan 2025 00:54:19 +0300
Subject: [PATCH] Async locate command
diff --git a/net/minecraft/server/commands/LocateCommand.java b/net/minecraft/server/commands/LocateCommand.java
index de314f1958304ea3285dfdd776f73f0628cd5af8..1692724f5406a22702f185f66073dd5e836ce7f8 100644
--- a/net/minecraft/server/commands/LocateCommand.java
+++ b/net/minecraft/server/commands/LocateCommand.java
@@ -100,44 +100,77 @@ public class LocateCommand {
}
private static int locateStructure(CommandSourceStack source, ResourceOrTagKeyArgument.Result<Structure> structure) throws CommandSyntaxException {
- Registry<Structure> registry = source.getLevel().registryAccess().lookupOrThrow(Registries.STRUCTURE);
- HolderSet<Structure> holderSet = (HolderSet<Structure>)getHolders(structure, registry)
- .orElseThrow(() -> ERROR_STRUCTURE_INVALID.create(structure.asPrintable()));
- BlockPos blockPos = BlockPos.containing(source.getPosition());
- ServerLevel level = source.getLevel();
- Stopwatch stopwatch = Stopwatch.createStarted(Util.TICKER);
- Pair<BlockPos, Holder<Structure>> pair = level.getChunkSource().getGenerator().findNearestMapStructure(level, holderSet, blockPos, 100, false);
- stopwatch.stop();
- if (pair == null) {
- throw ERROR_STRUCTURE_NOT_FOUND.create(structure.asPrintable());
- } else {
- return showLocateResult(source, structure, blockPos, pair, "commands.locate.structure.success", false, stopwatch.elapsed());
- }
+ // DivineMC start - Async structure locate
+ io.papermc.paper.util.MCUtil.scheduleAsyncTask(() -> {
+ Registry<Structure> registry = source.getLevel().registryAccess().lookupOrThrow(Registries.STRUCTURE);
+ HolderSet<Structure> holderSet;
+ try {
+ holderSet = getHolders(structure, registry)
+ .orElseThrow(() -> ERROR_STRUCTURE_INVALID.create(structure.asPrintable()));
+ } catch (CommandSyntaxException e) {
+ source.sendFailure(Component.literal(e.getMessage()));
+ return;
+ }
+ BlockPos blockPos = BlockPos.containing(source.getPosition());
+ ServerLevel level = source.getLevel();
+ Stopwatch stopwatch = Stopwatch.createStarted(Util.TICKER);
+ Pair<BlockPos, Holder<Structure>> pair = level.getChunkSource().getGenerator().findNearestMapStructure(level, holderSet, blockPos, 100, false);
+ stopwatch.stop();
+ if (pair == null) {
+ try {
+ throw ERROR_STRUCTURE_NOT_FOUND.create(structure.asPrintable());
+ } catch (CommandSyntaxException e) {
+ source.sendFailure(Component.literal(e.getMessage()));
+ }
+ } else {
+ showLocateResult(source, structure, blockPos, pair, "commands.locate.structure.success", false, stopwatch.elapsed());
+ }
+ });
+ return 0;
+ // DivineMC end - Async structure locate
}
private static int locateBiome(CommandSourceStack source, ResourceOrTagArgument.Result<Biome> biome) throws CommandSyntaxException {
- BlockPos blockPos = BlockPos.containing(source.getPosition());
- Stopwatch stopwatch = Stopwatch.createStarted(Util.TICKER);
- Pair<BlockPos, Holder<Biome>> pair = source.getLevel().findClosestBiome3d(biome, blockPos, 6400, 32, 64);
- stopwatch.stop();
- if (pair == null) {
- throw ERROR_BIOME_NOT_FOUND.create(biome.asPrintable());
- } else {
- return showLocateResult(source, biome, blockPos, pair, "commands.locate.biome.success", true, stopwatch.elapsed());
- }
+ // DivineMC start - Async biome locate
+ io.papermc.paper.util.MCUtil.scheduleAsyncTask(() -> {
+ BlockPos blockPos = BlockPos.containing(source.getPosition());
+ Stopwatch stopwatch = Stopwatch.createStarted(Util.TICKER);
+ Pair<BlockPos, Holder<Biome>> pair = source.getLevel().findClosestBiome3d(biome, blockPos, 6400, 32, 64);
+ stopwatch.stop();
+ if (pair == null) {
+ try {
+ throw ERROR_BIOME_NOT_FOUND.create(biome.asPrintable());
+ } catch (CommandSyntaxException e) {
+ source.sendFailure(Component.literal(e.getMessage()));
+ }
+ } else {
+ showLocateResult(source, biome, blockPos, pair, "commands.locate.biome.success", true, stopwatch.elapsed());
+ }
+ });
+ return 0;
+ // DivineMC end - Async biome locate
}
private static int locatePoi(CommandSourceStack source, ResourceOrTagArgument.Result<PoiType> poiType) throws CommandSyntaxException {
- BlockPos blockPos = BlockPos.containing(source.getPosition());
- ServerLevel level = source.getLevel();
- Stopwatch stopwatch = Stopwatch.createStarted(Util.TICKER);
- Optional<Pair<Holder<PoiType>, BlockPos>> optional = level.getPoiManager().findClosestWithType(poiType, blockPos, 256, PoiManager.Occupancy.ANY);
- stopwatch.stop();
- if (optional.isEmpty()) {
- throw ERROR_POI_NOT_FOUND.create(poiType.asPrintable());
- } else {
- return showLocateResult(source, poiType, blockPos, optional.get().swap(), "commands.locate.poi.success", false, stopwatch.elapsed());
- }
+ // DivineMC start - Async poi locate
+ io.papermc.paper.util.MCUtil.scheduleAsyncTask(() -> {
+ BlockPos blockPos = BlockPos.containing(source.getPosition());
+ ServerLevel level = source.getLevel();
+ Stopwatch stopwatch = Stopwatch.createStarted(Util.TICKER);
+ Optional<Pair<Holder<PoiType>, BlockPos>> optional = level.getPoiManager().findClosestWithType(poiType, blockPos, 256, PoiManager.Occupancy.ANY);
+ stopwatch.stop();
+ if (optional.isEmpty()) {
+ try {
+ throw ERROR_POI_NOT_FOUND.create(poiType.asPrintable());
+ } catch (CommandSyntaxException e) {
+ source.sendFailure(Component.literal(e.getMessage()));
+ }
+ } else {
+ showLocateResult(source, poiType, blockPos, optional.get().swap(), "commands.locate.poi.success", false, stopwatch.elapsed());
+ }
+ });
+ return 0;
+ // DivineMC end - Async poi locate
}
public static int showLocateResult(
@@ -192,7 +225,7 @@ public class LocateCommand {
.withHoverEvent(new HoverEvent.ShowText(Component.translatable("chat.coordinates.tooltip")))
);
source.sendSuccess(() -> Component.translatable(translationKey, elementName, component, i), false);
- LOGGER.info("Locating element {} took {} ms", elementName, duration.toMillis());
+ LOGGER.info("Locating element {} on Thread:{} took {} ms", elementName, Thread.currentThread().getName(), duration.toMillis()); // DivineMC - Log thread name
return i;
}

View File

@@ -0,0 +1,52 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sat, 1 Feb 2025 19:17:31 +0300
Subject: [PATCH] Carpet-Fixes: RecipeManager Optimize
Original project: https://github.com/fxmorin/carpet-fixes
Optimized the RecipeManager getFirstMatch call to be up to 3x faster
This is a fully vanilla optimization. Improves: [Blast]Furnace/Campfire/Smoker/Stonecutter/Crafting/Sheep Color Choosing
This was mostly made for the auto crafting table, since the performance boost is much more visible while using that mod
diff --git a/net/minecraft/world/item/crafting/RecipeManager.java b/net/minecraft/world/item/crafting/RecipeManager.java
index f2c82217811712625df594667330a73f8f44e261..d39ff67faf89f34dcb41769b268c3dafc1b6305b 100644
--- a/net/minecraft/world/item/crafting/RecipeManager.java
+++ b/net/minecraft/world/item/crafting/RecipeManager.java
@@ -166,7 +166,7 @@ public class RecipeManager extends SimplePreparableReloadListener<RecipeMap> imp
public <I extends RecipeInput, T extends Recipe<I>> Optional<RecipeHolder<T>> getRecipeFor(RecipeType<T> recipeType, I input, Level level) {
// CraftBukkit start
- List<RecipeHolder<T>> list = this.recipes.getRecipesFor(recipeType, input, level).toList();
+ List<RecipeHolder<T>> list = this.recipes.getRecipesForList(recipeType, input, level); // DivineMC - Carpet-Fixes - Remove streams to be faster
return (list.isEmpty()) ? Optional.empty() : Optional.of(list.getLast()); // CraftBukkit - SPIGOT-4638: last recipe gets priority
// CraftBukkit end
}
diff --git a/net/minecraft/world/item/crafting/RecipeMap.java b/net/minecraft/world/item/crafting/RecipeMap.java
index 098753ddd215b6ef5915fac71d8c4f0b19cf4142..1778e58dca9430756d59d07bf017ebe4cc1f4ed4 100644
--- a/net/minecraft/world/item/crafting/RecipeMap.java
+++ b/net/minecraft/world/item/crafting/RecipeMap.java
@@ -75,4 +75,24 @@ public class RecipeMap {
public <I extends RecipeInput, T extends Recipe<I>> Stream<RecipeHolder<T>> getRecipesFor(RecipeType<T> type, I input, Level level) {
return input.isEmpty() ? Stream.empty() : this.byType(type).stream().filter(recipeHolder -> recipeHolder.value().matches(input, level));
}
+
+ // DivineMC start - Carpet-Fixes - Remove streams to be faster
+ public <I extends RecipeInput, T extends Recipe<I>> java.util.List<RecipeHolder<T>> getRecipesForList(RecipeType<T> type, I input, Level world) {
+ java.util.List<RecipeHolder<T>> list;
+
+ if (input.isEmpty()) {
+ return java.util.List.of();
+ } else {
+ list = new java.util.ArrayList<>();
+ }
+
+ for (RecipeHolder<T> recipeholder : this.byType(type)) {
+ if (recipeholder.value().matches(input, world)) {
+ list.add(recipeholder);
+ }
+ }
+
+ return list;
+ }
+ // DivineMC end - Carpet-Fixes - Remove streams to be faster
}

View File

@@ -0,0 +1,217 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sun, 6 Jul 2025 02:23:03 +0300
Subject: [PATCH] lithium: faster chunk serialization
This patch is based on the following mixins and classes:
* "net/caffeinemc/mods/lithium/common/world/chunk/CompactingPackedIntegerArray.java"
* "net/caffeinemc/mods/lithium/common/world/chunk/LithiumHashPalette.java"
* "net/caffeinemc/mods/lithium/mixin/chunk/serialization/SimpleBitStorageMixin.java"
* "net/caffeinemc/mods/lithium/mixin/chunk/serialization/PalettedContainerMixin.java"
By: Angeline <jellysquid3@users.noreply.github.com>
As part of: Lithium (https://github.com/CaffeineMC/lithium)
Licensed under: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html)
diff --git a/net/minecraft/util/BitStorage.java b/net/minecraft/util/BitStorage.java
index 02502d50f0255f5bbcc0ecb965abb48cc1a112da..89c65b8e4b99b78ec847f0ef958166a645ed4324 100644
--- a/net/minecraft/util/BitStorage.java
+++ b/net/minecraft/util/BitStorage.java
@@ -38,4 +38,6 @@ public interface BitStorage extends ca.spottedleaf.moonrise.patches.block_counti
return ret;
}
// Paper end - block counting
+
+ <T> void compact(net.minecraft.world.level.chunk.Palette<T> srcPalette, net.minecraft.world.level.chunk.Palette<T> dstPalette, short[] out, net.minecraft.world.level.chunk.PalettedContainer<T> resizeHandler); // DivineMC - lithium: faster chunk serialization
}
diff --git a/net/minecraft/util/SimpleBitStorage.java b/net/minecraft/util/SimpleBitStorage.java
index e6306a68c8652d4c5d22d5ecb1416f5f931f76ee..536cf90d2c4bccabe67bffde6bc4fa644a9e7d63 100644
--- a/net/minecraft/util/SimpleBitStorage.java
+++ b/net/minecraft/util/SimpleBitStorage.java
@@ -465,4 +465,44 @@ public class SimpleBitStorage implements BitStorage {
super(message);
}
}
+
+ // DivineMC start - lithium: faster chunk serialization
+ @Override
+ public <T> void compact(net.minecraft.world.level.chunk.Palette<T> srcPalette, net.minecraft.world.level.chunk.Palette<T> dstPalette, short[] out, net.minecraft.world.level.chunk.PalettedContainer<T> resizeHandler) {
+ if (this.size >= Short.MAX_VALUE) {
+ throw new IllegalStateException("Array too large");
+ }
+
+ if (this.size != out.length) {
+ throw new IllegalStateException("Array size mismatch");
+ }
+
+ short[] mappings = new short[(int) (this.mask + 1)];
+
+ int idx = 0;
+
+ for (long word : this.data) {
+ long bits = word;
+
+ for (int elementIdx = 0; elementIdx < this.valuesPerLong; ++elementIdx) {
+ int value = (int) (bits & this.mask);
+ int remappedId = mappings[value];
+
+ if (remappedId == 0) {
+ remappedId = dstPalette.idFor(srcPalette.valueFor(value), resizeHandler) + 1;
+ mappings[value] = (short) remappedId;
+ }
+
+ out[idx] = (short) (remappedId - 1);
+ bits >>= this.bits;
+
+ ++idx;
+
+ if (idx >= this.size) {
+ return;
+ }
+ }
+ }
+ }
+ // DivineMC end - lithium: faster chunk serialization
}
diff --git a/net/minecraft/util/ZeroBitStorage.java b/net/minecraft/util/ZeroBitStorage.java
index 09fd99c9cbd23b5f3c899bfb00c9b89651948ed8..6fdd51c767399bce29dce1ecea2e13072a6f4d00 100644
--- a/net/minecraft/util/ZeroBitStorage.java
+++ b/net/minecraft/util/ZeroBitStorage.java
@@ -80,4 +80,6 @@ public class ZeroBitStorage implements BitStorage {
return ret;
}
// Paper end - block counting
+
+ @Override public <T> void compact(net.minecraft.world.level.chunk.Palette<T> srcPalette, net.minecraft.world.level.chunk.Palette<T> dstPalette, short[] out, net.minecraft.world.level.chunk.PalettedContainer<T> resizeHandler) { } // DivineMC - lithium: faster chunk serialization
}
diff --git a/net/minecraft/world/level/chunk/PalettedContainer.java b/net/minecraft/world/level/chunk/PalettedContainer.java
index 1843046fe145bb23f2e9cf3269fb05c0fb617603..f1cae4cab36546d5798d2b59ac59774d1c9838c0 100644
--- a/net/minecraft/world/level/chunk/PalettedContainer.java
+++ b/net/minecraft/world/level/chunk/PalettedContainer.java
@@ -29,6 +29,23 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
private final T @org.jetbrains.annotations.Nullable [] presetValues; // Paper - Anti-Xray - Add preset values
//private final ThreadingDetector threadingDetector = new ThreadingDetector("PalettedContainer"); // Paper - unused
+ // DivineMC start - lithium: faster chunk serialization
+ private static final ThreadLocal<short[]> CACHED_ARRAY_4096 = ThreadLocal.withInitial(() -> new short[4096]);
+ private static final ThreadLocal<short[]> CACHED_ARRAY_64 = ThreadLocal.withInitial(() -> new short[64]);
+
+ private Optional<LongStream> asOptional(long[] data) {
+ return Optional.of(Arrays.stream(data));
+ }
+
+ private short[] getOrCreate(int size) {
+ return switch (size) {
+ case 64 -> CACHED_ARRAY_64.get();
+ case 4096 -> CACHED_ARRAY_4096.get();
+ default -> new short[size];
+ };
+ }
+ // DivineMC end - lithium: faster chunk serialization
+
public void acquire() {
// this.threadingDetector.checkAndLock(); // Paper - disable this - use proper synchronization
}
@@ -340,29 +357,49 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
public synchronized PalettedContainerRO.PackedData<T> pack(Strategy<T> strategy) { // Paper - synchronize
this.acquire();
- PalettedContainerRO.PackedData var14;
+ // DivineMC start - lithium: faster chunk serialization
+ Optional<LongStream> data = Optional.empty();
+ List<T> elements = null;
try {
- BitStorage bitStorage = this.data.storage;
- Palette<T> palette = this.data.palette;
- HashMapPalette<T> hashMapPalette = new HashMapPalette<>(bitStorage.getBits());
- int entryCount = strategy.entryCount();
- int[] ints = reencodeContents(bitStorage, palette, hashMapPalette);
- Configuration configurationForPaletteSize = strategy.getConfigurationForPaletteSize(hashMapPalette.getSize());
- int i = configurationForPaletteSize.bitsInStorage();
- Optional<LongStream> optional;
- if (i != 0) {
- SimpleBitStorage simpleBitStorage = new SimpleBitStorage(i, entryCount, ints);
- optional = Optional.of(Arrays.stream(simpleBitStorage.getRaw()));
- } else {
- optional = Optional.empty();
+ net.caffeinemc.mods.lithium.common.world.chunk.LithiumHashPalette<T> hashPalette = null;
+
+ final Palette<T> palette = this.data.palette();
+ final BitStorage storage = this.data.storage();
+ if (storage instanceof ZeroBitStorage || palette.getSize() == 1) {
+ elements = List.of(palette.valueFor(0));
+ } else if (palette instanceof net.caffeinemc.mods.lithium.common.world.chunk.LithiumHashPalette<T> lithiumHashPalette) {
+ hashPalette = lithiumHashPalette;
}
- var14 = new PalettedContainerRO.PackedData<>(hashMapPalette.getEntries(), optional, i);
+ if (elements == null) {
+ net.caffeinemc.mods.lithium.common.world.chunk.LithiumHashPalette<T> compactedPalette = new net.caffeinemc.mods.lithium.common.world.chunk.LithiumHashPalette<>(storage.getBits());
+ short[] array = this.getOrCreate(strategy.entryCount());
+
+ storage.compact(this.data.palette(), compactedPalette, array, this);
+
+ if (hashPalette != null && hashPalette.getSize() == compactedPalette.getSize() && storage.getBits() == strategy.getConfigurationForPaletteSize(hashPalette.getSize()).bitsInStorage()) { // paletteSize can de-sync from palette - see https://github.com/CaffeineMC/lithium-fabric/issues/279
+ data = this.asOptional(storage.getRaw().clone());
+ elements = hashPalette.getElements();
+ } else {
+ int bits = strategy.getConfigurationForPaletteSize(compactedPalette.getSize()).bitsInStorage();
+ if (bits != 0) {
+ SimpleBitStorage copy = new SimpleBitStorage(bits, array.length);
+ for (int i = 0; i < array.length; ++i) {
+ copy.set(i, array[i]);
+ }
+
+ data = this.asOptional(copy.getRaw());
+ }
+
+ elements = compactedPalette.getElements();
+ }
+ }
} finally {
this.release();
}
- return var14;
+ return new PalettedContainerRO.PackedData<>(elements, data);
+ // DivineMC end - lithium: faster chunk serialization
}
private static <T> int[] reencodeContents(BitStorage storage, Palette<T> oldPalette, Palette<T> newPalette) {
@@ -412,13 +449,31 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
@Override
public void count(PalettedContainer.CountConsumer<T> countConsumer) {
- if (this.data.palette.getSize() == 1) {
- countConsumer.accept(this.data.palette.valueFor(0), this.data.storage.getSize());
- } else {
- Int2IntOpenHashMap map = new Int2IntOpenHashMap();
- this.data.storage.getAll(i -> map.addTo(i, 1));
- map.int2IntEntrySet().forEach(entry -> countConsumer.accept(this.data.palette.valueFor(entry.getIntKey()), entry.getIntValue()));
+ // DivineMC start - lithium: faster chunk serialization
+ int len = this.data.palette().getSize();
+
+ if (len > 4096) {
+ if (this.data.palette.getSize() == 1) {
+ countConsumer.accept(this.data.palette.valueFor(0), this.data.storage.getSize());
+ } else {
+ Int2IntOpenHashMap map = new Int2IntOpenHashMap();
+ this.data.storage.getAll(id -> map.addTo(id, 1));
+ map.int2IntEntrySet().forEach(idEntry -> countConsumer.accept(this.data.palette.valueFor(idEntry.getIntKey()), idEntry.getIntValue()));
+ }
+ }
+
+ short[] counts = new short[len];
+
+ this.data.storage().getAll(i -> counts[i]++);
+
+ for (int i = 0; i < counts.length; i++) {
+ T obj = this.data.palette().valueFor(i);
+
+ if (obj != null) {
+ countConsumer.accept(obj, counts[i]);
+ }
}
+ // DivineMC end - lithium: faster chunk serialization
}
@FunctionalInterface

View File

@@ -0,0 +1,241 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sun, 6 Jul 2025 02:38:39 +0300
Subject: [PATCH] C2ME: optimize noise generation
This patch is based on the following mixins:
* "com/ishland/c2me/opts/math/mixin/MixinOctavePerlinNoiseSampler.java"
* "com/ishland/c2me/opts/math/mixin/MixinPerlinNoiseSampler.java"
By: ishland <ishlandmc@yeah.net>
As part of: C2ME (https://github.com/RelativityMC/C2ME-fabric)
Licensed under: MIT (https://opensource.org/licenses/MIT)
diff --git a/net/minecraft/world/level/levelgen/synth/ImprovedNoise.java b/net/minecraft/world/level/levelgen/synth/ImprovedNoise.java
index fb11a2eea540d55e50eab59f9857ca5d99f556f8..c40f65c30b6422a27154295a2b3a63483496dcca 100644
--- a/net/minecraft/world/level/levelgen/synth/ImprovedNoise.java
+++ b/net/minecraft/world/level/levelgen/synth/ImprovedNoise.java
@@ -11,6 +11,27 @@ public final class ImprovedNoise {
public final double yo;
public final double zo;
+ // DivineMC start - C2ME: optimize noise generation
+ private static final double[] FLAT_SIMPLEX_GRAD = new double[]{
+ 1, 1, 0, 0,
+ -1, 1, 0, 0,
+ 1, -1, 0, 0,
+ -1, -1, 0, 0,
+ 1, 0, 1, 0,
+ -1, 0, 1, 0,
+ 1, 0, -1, 0,
+ -1, 0, -1, 0,
+ 0, 1, 1, 0,
+ 0, -1, 1, 0,
+ 0, 1, -1, 0,
+ 0, -1, -1, 0,
+ 1, 1, 0, 0,
+ 0, -1, 1, 0,
+ -1, 1, 0, 0,
+ 0, -1, -1, 0,
+ };
+ // DivineMC end - C2ME: optimize noise generation
+
public ImprovedNoise(RandomSource random) {
this.xo = random.nextDouble() * 256.0;
this.yo = random.nextDouble() * 256.0;
@@ -38,9 +59,11 @@ public final class ImprovedNoise {
double d = x + this.xo;
double d1 = y + this.yo;
double d2 = z + this.zo;
- int floor = Mth.floor(d);
- int floor1 = Mth.floor(d1);
- int floor2 = Mth.floor(d2);
+ // DivineMC start - C2ME: optimize noise generation
+ double floor = Math.floor(d);
+ double floor1 = Math.floor(d1);
+ double floor2 = Math.floor(d2);
+ // DivineMC end - C2ME: optimize noise generation
double d3 = d - floor;
double d4 = d1 - floor1;
double d5 = d2 - floor2;
@@ -53,25 +76,27 @@ public final class ImprovedNoise {
d6 = d4;
}
- d7 = Mth.floor(d6 / yScale + 1.0E-7F) * yScale;
+ d7 = Math.floor(d6 / yScale + 1.0E-7F) * yScale; // DivineMC - C2ME: optimize noise generation
} else {
d7 = 0.0;
}
- return this.sampleAndLerp(floor, floor1, floor2, d3, d4 - d7, d5, d4);
+ return this.sampleAndLerp((int) floor, (int) floor1, (int) floor2, d3, d4 - d7, d5, d4); // DivineMC - C2ME: optimize noise generation
}
public double noiseWithDerivative(double x, double y, double z, double[] values) {
double d = x + this.xo;
double d1 = y + this.yo;
double d2 = z + this.zo;
- int floor = Mth.floor(d);
- int floor1 = Mth.floor(d1);
- int floor2 = Mth.floor(d2);
+ // DivineMC start - C2ME: optimize noise generation
+ double floor = Math.floor(d);
+ double floor1 = Math.floor(d1);
+ double floor2 = Math.floor(d2);
+ // DivineMC end - C2ME: optimize noise generation
double d3 = d - floor;
double d4 = d1 - floor1;
double d5 = d2 - floor2;
- return this.sampleWithDerivative(floor, floor1, floor2, d3, d4, d5, values);
+ return this.sampleWithDerivative((int) floor, (int) floor1, (int) floor2, d3, d4, d5, values); // DivineMC - C2ME: optimize noise generation
}
private static double gradDot(int gradIndex, double xFactor, double yFactor, double zFactor) {
@@ -83,24 +108,69 @@ public final class ImprovedNoise {
}
private double sampleAndLerp(int gridX, int gridY, int gridZ, double deltaX, double weirdDeltaY, double deltaZ, double deltaY) {
- int i = this.p(gridX);
- int i1 = this.p(gridX + 1);
- int i2 = this.p(i + gridY);
- int i3 = this.p(i + gridY + 1);
- int i4 = this.p(i1 + gridY);
- int i5 = this.p(i1 + gridY + 1);
- double d = gradDot(this.p(i2 + gridZ), deltaX, weirdDeltaY, deltaZ);
- double d1 = gradDot(this.p(i4 + gridZ), deltaX - 1.0, weirdDeltaY, deltaZ);
- double d2 = gradDot(this.p(i3 + gridZ), deltaX, weirdDeltaY - 1.0, deltaZ);
- double d3 = gradDot(this.p(i5 + gridZ), deltaX - 1.0, weirdDeltaY - 1.0, deltaZ);
- double d4 = gradDot(this.p(i2 + gridZ + 1), deltaX, weirdDeltaY, deltaZ - 1.0);
- double d5 = gradDot(this.p(i4 + gridZ + 1), deltaX - 1.0, weirdDeltaY, deltaZ - 1.0);
- double d6 = gradDot(this.p(i3 + gridZ + 1), deltaX, weirdDeltaY - 1.0, deltaZ - 1.0);
- double d7 = gradDot(this.p(i5 + gridZ + 1), deltaX - 1.0, weirdDeltaY - 1.0, deltaZ - 1.0);
- double d8 = Mth.smoothstep(deltaX);
- double d9 = Mth.smoothstep(deltaY);
- double d10 = Mth.smoothstep(deltaZ);
- return Mth.lerp3(d8, d9, d10, d, d1, d2, d3, d4, d5, d6, d7);
+ // DivineMC start - C2ME: optimize noise generation
+ final int var0 = gridX & 0xFF;
+ final int var1 = (gridX + 1) & 0xFF;
+ final int var2 = this.p[var0] & 0xFF;
+ final int var3 = this.p[var1] & 0xFF;
+ final int var4 = (var2 + gridY) & 0xFF;
+ final int var5 = (var3 + gridY) & 0xFF;
+ final int var6 = (var2 + gridY + 1) & 0xFF;
+ final int var7 = (var3 + gridY + 1) & 0xFF;
+ final int var8 = this.p[var4] & 0xFF;
+ final int var9 = this.p[var5] & 0xFF;
+ final int var10 = this.p[var6] & 0xFF;
+ final int var11 = this.p[var7] & 0xFF;
+
+ final int var12 = (var8 + gridZ) & 0xFF;
+ final int var13 = (var9 + gridZ) & 0xFF;
+ final int var14 = (var10 + gridZ) & 0xFF;
+ final int var15 = (var11 + gridZ) & 0xFF;
+ final int var16 = (var8 + gridZ + 1) & 0xFF;
+ final int var17 = (var9 + gridZ + 1) & 0xFF;
+ final int var18 = (var10 + gridZ + 1) & 0xFF;
+ final int var19 = (var11 + gridZ + 1) & 0xFF;
+ final int var20 = (this.p[var12] & 15) << 2;
+ final int var21 = (this.p[var13] & 15) << 2;
+ final int var22 = (this.p[var14] & 15) << 2;
+ final int var23 = (this.p[var15] & 15) << 2;
+ final int var24 = (this.p[var16] & 15) << 2;
+ final int var25 = (this.p[var17] & 15) << 2;
+ final int var26 = (this.p[var18] & 15) << 2;
+ final int var27 = (this.p[var19] & 15) << 2;
+ final double var60 = deltaX - 1.0;
+ final double var61 = weirdDeltaY - 1.0;
+ final double var62 = deltaZ - 1.0;
+ final double var87 = FLAT_SIMPLEX_GRAD[(var20) | 0] * deltaX + FLAT_SIMPLEX_GRAD[(var20) | 1] * weirdDeltaY + FLAT_SIMPLEX_GRAD[(var20) | 2] * deltaZ;
+ final double var88 = FLAT_SIMPLEX_GRAD[(var21) | 0] * var60 + FLAT_SIMPLEX_GRAD[(var21) | 1] * weirdDeltaY + FLAT_SIMPLEX_GRAD[(var21) | 2] * deltaZ;
+ final double var89 = FLAT_SIMPLEX_GRAD[(var22) | 0] * deltaX + FLAT_SIMPLEX_GRAD[(var22) | 1] * var61 + FLAT_SIMPLEX_GRAD[(var22) | 2] * deltaZ;
+ final double var90 = FLAT_SIMPLEX_GRAD[(var23) | 0] * var60 + FLAT_SIMPLEX_GRAD[(var23) | 1] * var61 + FLAT_SIMPLEX_GRAD[(var23) | 2] * deltaZ;
+ final double var91 = FLAT_SIMPLEX_GRAD[(var24) | 0] * deltaX + FLAT_SIMPLEX_GRAD[(var24) | 1] * weirdDeltaY + FLAT_SIMPLEX_GRAD[(var24) | 2] * var62;
+ final double var92 = FLAT_SIMPLEX_GRAD[(var25) | 0] * var60 + FLAT_SIMPLEX_GRAD[(var25) | 1] * weirdDeltaY + FLAT_SIMPLEX_GRAD[(var25) | 2] * var62;
+ final double var93 = FLAT_SIMPLEX_GRAD[(var26) | 0] * deltaX + FLAT_SIMPLEX_GRAD[(var26) | 1] * var61 + FLAT_SIMPLEX_GRAD[(var26) | 2] * var62;
+ final double var94 = FLAT_SIMPLEX_GRAD[(var27) | 0] * var60 + FLAT_SIMPLEX_GRAD[(var27) | 1] * var61 + FLAT_SIMPLEX_GRAD[(var27) | 2] * var62;
+
+ final double var95 = deltaX * 6.0 - 15.0;
+ final double var96 = deltaY * 6.0 - 15.0;
+ final double var97 = deltaZ * 6.0 - 15.0;
+ final double var98 = deltaX * var95 + 10.0;
+ final double var99 = deltaY * var96 + 10.0;
+ final double var100 = deltaZ * var97 + 10.0;
+ final double var101 = deltaX * deltaX * deltaX * var98;
+ final double var102 = deltaY * deltaY * deltaY * var99;
+ final double var103 = deltaZ * deltaZ * deltaZ * var100;
+
+ final double var113 = var87 + var101 * (var88 - var87);
+ final double var114 = var93 + var101 * (var94 - var93);
+ final double var115 = var91 + var101 * (var92 - var91);
+ final double var116 = var89 + var101 * (var90 - var89);
+ final double var117 = var114 - var115;
+ final double var118 = var102 * (var116 - var113);
+ final double var119 = var102 * var117;
+ final double var120 = var113 + var118;
+ final double var121 = var115 + var119;
+ return var120 + (var103 * (var121 - var120));
+ // DivineMC end - C2ME: optimize noise generation
}
private double sampleWithDerivative(int gridX, int gridY, int gridZ, double deltaX, double deltaY, double deltaZ, double[] noiseValues) {
diff --git a/net/minecraft/world/level/levelgen/synth/PerlinNoise.java b/net/minecraft/world/level/levelgen/synth/PerlinNoise.java
index da3c26fbad32d75d71f7e59c8c3341316a754756..f05902bb4b0d9e746d0033e73ec9f3b5626a53b5 100644
--- a/net/minecraft/world/level/levelgen/synth/PerlinNoise.java
+++ b/net/minecraft/world/level/levelgen/synth/PerlinNoise.java
@@ -26,6 +26,7 @@ public class PerlinNoise {
private final double lowestFreqValueFactor;
private final double lowestFreqInputFactor;
private final double maxValue;
+ private final double [] amplitudesArray; // DivineMC - C2ME: optimize noise generation
@Deprecated
public static PerlinNoise createLegacyForBlendedNoise(RandomSource random, IntStream octaves) {
@@ -127,6 +128,7 @@ public class PerlinNoise {
this.lowestFreqInputFactor = Math.pow(2.0, -i);
this.lowestFreqValueFactor = Math.pow(2.0, size - 1) / (Math.pow(2.0, size) - 1.0);
this.maxValue = this.edgeValue(2.0);
+ this.amplitudesArray = this.amplitudes.toDoubleArray(); // DivineMC - C2ME: optimize noise generation
}
protected double maxValue() {
@@ -138,7 +140,29 @@ public class PerlinNoise {
}
public double getValue(double x, double y, double z) {
- return this.getValue(x, y, z, 0.0, 0.0, false);
+ // DivineMC start - C2ME: optimize noise generation
+ double d = 0.0;
+ double e = this.lowestFreqInputFactor;
+ double f = this.lowestFreqValueFactor;
+
+ final int length = this.noiseLevels.length;
+
+ for (int i = 0; i < length; i++) {
+ ImprovedNoise perlinNoiseSampler = this.noiseLevels[i];
+ if (perlinNoiseSampler != null) {
+ @SuppressWarnings("deprecation")
+ double g = perlinNoiseSampler.noise(
+ wrap(x * e), wrap(y * e), wrap(z * e), 0.0, 0.0
+ );
+ d += this.amplitudesArray[i] * g * f;
+ }
+
+ e *= 2.0;
+ f /= 2.0;
+ }
+
+ return d;
+ // DivineMC end - C2ME: optimize noise generation
}
@Deprecated
@@ -187,7 +211,7 @@ public class PerlinNoise {
}
public static double wrap(double value) {
- return value - Mth.lfloor(value / 3.3554432E7 + 0.5) * 3.3554432E7;
+ return value - Math.floor(value / 3.3554432E7 + 0.5) * 3.3554432E7; // DivineMC - C2ME: optimize noise generation
}
protected int firstOctave() {

View File

@@ -0,0 +1,48 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sun, 6 Jul 2025 02:59:37 +0300
Subject: [PATCH] Use Java's Math functions
diff --git a/net/minecraft/util/Mth.java b/net/minecraft/util/Mth.java
index 0713d164851216483247da09ff7901f8bfbe2eb9..ed46863a244f4e23610a918f0692390130eaadaf 100644
--- a/net/minecraft/util/Mth.java
+++ b/net/minecraft/util/Mth.java
@@ -58,18 +58,15 @@ public class Mth {
}
public static int floor(float value) {
- int i = (int)value;
- return value < i ? i - 1 : i;
+ return (int) Math.floor(value); // DivineMC - Use Java's Math functions
}
public static int floor(double value) {
- int i = (int)value;
- return value < i ? i - 1 : i;
+ return (int) Math.floor(value); // DivineMC - Use Java's Math functions
}
public static long lfloor(double value) {
- long l = (long)value;
- return value < l ? l - 1L : l;
+ return (long) Math.floor(value); // DivineMC - Use Java's Math functions
}
public static float abs(float value) {
@@ -81,13 +78,11 @@ public class Mth {
}
public static int ceil(float value) {
- int i = (int)value;
- return value > i ? i + 1 : i;
+ return (int) Math.ceil(value); // DivineMC - Use Java's Math functions
}
public static int ceil(double value) {
- int i = (int)value;
- return value > i ? i + 1 : i;
+ return (int) Math.ceil(value); // DivineMC - Use Java's Math functions
}
public static long ceilLong(double value) {

View File

@@ -0,0 +1,42 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sun, 6 Jul 2025 03:03:04 +0300
Subject: [PATCH] Disable leaf decay
diff --git a/net/minecraft/world/level/block/LeavesBlock.java b/net/minecraft/world/level/block/LeavesBlock.java
index 010e9814490ffaa153df5b7865da17e2a84c7e82..f43dbc0fc05f549521490595fe594c424b2e8a87 100644
--- a/net/minecraft/world/level/block/LeavesBlock.java
+++ b/net/minecraft/world/level/block/LeavesBlock.java
@@ -70,12 +70,29 @@ public abstract class LeavesBlock extends Block implements SimpleWaterloggedBloc
}
protected boolean decaying(BlockState state) {
- return !state.getValue(PERSISTENT) && state.getValue(DISTANCE) == 7;
+ return !org.bxteam.divinemc.config.DivineConfig.FixesCategory.disableLeafDecay && !state.getValue(PERSISTENT) && state.getValue(DISTANCE) == 7; // DivineMC - Disable leaf decay
}
@Override
protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
- level.setBlock(pos, updateDistance(state, level, pos), 3);
+ // DivineMC start - Disable leaf decay
+ if (org.bxteam.divinemc.config.DivineConfig.FixesCategory.disableLeafDecay) return; // DivineMC - Disable leaf decay
+ int newValue = 7;
+ int oldValue = state.getValue(DISTANCE);
+ BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
+
+ for (Direction direction : Direction.values()) {
+ mutable.setWithOffset(pos, direction);
+ newValue = Math.min(newValue, getDistanceAt(level.getBlockState(mutable)) + 1);
+ if (newValue == 1) {
+ break;
+ }
+ }
+
+ if (newValue != oldValue) {
+ level.setBlock(pos, state.setValue(DISTANCE, newValue), 3);
+ }
+ // DivineMC end - Disable leaf decay
}
@Override

View File

@@ -0,0 +1,651 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sat, 1 Feb 2025 16:05:57 +0300
Subject: [PATCH] Optimize entities
diff --git a/net/minecraft/world/entity/AgeableMob.java b/net/minecraft/world/entity/AgeableMob.java
index 065553a549e54492e8fe42fe7dc61d11ac8da6ef..402955e7042f01bdb8296a43bcca52dbb3a224c1 100644
--- a/net/minecraft/world/entity/AgeableMob.java
+++ b/net/minecraft/world/entity/AgeableMob.java
@@ -126,6 +126,16 @@ public abstract class AgeableMob extends PathfinderMob {
public void onSyncedDataUpdated(EntityDataAccessor<?> key) {
if (DATA_BABY_ID.equals(key)) {
this.refreshDimensions();
+ // DivineMC start - Optimize entities
+ if (isBaby()) {
+ org.bxteam.divinemc.util.entity.SensorHelper.enableSensor(this, net.minecraft.world.entity.ai.sensing.SensorType.NEAREST_ADULT, true);
+ } else {
+ org.bxteam.divinemc.util.entity.SensorHelper.disableSensor(this, net.minecraft.world.entity.ai.sensing.SensorType.NEAREST_ADULT);
+ if (this.getBrain().hasMemoryValue(net.minecraft.world.entity.ai.memory.MemoryModuleType.NEAREST_VISIBLE_ADULT)) {
+ this.getBrain().setMemory(net.minecraft.world.entity.ai.memory.MemoryModuleType.NEAREST_VISIBLE_ADULT, java.util.Optional.empty());
+ }
+ }
+ // DivineMC end - Optimize entities
}
super.onSyncedDataUpdated(key);
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 79d224baca372cb1b6b89f7fcb63c5fe3707adb5..b5004de3ca8951a5884a3b62b129aa2588a67af7 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -1712,6 +1712,10 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name
private int checkInsideBlocks(Vec3 from, Vec3 to, InsideBlockEffectApplier.StepBasedCollector stepBasedCollector, LongSet visited, int maxSteps) {
AABB aabb = this.makeBoundingBox(to).deflate(1.0E-5F);
+ // DivineMC start - Optimize entities
+ final net.minecraft.world.level.chunk.ChunkAccess[] cachedChunk = new net.minecraft.world.level.chunk.ChunkAccess[] { null };
+ final long[] lastChunkPos = { Long.MIN_VALUE };
+ // DivineMC end - Optimize entities
boolean flag = from.distanceToSqr(to) > Mth.square(0.9999900000002526);
boolean flag1 = this.level instanceof ServerLevel serverLevel
&& serverLevel.getServer().debugSubscribers().hasAnySubscriberFor(DebugSubscriptions.ENTITY_BLOCK_INTERSECTIONS);
@@ -1727,7 +1731,21 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name
return false;
} else {
atomicInteger.set(index);
- BlockState blockState = this.level().getBlockState(pos);
+ // DivineMC start - Optimize entities
+ final int chunkX = pos.getX() >> 4;
+ final int chunkZ = pos.getZ() >> 4;
+ final long chunkLongPos = ((long) chunkZ << 32) | (chunkX & 0xFFFFFFFFL);
+ if (lastChunkPos[0] != chunkLongPos) {
+ // update cache, this is a different chunk than previous
+ lastChunkPos[0] = chunkLongPos;
+ cachedChunk[0] = this.level.getChunkIfLoaded(chunkX, chunkZ);
+ }
+ net.minecraft.world.level.chunk.ChunkAccess chunk = cachedChunk[0];
+ if (chunk == null) {
+ return true;
+ }
+ BlockState blockState = chunk.getBlockState(pos);
+ // DivineMC end - Optimize entities
if (blockState.isAir()) {
if (flag1) {
this.debugBlockIntersection((ServerLevel)this.level(), pos.immutable(), false, false);
diff --git a/net/minecraft/world/entity/InsideBlockEffectApplier.java b/net/minecraft/world/entity/InsideBlockEffectApplier.java
index a7bc5ead2062504ceac95f603bc1ca8d4290bbfd..533c790ce305043f53c76f03a12676c567118a44 100644
--- a/net/minecraft/world/entity/InsideBlockEffectApplier.java
+++ b/net/minecraft/world/entity/InsideBlockEffectApplier.java
@@ -31,68 +31,115 @@ public interface InsideBlockEffectApplier {
public static class StepBasedCollector implements InsideBlockEffectApplier {
private static final InsideBlockEffectType[] APPLY_ORDER = InsideBlockEffectType.values();
- private static final int NO_STEP = -1;
- private final Map<InsideBlockEffectType, Consumer<Entity>> effectsInStep = new java.util.EnumMap<>(InsideBlockEffectType.class); // Paper - track position inside effect was triggered on
- private final Map<InsideBlockEffectType, List<Consumer<Entity>>> beforeEffectsInStep = Util.makeEnumMap(
- InsideBlockEffectType.class, insideBlockEffectType -> new ArrayList<>()
- );
- private final Map<InsideBlockEffectType, List<Consumer<Entity>>> afterEffectsInStep = Util.makeEnumMap(
- InsideBlockEffectType.class, insideBlockEffectType -> new ArrayList<>()
- );
- private final List<Consumer<Entity>> finalEffects = new ArrayList<>();
+ // DivineMC start - Optimize entities
+ private final Consumer<Entity>[] effectsInStep = new Consumer[APPLY_ORDER.length];
+ private final it.unimi.dsi.fastutil.objects.ObjectArrayList<Consumer<Entity>>[] beforeEffectsInStep = new it.unimi.dsi.fastutil.objects.ObjectArrayList[APPLY_ORDER.length];
+ private final it.unimi.dsi.fastutil.objects.ObjectArrayList<Consumer<Entity>>[] afterEffectsInStep = new it.unimi.dsi.fastutil.objects.ObjectArrayList[APPLY_ORDER.length];
+ private final it.unimi.dsi.fastutil.objects.ObjectArrayList<Consumer<Entity>> finalEffects = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>();
+ // DivineMC end - Optimize entities
private int lastStep = -1;
- public void advanceStep(int step, net.minecraft.core.BlockPos pos) { // Paper - track position inside effect was triggered on
- this.currentBlockPos = pos; // Paper - track position inside effect was triggered on
+ // DivineMC start - Optimize entities
+ public StepBasedCollector() {
+ for (int i = 0; i < APPLY_ORDER.length; i++) {
+ beforeEffectsInStep[i] = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(2);
+ afterEffectsInStep[i] = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(2);
+ }
+ }
+
+ public void advanceStep(int step, net.minecraft.core.BlockPos pos) {
+ this.currentBlockPos = pos;
if (this.lastStep != step) {
this.lastStep = step;
this.flushStep();
}
}
+ // DivineMC end - Optimize entities
public void applyAndClear(Entity entity) {
this.flushStep();
- for (Consumer<Entity> consumer : this.finalEffects) {
+ // DivineMC start - Optimize entities
+ List<Consumer<Entity>> effects = this.finalEffects;
+ int size = effects.size();
+
+ if (size == 0) {
+ this.lastStep = -1;
+ return;
+ }
+
+ if (!entity.isAlive()) {
+ effects.clear();
+ this.lastStep = -1;
+ return;
+ }
+
+ int i = 0;
+ while (i < size - 3) {
+ effects.get(i++).accept(entity);
+ effects.get(i++).accept(entity);
+ effects.get(i++).accept(entity);
+ effects.get(i++).accept(entity);
if (!entity.isAlive()) {
break;
}
+ }
- consumer.accept(entity);
+ if (entity.isAlive()) {
+ for (; i < size; i++) {
+ effects.get(i).accept(entity);
+ if (!entity.isAlive()) {
+ break;
+ }
+ }
}
- this.finalEffects.clear();
+ effects.clear();
+ // DivineMC end - Optimize entities
this.lastStep = -1;
}
private void flushStep() {
- for (InsideBlockEffectType insideBlockEffectType : APPLY_ORDER) {
- List<Consumer<Entity>> list = this.beforeEffectsInStep.get(insideBlockEffectType);
- this.finalEffects.addAll(list);
- list.clear();
- if (this.effectsInStep.remove(insideBlockEffectType) instanceof final Consumer<Entity> recordedEffect) { // Paper - track position inside effect was triggered on - better than null check to avoid diff.
- this.finalEffects.add(recordedEffect); // Paper - track position inside effect was triggered on
+ // DivineMC start - Optimize entities
+ final int len = APPLY_ORDER.length;
+ final Consumer<Entity>[] effectArr = this.effectsInStep;
+ final List<Consumer<Entity>> finalList = this.finalEffects;
+
+ for (int i = 0; i < len; i++) {
+ List<Consumer<Entity>> beforeList = this.beforeEffectsInStep[i];
+ if (!beforeList.isEmpty()) {
+ finalList.addAll(beforeList);
+ beforeList.clear();
}
- List<Consumer<Entity>> list1 = this.afterEffectsInStep.get(insideBlockEffectType);
- this.finalEffects.addAll(list1);
- list1.clear();
+ Consumer<Entity> effect = effectArr[i];
+ if (effect != null) {
+ finalList.add(effect);
+ effectArr[i] = null;
+ }
+
+ List<Consumer<Entity>> afterList = this.afterEffectsInStep[i];
+ if (!afterList.isEmpty()) {
+ finalList.addAll(afterList);
+ afterList.clear();
+ }
}
+ // DivineMC end - Optimize entities
}
@Override
public void apply(InsideBlockEffectType type) {
- this.effectsInStep.put(type, recorded(type)); // Paper - track position inside effect was triggered on
+ effectsInStep[type.ordinal()] = recorded(type); // DivineMC - Optimize entities
}
@Override
public void runBefore(InsideBlockEffectType type, Consumer<Entity> effect) {
- this.beforeEffectsInStep.get(type).add(effect);
+ beforeEffectsInStep[type.ordinal()].add(effect); // DivineMC - Optimize entities
}
@Override
public void runAfter(InsideBlockEffectType type, Consumer<Entity> effect) {
- this.afterEffectsInStep.get(type).add(effect);
+ afterEffectsInStep[type.ordinal()].add(effect); // DivineMC - Optimize entities
}
// Paper start - track position inside effect was triggered on
diff --git a/net/minecraft/world/entity/ai/Brain.java b/net/minecraft/world/entity/ai/Brain.java
index 9ccddcb9a3df8fd9e769069b4502c4422872c562..a05df4dfa6b5078558ef9d6603297298a3e10c3d 100644
--- a/net/minecraft/world/entity/ai/Brain.java
+++ b/net/minecraft/world/entity/ai/Brain.java
@@ -45,16 +45,75 @@ public class Brain<E extends LivingEntity> {
static final Logger LOGGER = LogUtils.getLogger();
private final Supplier<Codec<Brain<E>>> codec;
private static final int SCHEDULE_UPDATE_DELAY = 20;
- private final Map<MemoryModuleType<?>, Optional<? extends ExpirableValue<?>>> memories = Maps.newHashMap();
- public final Map<SensorType<? extends Sensor<? super E>>, Sensor<? super E>> sensors = Maps.newLinkedHashMap();
+ // DivineMC start - Optimize entities
+ private Map<MemoryModuleType<?>, Optional<? extends ExpirableValue<?>>> memories = Maps.newConcurrentMap();
+ public Map<SensorType<? extends Sensor<? super E>>, Sensor<? super E>> sensors = Maps.newLinkedHashMap();
private final Map<Integer, Map<Activity, Set<BehaviorControl<? super E>>>> availableBehaviorsByPriority = Maps.newTreeMap();
private Schedule schedule = Schedule.EMPTY;
- private final Map<Activity, Set<Pair<MemoryModuleType<?>, MemoryStatus>>> activityRequirements = Maps.newHashMap();
+ private Map<Activity, Set<Pair<MemoryModuleType<?>, MemoryStatus>>> activityRequirements = Maps.newHashMap();
+ // DivineMC end - Optimize entities
private final Map<Activity, Set<MemoryModuleType<?>>> activityMemoriesToEraseWhenStopped = Maps.newHashMap();
private Set<Activity> coreActivities = Sets.newHashSet();
private final Set<Activity> activeActivities = Sets.newHashSet();
private Activity defaultActivity = Activity.IDLE;
private long lastScheduleUpdate = -9999L;
+ // DivineMC start - Optimize entities
+ private java.util.ArrayList<net.minecraft.world.entity.ai.behavior.BehaviorControl<? super E>> possibleTasks;
+ private org.bxteam.divinemc.util.collections.MaskedList<net.minecraft.world.entity.ai.behavior.BehaviorControl<? super E>> runningTasks;
+
+ private void onTasksChanged() {
+ this.runningTasks = null;
+ this.onPossibleActivitiesChanged();
+ }
+
+ private void onPossibleActivitiesChanged() {
+ this.possibleTasks = null;
+ }
+
+ private void initPossibleTasks() {
+ this.possibleTasks = new java.util.ArrayList<>();
+ for (Map<Activity, Set<BehaviorControl<? super E>>> map : this.availableBehaviorsByPriority.values()) {
+ for (Map.Entry<Activity, Set<BehaviorControl<? super E>>> entry : map.entrySet()) {
+ Activity activity = entry.getKey();
+ if (!this.activeActivities.contains(activity)) {
+ continue;
+ }
+ Set<BehaviorControl<? super E>> set = entry.getValue();
+ for (BehaviorControl<? super E> task : set) {
+ //noinspection UseBulkOperation
+ this.possibleTasks.add(task);
+ }
+ }
+ }
+ }
+
+ private java.util.ArrayList<net.minecraft.world.entity.ai.behavior.BehaviorControl<? super E>> getPossibleTasks() {
+ if (this.possibleTasks == null) {
+ this.initPossibleTasks();
+ }
+ return this.possibleTasks;
+ }
+
+ private org.bxteam.divinemc.util.collections.MaskedList<net.minecraft.world.entity.ai.behavior.BehaviorControl<? super E>> getCurrentlyRunningTasks() {
+ if (this.runningTasks == null) {
+ this.initCurrentlyRunningTasks();
+ }
+ return this.runningTasks;
+ }
+
+ private void initCurrentlyRunningTasks() {
+ org.bxteam.divinemc.util.collections.MaskedList<net.minecraft.world.entity.ai.behavior.BehaviorControl<? super E>> list = new org.bxteam.divinemc.util.collections.MaskedList<>(new ObjectArrayList<>(), false);
+
+ for (Map<Activity, Set<BehaviorControl<? super E>>> map : this.availableBehaviorsByPriority.values()) {
+ for (Set<BehaviorControl<? super E>> set : map.values()) {
+ for (BehaviorControl<? super E> task : set) {
+ list.addOrSet(task, task.getStatus() == Behavior.Status.RUNNING);
+ }
+ }
+ }
+ this.runningTasks = list;
+ }
+ // DivineMC end - Optimize entities
public static <E extends LivingEntity> Brain.Provider<E> provider(
Collection<? extends MemoryModuleType<?>> memoryTypes, Collection<? extends SensorType<? extends Sensor<? super E>>> sensorTypes
@@ -146,6 +205,12 @@ public class Brain<E extends LivingEntity> {
for (Brain.MemoryValue<?> memoryValue : memoryValues) {
memoryValue.setMemoryInternal(this);
}
+ // DivineMC start - Optimize entities
+ this.onTasksChanged();
+ this.memories = new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(this.memories);
+ this.sensors = new it.unimi.dsi.fastutil.objects.Reference2ReferenceLinkedOpenHashMap<>(this.sensors);
+ this.activityRequirements = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(this.activityRequirements);
+ // DivineMC end - Optimize entities
}
public <T> DataResult<T> serializeStart(DynamicOps<T> ops) {
@@ -165,6 +230,7 @@ public class Brain<E extends LivingEntity> {
}
public <U> void eraseMemory(MemoryModuleType<U> type) {
+ if (!this.memories.containsKey(type)) return; // DivineMC - Optimize entities
this.setMemory(type, Optional.empty());
}
@@ -180,16 +246,33 @@ public class Brain<E extends LivingEntity> {
this.setMemoryInternal(memoryType, memory.map(ExpirableValue::of));
}
+ // DivineMC start - Optimize entities
<U> void setMemoryInternal(MemoryModuleType<U> memoryType, Optional<? extends ExpirableValue<?>> memory) {
+ if (memory.isPresent() && this.isEmptyCollection(memory.get().getValue())) {
+ this.eraseMemory(memoryType);
+ return;
+ }
+
if (this.memories.containsKey(memoryType)) {
- if (memory.isPresent() && this.isEmptyCollection(memory.get().getValue())) {
- this.eraseMemory(memoryType);
- } else {
- this.memories.put(memoryType, memory);
- }
+ this.increaseMemoryModificationCount(this.memories, memoryType, memory);
}
}
+ private long memoryModCount = 1;
+
+ public long getMemoryModCount() {
+ return memoryModCount;
+ }
+
+ private <T, A> Object increaseMemoryModificationCount(Map<T, A> map, T key, A newValue) {
+ Object oldValue = map.put(key, newValue);
+ if (oldValue == null || ((Optional<?>) oldValue).isPresent() != ((Optional<?>) newValue).isPresent()) {
+ this.memoryModCount++;
+ }
+ return oldValue;
+ }
+ // DivineMC end - Optimize entities
+
public <U> Optional<U> getMemory(MemoryModuleType<U> type) {
Optional<? extends ExpirableValue<?>> optional = this.memories.get(type);
if (optional == null) {
@@ -251,19 +334,7 @@ public class Brain<E extends LivingEntity> {
@Deprecated
@VisibleForDebug
public List<BehaviorControl<? super E>> getRunningBehaviors() {
- List<BehaviorControl<? super E>> list = new ObjectArrayList<>();
-
- for (Map<Activity, Set<BehaviorControl<? super E>>> map : this.availableBehaviorsByPriority.values()) {
- for (Set<BehaviorControl<? super E>> set : map.values()) {
- for (BehaviorControl<? super E> behaviorControl : set) {
- if (behaviorControl.getStatus() == Behavior.Status.RUNNING) {
- list.add(behaviorControl);
- }
- }
- }
- }
-
- return list;
+ return this.getCurrentlyRunningTasks(); // DivineMC - Optimize entities
}
public void useDefaultActivity() {
@@ -294,6 +365,7 @@ public class Brain<E extends LivingEntity> {
this.activeActivities.clear();
this.activeActivities.addAll(this.coreActivities);
this.activeActivities.add(activity);
+ this.onPossibleActivitiesChanged(); // DivineMC - Optimize entities
}
}
@@ -383,11 +455,13 @@ public class Brain<E extends LivingEntity> {
.computeIfAbsent(activity, activity1 -> Sets.newLinkedHashSet())
.add((BehaviorControl<? super E>)pair.getSecond());
}
+ this.onTasksChanged(); // DivineMC - Optimize entities
}
@VisibleForTesting
public void removeAllBehaviors() {
this.availableBehaviorsByPriority.clear();
+ this.onTasksChanged(); // DivineMC - Optimize entities
}
public boolean isActive(Activity activity) {
@@ -404,6 +478,7 @@ public class Brain<E extends LivingEntity> {
}
}
+ brain.memoryModCount = this.memoryModCount + 1; // DivineMC - Optimize entities
return brain;
}
@@ -438,31 +513,38 @@ public class Brain<E extends LivingEntity> {
for (BehaviorControl<? super E> behaviorControl : this.getRunningBehaviors()) {
behaviorControl.doStop(level, owner, gameTime);
+ // DivineMC start - Optimize entities
+ if (this.runningTasks != null) {
+ this.runningTasks.setVisible(behaviorControl, false);
+ }
+ // DivineMC end - Optimize entities
}
}
+ // DivineMC start - Optimize entities
private void startEachNonRunningBehavior(ServerLevel level, E entity) {
- long gameTime = level.getGameTime();
-
- for (Map<Activity, Set<BehaviorControl<? super E>>> map : this.availableBehaviorsByPriority.values()) {
- for (Entry<Activity, Set<BehaviorControl<? super E>>> entry : map.entrySet()) {
- Activity activity = entry.getKey();
- if (this.activeActivities.contains(activity)) {
- for (BehaviorControl<? super E> behaviorControl : entry.getValue()) {
- if (behaviorControl.getStatus() == Behavior.Status.STOPPED) {
- behaviorControl.tryStart(level, entity, gameTime);
- }
- }
+ long startTime = level.getGameTime();
+ for (BehaviorControl<? super E> task : this.getPossibleTasks()) {
+ if (task.getStatus() == Behavior.Status.STOPPED) {
+ task.tryStart(level, entity, startTime);
+ if (this.runningTasks != null && task.getStatus() == Behavior.Status.RUNNING) {
+ this.runningTasks.setVisible(task, true);
}
}
}
}
+ // DivineMC end - Optimize entities
private void tickEachRunningBehavior(ServerLevel level, E entity) {
long gameTime = level.getGameTime();
for (BehaviorControl<? super E> behaviorControl : this.getRunningBehaviors()) {
behaviorControl.tickOrStop(level, entity, gameTime);
+ // DivineMC start - Optimize entities
+ if (this.runningTasks != null && behaviorControl.getStatus() != Behavior.Status.RUNNING) {
+ this.runningTasks.setVisible(behaviorControl, false);
+ }
+ // DivineMC end - Optimize entities
}
}
diff --git a/net/minecraft/world/entity/ai/behavior/Behavior.java b/net/minecraft/world/entity/ai/behavior/Behavior.java
index 5b0cadd2544fb2a627822e645ff32fec2e9cfda9..02508662c722a515cfd78f872c8ba8bbffd8b6fb 100644
--- a/net/minecraft/world/entity/ai/behavior/Behavior.java
+++ b/net/minecraft/world/entity/ai/behavior/Behavior.java
@@ -14,6 +14,10 @@ public abstract class Behavior<E extends LivingEntity> implements BehaviorContro
private long endTimestamp;
private final int minDuration;
private final int maxDuration;
+ // DivineMC start - Optimize entities
+ private long cachedMemoryModCount = -1;
+ private boolean cachedHasRequiredMemoryState;
+ // DivineMC end - Optimize entities
private final String configKey; // Paper - configurable behavior tick rate and timings
public Behavior(Map<MemoryModuleType<?>, MemoryStatus> entryCondition) {
@@ -27,7 +31,7 @@ public abstract class Behavior<E extends LivingEntity> implements BehaviorContro
public Behavior(Map<MemoryModuleType<?>, MemoryStatus> entryCondition, int minDuration, int maxDuration) {
this.minDuration = minDuration;
this.maxDuration = maxDuration;
- this.entryCondition = entryCondition;
+ this.entryCondition = new it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap<>(entryCondition); // DivineMC - Optimize entities - Use fastutil
// Paper start - configurable behavior tick rate and timings
String key = io.papermc.paper.util.MappingEnvironment.reobf() ? io.papermc.paper.util.ObfHelper.INSTANCE.deobfClassName(this.getClass().getName()) : this.getClass().getName();
int lastSeparator = key.lastIndexOf('.');
@@ -103,17 +107,26 @@ public abstract class Behavior<E extends LivingEntity> implements BehaviorContro
return this.getClass().getSimpleName();
}
- protected boolean hasRequiredMemories(E owner) {
- for (Entry<MemoryModuleType<?>, MemoryStatus> entry : this.entryCondition.entrySet()) {
- MemoryModuleType<?> memoryModuleType = entry.getKey();
- MemoryStatus memoryStatus = entry.getValue();
- if (!owner.getBrain().checkMemory(memoryModuleType, memoryStatus)) {
- return false;
+ // DivineMC start - Optimize entities
+ public boolean hasRequiredMemories(E entity) {
+ net.minecraft.world.entity.ai.Brain<?> brain = entity.getBrain();
+ long modCount = brain.getMemoryModCount();
+ if (this.cachedMemoryModCount == modCount) {
+ return this.cachedHasRequiredMemoryState;
+ }
+ this.cachedMemoryModCount = modCount;
+
+ it.unimi.dsi.fastutil.objects.ObjectIterator<it.unimi.dsi.fastutil.objects.Reference2ObjectMap.Entry<net.minecraft.world.entity.ai.memory.MemoryModuleType<?>, net.minecraft.world.entity.ai.memory.MemoryStatus>> fastIterator = ((it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap<net.minecraft.world.entity.ai.memory.MemoryModuleType<?>, net.minecraft.world.entity.ai.memory.MemoryStatus>) this.entryCondition).reference2ObjectEntrySet().fastIterator();
+ while (fastIterator.hasNext()) {
+ it.unimi.dsi.fastutil.objects.Reference2ObjectMap.Entry<MemoryModuleType<?>, MemoryStatus> entry = fastIterator.next();
+ if (!brain.checkMemory(entry.getKey(), entry.getValue())) {
+ return this.cachedHasRequiredMemoryState = false;
}
}
- return true;
+ return this.cachedHasRequiredMemoryState = true;
}
+ // DivineMC end - Optimize entities
public static enum Status {
STOPPED,
diff --git a/net/minecraft/world/entity/ai/behavior/LongJumpToRandomPos.java b/net/minecraft/world/entity/ai/behavior/LongJumpToRandomPos.java
index 977afa268838304abdb34be253ca36ac1c22e99f..9fdb2ce7b72d0a10c2148027990a0048ca93f976 100644
--- a/net/minecraft/world/entity/ai/behavior/LongJumpToRandomPos.java
+++ b/net/minecraft/world/entity/ai/behavior/LongJumpToRandomPos.java
@@ -119,6 +119,12 @@ public class LongJumpToRandomPos<E extends Mob> extends Behavior<E> {
int x = blockPos.getX();
int y = blockPos.getY();
int z = blockPos.getZ();
+ // DivineMC start - Optimize entities
+ if (this.maxLongJumpWidth < 128 && this.maxLongJumpHeight < 128) {
+ this.jumpCandidates = org.bxteam.divinemc.util.collections.LongJumpChoiceList.forCenter(blockPos, (byte) this.maxLongJumpWidth, (byte) this.maxLongJumpHeight);
+ return;
+ }
+ // DivineMC end - Optimize entities
this.jumpCandidates = BlockPos.betweenClosedStream(
x - this.maxLongJumpWidth,
y - this.maxLongJumpHeight,
@@ -174,12 +180,25 @@ public class LongJumpToRandomPos<E extends Mob> extends Behavior<E> {
}
}
+ // DivineMC start - Optimize entities
protected Optional<LongJumpToRandomPos.PossibleJump> getJumpCandidate(ServerLevel level) {
- Optional<LongJumpToRandomPos.PossibleJump> randomItem = WeightedRandom.getRandomItem(
- level.random, this.jumpCandidates, LongJumpToRandomPos.PossibleJump::weight
- );
- randomItem.ifPresent(this.jumpCandidates::remove);
- return randomItem;
+ Optional<LongJumpToRandomPos.PossibleJump> optional = getRandomFast(level.random, this.jumpCandidates);
+ skipRemoveIfAlreadyRemoved(optional, this.jumpCandidates::remove);
+ return optional;
+ }
+
+ private Optional<LongJumpToRandomPos.PossibleJump> getRandomFast(net.minecraft.util.RandomSource random, List<LongJumpToRandomPos.PossibleJump> pool) {
+ if (pool instanceof org.bxteam.divinemc.util.collections.LongJumpChoiceList longJumpChoiceList) {
+ return Optional.ofNullable(longJumpChoiceList.removeRandomWeightedByDistanceSq(random));
+ } else {
+ return WeightedRandom.getRandomItem(random, pool, LongJumpToRandomPos.PossibleJump::weight);
+ }
+ }
+
+ private void skipRemoveIfAlreadyRemoved(Optional<LongJumpToRandomPos.PossibleJump> result, java.util.function.Consumer<? super net.minecraft.world.entity.ai.behavior.LongJumpToRandomPos.PossibleJump> removeAction) {
+ if (!(this.jumpCandidates instanceof org.bxteam.divinemc.util.collections.LongJumpChoiceList)) {
+ result.ifPresent(removeAction);
+ }
}
private boolean isAcceptableLandingPosition(ServerLevel level, E entity, BlockPos pos) {
diff --git a/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java b/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java
index b0c5e41fefc7c9adf1a61bd5b52861736657d37e..dfc62772d5617f0dce72b45a1bebf1b2f051efd5 100644
--- a/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java
+++ b/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java
@@ -17,10 +17,10 @@ public class NearestLivingEntitySensor<T extends LivingEntity> extends Sensor<T>
protected void doTick(ServerLevel level, T entity) {
double attributeValue = entity.getAttributeValue(Attributes.FOLLOW_RANGE);
AABB aabb = entity.getBoundingBox().inflate(attributeValue, attributeValue, attributeValue);
- List<LivingEntity> entitiesOfClass = level.getEntitiesOfClass(
+ it.unimi.dsi.fastutil.objects.ObjectArrayList<LivingEntity> entitiesOfClass = (it.unimi.dsi.fastutil.objects.ObjectArrayList<LivingEntity>) level.getEntitiesOfClass( // DivineMC - Optimize collections
LivingEntity.class, aabb, matchableEntity -> matchableEntity != entity && matchableEntity.isAlive()
);
- entitiesOfClass.sort(Comparator.comparingDouble(entity::distanceToSqr));
+ entitiesOfClass.unstableSort(Comparator.comparingDouble(entity::distanceToSqr)); // DivineMC - Optimize collections
Brain<?> brain = entity.getBrain();
brain.setMemory(MemoryModuleType.NEAREST_LIVING_ENTITIES, entitiesOfClass);
brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, new NearestVisibleLivingEntities(level, entity, entitiesOfClass));
diff --git a/net/minecraft/world/entity/ai/sensing/VillagerBabiesSensor.java b/net/minecraft/world/entity/ai/sensing/VillagerBabiesSensor.java
index 24d1928445b5571e040a2b12d5c82e77a880d9bd..dac0a23aebf2dea1972c07d5c82079da7c9837ac 100644
--- a/net/minecraft/world/entity/ai/sensing/VillagerBabiesSensor.java
+++ b/net/minecraft/world/entity/ai/sensing/VillagerBabiesSensor.java
@@ -21,9 +21,22 @@ public class VillagerBabiesSensor extends Sensor<LivingEntity> {
entity.getBrain().setMemory(MemoryModuleType.VISIBLE_VILLAGER_BABIES, this.getNearestVillagerBabies(entity));
}
+ // DivineMC start - Optimize baby villager sensor
private List<LivingEntity> getNearestVillagerBabies(LivingEntity livingEntity) {
- return ImmutableList.copyOf(this.getVisibleEntities(livingEntity).findAll(this::isVillagerBaby));
+ NearestVisibleLivingEntities visibleEntities = this.getVisibleEntities(livingEntity);
+ ImmutableList.Builder<LivingEntity> babies = ImmutableList.builder();
+
+ for (LivingEntity target : visibleEntities.nearbyEntities) {
+ if (target.getType() == EntityType.VILLAGER
+ && target.isBaby()
+ && visibleEntities.lineOfSightTest.test(target)) {
+ babies.add(target);
+ }
+ }
+
+ return babies.build();
}
+ // DivineMC end - Optimize baby villager sensor
private boolean isVillagerBaby(LivingEntity livingEntity) {
return livingEntity.getType() == EntityType.VILLAGER && livingEntity.isBaby();
diff --git a/net/minecraft/world/entity/animal/goat/Goat.java b/net/minecraft/world/entity/animal/goat/Goat.java
index 80714e2a6e74047af89680c261e2dbc097d86062..73fe7a8c216aed7dc2274fd53fb126d85ec5137d 100644
--- a/net/minecraft/world/entity/animal/goat/Goat.java
+++ b/net/minecraft/world/entity/animal/goat/Goat.java
@@ -99,6 +99,13 @@ public class Goat extends Animal {
this.getNavigation().setCanFloat(true);
this.setPathfindingMalus(PathType.POWDER_SNOW, -1.0F);
this.setPathfindingMalus(PathType.DANGER_POWDER_SNOW, -1.0F);
+ // DivineMC start - Optimize entities
+ if (!this.getBrain().hasMemoryValue(MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM)) {
+ org.bxteam.divinemc.util.entity.SensorHelper.disableSensor(this, SensorType.NEAREST_ITEMS);
+ } else if (net.minecraft.SharedConstants.IS_RUNNING_IN_IDE) {
+ throw new IllegalStateException("Goat Entity has a nearest visible wanted item memory module! This patch(Optimize-Brain, Goat.java changes) should probably be removed permanently!");
+ }
+ // DivineMC end - Optimize entities
}
public ItemStack createHorn() {
diff --git a/net/minecraft/world/entity/schedule/Activity.java b/net/minecraft/world/entity/schedule/Activity.java
index 5a143bb6fabba3dc4e2272afb0be636d5722ea22..133a51ed45500aba7b0bc4a7acb19731a524d8c1 100644
--- a/net/minecraft/world/entity/schedule/Activity.java
+++ b/net/minecraft/world/entity/schedule/Activity.java
@@ -32,10 +32,12 @@ public class Activity {
public static final Activity DIG = register("dig");
private final String name;
private final int hashCode;
+ public final int id; // DivineMC - Optimize entities - cache registry ID
- private Activity(String name) {
+ private Activity(String name, int id) {
this.name = name;
this.hashCode = name.hashCode();
+ this.id = id; // DivineMC - Optimize entities - cache registry ID
}
public String getName() {
@@ -43,7 +45,7 @@ public class Activity {
}
private static Activity register(String key) {
- return Registry.register(BuiltInRegistries.ACTIVITY, key, new Activity(key));
+ return Registry.register(BuiltInRegistries.ACTIVITY, key, new Activity(key, BuiltInRegistries.ACTIVITY.size())); // DivineMC - Optimize entities - cache registry ID
}
@Override

View File

@@ -0,0 +1,59 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sat, 1 Feb 2025 19:50:02 +0300
Subject: [PATCH] SparklyPaper: Skip distanceToSqr call in
ServerEntity#sendChanges if the delta movement hasn't changed
Original project: https://github.com/SparklyPower/SparklyPaper
Patch description:
The "distanceToSqr" call is a bit expensive, so avoiding it is pretty nice, around ~15% calls are skipped with this check
We could also check if the x,y,z coordinates are equal, but for now, let's just keep the identity check, which also helps us since Minecraft's code does reuse the original delta movement Vec3 object
diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java
index 638432a2e6506d3db6a25c068a33eeafb13cf0d6..737a6ff0bfec9b555fa425619d97b80ef95cb3e6 100644
--- a/net/minecraft/server/level/ServerEntity.java
+++ b/net/minecraft/server/level/ServerEntity.java
@@ -197,23 +197,27 @@ public class ServerEntity {
if (this.entity.hasImpulse || this.trackDelta || this.entity instanceof LivingEntity && ((LivingEntity)this.entity).isFallFlying()) {
Vec3 deltaMovement = this.entity.getDeltaMovement();
- double d = deltaMovement.distanceToSqr(this.lastSentMovement);
- if (d > 1.0E-7 || d > 0.0 && deltaMovement.lengthSqr() == 0.0) {
- this.lastSentMovement = deltaMovement;
- if (this.entity instanceof AbstractHurtingProjectile abstractHurtingProjectile) {
- this.synchronizer
- .sendToTrackingPlayers(
- new ClientboundBundlePacket(
- List.of(
- new ClientboundSetEntityMotionPacket(this.entity.getId(), this.lastSentMovement),
- new ClientboundProjectilePowerPacket(abstractHurtingProjectile.getId(), abstractHurtingProjectile.accelerationPower)
+ // DivineMC start - Skip "distanceToSqr" call in "ServerEntity#sendChanges" if the delta movement hasn't changed
+ if (deltaMovement != this.lastSentMovement) {
+ double d = deltaMovement.distanceToSqr(this.lastSentMovement);
+ if (d > 1.0E-7 || d > 0.0 && deltaMovement.lengthSqr() == 0.0) {
+ this.lastSentMovement = deltaMovement;
+ if (this.entity instanceof AbstractHurtingProjectile abstractHurtingProjectile) {
+ this.synchronizer
+ .sendToTrackingPlayers(
+ new ClientboundBundlePacket(
+ List.of(
+ new ClientboundSetEntityMotionPacket(this.entity.getId(), this.lastSentMovement),
+ new ClientboundProjectilePowerPacket(abstractHurtingProjectile.getId(), abstractHurtingProjectile.accelerationPower)
+ )
)
- )
- );
- } else {
- this.synchronizer.sendToTrackingPlayers(new ClientboundSetEntityMotionPacket(this.entity.getId(), this.lastSentMovement));
+ );
+ } else {
+ this.synchronizer.sendToTrackingPlayers(new ClientboundSetEntityMotionPacket(this.entity.getId(), this.lastSentMovement));
+ }
}
}
+ // DivineMC end - Skip "distanceToSqr" call in "ServerEntity#sendChanges" if the delta movement hasn't changed
}
if (packet != null) {

View File

@@ -0,0 +1,119 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sun, 6 Jul 2025 03:24:48 +0300
Subject: [PATCH] Carpet-Fixes: Optimized getBiome method
Original license: MIT
Original project: https://github.com/fxmorin/carpet-fixes
diff --git a/net/minecraft/world/level/biome/BiomeManager.java b/net/minecraft/world/level/biome/BiomeManager.java
index 73962e79a0f3d892e3155443a1b84508b0f4042e..c5fa1d7613410593f5b430968398c4ab1b40a98b 100644
--- a/net/minecraft/world/level/biome/BiomeManager.java
+++ b/net/minecraft/world/level/biome/BiomeManager.java
@@ -14,6 +14,7 @@ public class BiomeManager {
private static final int ZOOM_MASK = 3;
private final BiomeManager.NoiseBiomeSource noiseBiomeSource;
private final long biomeZoomSeed;
+ private static final double maxOffset = 0.4500000001D; // DivineMC - Carpet-Fixes: Optimized getBiome method
public BiomeManager(BiomeManager.NoiseBiomeSource noiseBiomeSource, long biomeZoomSeed) {
this.noiseBiomeSource = noiseBiomeSource;
@@ -29,39 +30,68 @@ public class BiomeManager {
}
public Holder<Biome> getBiome(BlockPos pos) {
- int i = pos.getX() - 2;
- int i1 = pos.getY() - 2;
- int i2 = pos.getZ() - 2;
- int i3 = i >> 2;
- int i4 = i1 >> 2;
- int i5 = i2 >> 2;
- double d = (i & 3) / 4.0;
- double d1 = (i1 & 3) / 4.0;
- double d2 = (i2 & 3) / 4.0;
- int i6 = 0;
- double d3 = Double.POSITIVE_INFINITY;
-
- for (int i7 = 0; i7 < 8; i7++) {
- boolean flag = (i7 & 4) == 0;
- boolean flag1 = (i7 & 2) == 0;
- boolean flag2 = (i7 & 1) == 0;
- int i8 = flag ? i3 : i3 + 1;
- int i9 = flag1 ? i4 : i4 + 1;
- int i10 = flag2 ? i5 : i5 + 1;
- double d4 = flag ? d : d - 1.0;
- double d5 = flag1 ? d1 : d1 - 1.0;
- double d6 = flag2 ? d2 : d2 - 1.0;
- double fiddledDistance = getFiddledDistance(this.biomeZoomSeed, i8, i9, i10, d4, d5, d6);
- if (d3 > fiddledDistance) {
- i6 = i7;
- d3 = fiddledDistance;
+ // DivineMC start - Carpet-Fixes: Optimized getBiome method
+ int xMinus2 = pos.getX() - 2;
+ int yMinus2 = pos.getY() - 2;
+ int zMinus2 = pos.getZ() - 2;
+ int x = xMinus2 >> 2;
+ int y = yMinus2 >> 2;
+ int z = zMinus2 >> 2;
+ double quartX = (double) (xMinus2 & 3) / 4.0;
+ double quartY = (double) (yMinus2 & 3) / 4.0;
+ double quartZ = (double) (zMinus2 & 3) / 4.0;
+ int smallestX = 0;
+ double smallestDist = Double.POSITIVE_INFINITY;
+
+ for (int biomeX = 0; biomeX < 8; ++biomeX) {
+ boolean everyOtherQuad = (biomeX & 4) == 0;
+ boolean everyOtherPair = (biomeX & 2) == 0;
+ boolean everyOther = (biomeX & 1) == 0;
+ double quartXX = everyOtherQuad ? quartX : quartX - 1.0;
+ double quartYY = everyOtherPair ? quartY : quartY - 1.0;
+ double quartZZ = everyOther ? quartZ : quartZ - 1.0;
+
+ double maxQuartYY = 0.0, maxQuartZZ = 0.0;
+ if (biomeX != 0) {
+ maxQuartYY = Mth.square(Math.max(quartYY + maxOffset, Math.abs(quartYY - maxOffset)));
+ maxQuartZZ = Mth.square(Math.max(quartZZ + maxOffset, Math.abs(quartZZ - maxOffset)));
+ double maxQuartXX = Mth.square(Math.max(quartXX + maxOffset, Math.abs(quartXX - maxOffset)));
+ if (smallestDist < maxQuartXX + maxQuartYY + maxQuartZZ) continue;
+ }
+
+ int xx = everyOtherQuad ? x : x + 1;
+ int yy = everyOtherPair ? y : y + 1;
+ int zz = everyOther ? z : z + 1;
+
+ long seed = LinearCongruentialGenerator.next(this.biomeZoomSeed, xx);
+ seed = LinearCongruentialGenerator.next(seed, yy);
+ seed = LinearCongruentialGenerator.next(seed, zz);
+ seed = LinearCongruentialGenerator.next(seed, xx);
+ seed = LinearCongruentialGenerator.next(seed, yy);
+ seed = LinearCongruentialGenerator.next(seed, zz);
+ double offsetX = getFiddle(seed);
+ double sqrX = Mth.square(quartXX + offsetX);
+ if (biomeX != 0 && smallestDist < sqrX + maxQuartYY + maxQuartZZ) continue;
+ seed = LinearCongruentialGenerator.next(seed, this.biomeZoomSeed);
+ double offsetY = getFiddle(seed);
+ double sqrY = Mth.square(quartYY + offsetY);
+ if (biomeX != 0 && smallestDist < sqrX + sqrY + maxQuartZZ) continue;
+ seed = LinearCongruentialGenerator.next(seed, this.biomeZoomSeed);
+ double offsetZ = getFiddle(seed);
+ double biomeDist = sqrX + sqrY + Mth.square(quartZZ + offsetZ);
+
+ if (smallestDist > biomeDist) {
+ smallestX = biomeX;
+ smallestDist = biomeDist;
}
}
- int i7x = (i6 & 4) == 0 ? i3 : i3 + 1;
- int i11 = (i6 & 2) == 0 ? i4 : i4 + 1;
- int i12 = (i6 & 1) == 0 ? i5 : i5 + 1;
- return this.noiseBiomeSource.getNoiseBiome(i7x, i11, i12);
+ return this.noiseBiomeSource.getNoiseBiome(
+ (smallestX & 4) == 0 ? x : x + 1,
+ (smallestX & 2) == 0 ? y : y + 1,
+ (smallestX & 1) == 0 ? z : z + 1
+ );
+ // DivineMC end - Carpet-Fixes: Optimized getBiome method
}
public Holder<Biome> getNoiseBiomeAtPosition(double x, double y, double z) {

View File

@@ -0,0 +1,126 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Thu, 8 May 2025 16:49:29 +0300
Subject: [PATCH] Carpet-Fixes: Sheep Optimization
This patch is based on the following mixins and classes:
* "carpetfixes/helpers/Utils.java"
* "carpetfixes/mixins/optimizations/SheepEntity_childColorMixin.java"
By: fxmorin <28154542+fxmorin@users.noreply.github.com>
As part of: carpet-fixes (https://github.com/fxmorin/carpet-fixes)
Licensed under: MIT (https://opensource.org/licenses/MIT)
Patch description:
The game determines the child sheep's color by getting a wool block from the parents, putting them in a crafting
recipe, getting the output wool and getting the color from that.
I don't know in what world we would consider a data-driven method with that much overhead as a smart idea. Instead,
we used a prebaked list of all the possible colors and combinations, however this means that you can't use a
datapack to change it.
diff --git a/net/minecraft/world/item/DyeColor.java b/net/minecraft/world/item/DyeColor.java
index c9cde255117b46690b2b6670d009a00b051af016..2c9f513e6ccd75959484f29a375671e21aab9590 100644
--- a/net/minecraft/world/item/DyeColor.java
+++ b/net/minecraft/world/item/DyeColor.java
@@ -112,6 +112,15 @@ public enum DyeColor implements StringRepresentable {
}
public static DyeColor getMixedColor(ServerLevel level, DyeColor first, DyeColor second) {
+ // DivineMC start - Carpet-Fixes: Sheep Optimization
+ if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sheepOptimization) {
+ DyeColor col = properDye(first, second);
+
+ if (col == null) col = level.random.nextBoolean() ? first : second;
+
+ return col;
+ }
+ // DivineMC end - Carpet-Fixes: Sheep Optimization
CraftingInput craftingInput = makeCraftColorInput(first, second);
return level.recipeAccess()
.getRecipeFor(RecipeType.CRAFTING, craftingInput, level)
@@ -132,4 +141,85 @@ public enum DyeColor implements StringRepresentable {
return values()[random.nextInt(values().length)];
}
// Purpur end - Shulker spawn from bullet options
+
+ // DivineMC start - Carpet-Fixes: Sheep Optimization
+ private static DyeColor properDye(DyeColor firstColor, DyeColor secondColor) {
+ if (firstColor.equals(secondColor)) return firstColor;
+
+ switch (firstColor) {
+ case WHITE -> {
+ switch (secondColor) {
+ case BLUE -> {
+ return DyeColor.LIGHT_BLUE;
+ }
+ case GRAY -> {
+ return DyeColor.LIGHT_GRAY;
+ }
+ case BLACK -> {
+ return DyeColor.GRAY;
+ }
+ case GREEN -> {
+ return DyeColor.LIME;
+ }
+ case RED -> {
+ return DyeColor.PINK;
+ }
+ }
+ }
+ case BLUE -> {
+ switch (secondColor) {
+ case WHITE -> {
+ return DyeColor.LIGHT_BLUE;
+ }
+ case GREEN -> {
+ return DyeColor.CYAN;
+ }
+ case RED -> {
+ return DyeColor.PURPLE;
+ }
+ }
+ }
+ case RED -> {
+ switch (secondColor) {
+ case YELLOW -> {
+ return DyeColor.ORANGE;
+ }
+ case WHITE -> {
+ return DyeColor.PINK;
+ }
+ case BLUE -> {
+ return DyeColor.PURPLE;
+ }
+ }
+ }
+ case GREEN -> {
+ switch (secondColor) {
+ case BLUE -> {
+ return DyeColor.CYAN;
+ }
+ case WHITE -> {
+ return DyeColor.LIME;
+ }
+ }
+ }
+ case YELLOW -> {
+ if (secondColor.equals(DyeColor.RED)) return DyeColor.ORANGE;
+ }
+ case PURPLE -> {
+ if (secondColor.equals(DyeColor.PINK)) return DyeColor.MAGENTA;
+ }
+ case PINK -> {
+ if (secondColor.equals(DyeColor.PURPLE)) return DyeColor.MAGENTA;
+ }
+ case GRAY -> {
+ if (secondColor.equals(DyeColor.WHITE)) return DyeColor.LIGHT_GRAY;
+ }
+ case BLACK -> {
+ if (secondColor.equals(DyeColor.WHITE)) return DyeColor.GRAY;
+ }
+ }
+
+ return null;
+ }
+ // DivineMC end - Carpet-Fixes: Sheep Optimization
}

View File

@@ -0,0 +1,111 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Wed, 11 Jun 2025 19:33:13 +0300
Subject: [PATCH] Carpet-AMS-Addition: Optimized dragon respawn
This patch is based on the following mixins and classes:
* "club/mcams/carpet/mixin/rule/optimizedDragonRespawn/BlockPatternMixin.java"
* "club/mcams/carpet/mixin/rule/optimizedDragonRespawn/EnderDragonFightMixin.java"
* "club/mcams/carpet/helpers/rule/optimizedDragonRespawn/BlockPatternHelper.java"
By: 1024-byteeeee
As part of: Carpet-AMS-Addition (https://github.com/Minecraft-AMS/Carpet-AMS-Addition)
Licensed under: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html)
diff --git a/net/minecraft/world/level/block/state/pattern/BlockPattern.java b/net/minecraft/world/level/block/state/pattern/BlockPattern.java
index f7bb979f08634a7e1b77c59040f59fb5e11aafa5..e0eca73d9e8a77b2a4972db61001394c6bf1d2c4 100644
--- a/net/minecraft/world/level/block/state/pattern/BlockPattern.java
+++ b/net/minecraft/world/level/block/state/pattern/BlockPattern.java
@@ -59,7 +59,7 @@ public class BlockPattern {
}
@Nullable
- private BlockPattern.BlockPatternMatch matches(BlockPos pos, Direction finger, Direction thumb, LoadingCache<BlockPos, BlockInWorld> cache) {
+ public BlockPattern.BlockPatternMatch matches(BlockPos pos, Direction finger, Direction thumb, LoadingCache<BlockPos, BlockInWorld> cache) { // DivineMC - Carpet-AMS-Addition: Optimized dragon respawn - make public
for (int i = 0; i < this.width; i++) {
for (int i1 = 0; i1 < this.height; i1++) {
for (int i2 = 0; i2 < this.depth; i2++) {
diff --git a/net/minecraft/world/level/dimension/end/EndDragonFight.java b/net/minecraft/world/level/dimension/end/EndDragonFight.java
index 7f9bd256da15da915db1c45099572d9eb3286166..fe586b99bdf7c145b7a51afc9f4ca910f22af6d8 100644
--- a/net/minecraft/world/level/dimension/end/EndDragonFight.java
+++ b/net/minecraft/world/level/dimension/end/EndDragonFight.java
@@ -273,8 +273,68 @@ public class EndDragonFight {
return false;
}
+ // DivineMC start - Carpet-AMS-Addition: Optimized dragon respawn
+ private int cachePortalChunkIteratorX = -8;
+ private int cachePortalChunkIteratorZ = -8;
+ private int cachePortalOriginIteratorY = -1;
+
@Nullable
public BlockPattern.BlockPatternMatch findExitPortal() {
+ if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.optimizedDragonRespawn) {
+ int i, j;
+ for (i = cachePortalChunkIteratorX; i <= 8; ++i) {
+ for (j = cachePortalChunkIteratorZ; j <= 8; ++j) {
+ LevelChunk worldChunk = this.level.getChunk(i, j);
+ for (BlockEntity blockEntity : worldChunk.getBlockEntities().values()) {
+ if (blockEntity instanceof net.minecraft.world.level.block.entity.TheEndGatewayBlockEntity) {
+ continue;
+ }
+ if (blockEntity instanceof TheEndPortalBlockEntity) {
+ BlockPattern.BlockPatternMatch blockPatternMatch = this.exitPortalPattern.find(this.level, blockEntity.getBlockPos());
+ if (blockPatternMatch != null) {
+ BlockPos blockPos = blockPatternMatch.getBlock(3, 3, 3).getPos();
+ if (this.portalLocation == null) {
+ this.portalLocation = blockPos;
+ }
+
+ cachePortalChunkIteratorX = i;
+ cachePortalChunkIteratorZ = j;
+ return blockPatternMatch;
+ }
+ }
+ }
+ }
+ }
+
+ if (this.needsStateScanning || this.portalLocation == null) {
+ if (cachePortalOriginIteratorY != -1) {
+ i = cachePortalOriginIteratorY;
+ } else {
+ i = this.level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, EndPodiumFeature.getLocation(BlockPos.ZERO)).getY();
+ }
+ boolean notFirstSearch = false;
+ for (j = i; j >= 0; --j) {
+ BlockPattern.BlockPatternMatch result2 = null;
+ if (notFirstSearch) {
+ result2 = org.bxteam.divinemc.util.BlockPatternHelper.partialSearchAround(this.exitPortalPattern, this.level, new BlockPos(EndPodiumFeature.getLocation(BlockPos.ZERO).getY(), j, EndPodiumFeature.getLocation(BlockPos.ZERO).getZ()));
+ } else {
+ result2 = this.exitPortalPattern.find(this.level, new BlockPos(EndPodiumFeature.getLocation(BlockPos.ZERO).getX(), j, EndPodiumFeature.getLocation(BlockPos.ZERO).getZ()));
+ }
+ if (result2 != null) {
+ if (this.portalLocation == null) {
+ this.portalLocation = result2.getBlock(3, 3, 3).getPos();
+ }
+ cachePortalOriginIteratorY = j;
+ return result2;
+ }
+ notFirstSearch = true;
+ }
+ }
+
+ return null;
+ }
+ // DivineMC end - Carpet-AMS-Addition: Optimized dragon respawn
+
ChunkPos chunkPos = new ChunkPos(this.origin);
for (int i = -8 + chunkPos.x; i <= 8 + chunkPos.x; i++) {
@@ -572,6 +632,11 @@ public class EndDragonFight {
}
public boolean respawnDragon(List<EndCrystal> crystals) { // CraftBukkit - return boolean
+ // DivineMC start - Carpet-AMS-Addition: Optimized dragon respawn
+ cachePortalChunkIteratorX = -8;
+ cachePortalChunkIteratorZ = -8;
+ cachePortalOriginIteratorY = -1;
+ // DivineMC end - Carpet-AMS-Addition: Optimized dragon respawn
if (this.dragonKilled && this.respawnStage == null) {
for (BlockPattern.BlockPatternMatch blockPatternMatch = this.findExitPortal(); blockPatternMatch != null; blockPatternMatch = this.findExitPortal()) {
for (int i = 0; i < this.exitPortalPattern.getWidth(); i++) {

View File

@@ -0,0 +1,44 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sat, 8 Mar 2025 13:27:07 +0300
Subject: [PATCH] ModernFix: compact_bit_storage
This patch is based on following mixins:
* "org/embeddedt/modernfix/common/mixin/perf/compact_bit_storage/PalettedContainerMixin.java"
By: embeddedt <42941056+embeddedt@users.noreply.github.com>
As part of: ModernFix (https://github.com/embeddedt/ModernFix)
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/net/minecraft/world/level/chunk/PalettedContainer.java b/net/minecraft/world/level/chunk/PalettedContainer.java
index f1cae4cab36546d5798d2b59ac59774d1c9838c0..731da3f35013d9916577a7f475d6a796dd201742 100644
--- a/net/minecraft/world/level/chunk/PalettedContainer.java
+++ b/net/minecraft/world/level/chunk/PalettedContainer.java
@@ -283,6 +283,28 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
data.palette.read(buffer, this.strategy.globalMap());
buffer.readFixedSizeLongArray(data.storage.getRaw());
this.data = data;
+ // DivineMC start - ModernFix: compact_bit_storage
+ if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.useCompactBitStorage && _byte > 1) {
+ long[] storageArray = this.data.storage.getRaw();
+ boolean empty = true;
+ for (long l : storageArray) {
+ if (l != 0) {
+ empty = false;
+ break;
+ }
+ }
+ if (empty && storageArray.length > 0) {
+ T value;
+ try {
+ value = this.data.palette.valueFor(0);
+ } catch (RuntimeException e) {
+ return;
+ }
+ this.data = this.createOrReuseData(null, 0);
+ this.data.palette.idFor(value);
+ }
+ }
+ // DivineMC end - ModernFix: compact_bit_storage
this.addPresetValues(); // Paper - Anti-Xray - Add preset values (inefficient, but this isn't used by the server)
this.updateData(this.data); // Paper - optimise palette reads
} finally {

View File

@@ -0,0 +1,53 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Mon, 3 Mar 2025 20:08:44 +0300
Subject: [PATCH] Option to disable disconnect.spam
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index dc180d67066436263370d8bf55dafabad7c34004..46cf071e34c8b46c0dafadb14eb6fc1bb1c4d9a8 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -852,7 +852,7 @@ public class ServerGamePacketListenerImpl
public void handleCustomCommandSuggestions(ServerboundCommandSuggestionPacket packet) {
// PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); // Paper - AsyncTabCompleteEvent; run this async
// CraftBukkit start
- if (!this.tabSpamThrottler.isIncrementAndUnderThreshold() && !this.server.getPlayerList().isOp(this.player.nameAndId()) && !this.server.isSingleplayerOwner(this.player.nameAndId())) { // Paper - configurable tab spam limits
+ if (!this.tabSpamThrottler.isIncrementAndUnderThreshold() && !this.server.getPlayerList().isOp(this.player.nameAndId()) && !this.server.isSingleplayerOwner(this.player.nameAndId()) && !org.bxteam.divinemc.config.DivineConfig.NetworkCategory.disableDisconnectSpam) { // Paper - configurable tab spam limits // DivineMC - Option to disable disconnect.spam
this.disconnectAsync(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - Kick event cause // Paper - add proper async disconnect
return;
}
@@ -864,7 +864,7 @@ public class ServerGamePacketListenerImpl
// Paper end - Don't suggest if tab-complete is disabled
// Paper start
final int index;
- if (packet.getCommand().length() > 64 && ((index = packet.getCommand().indexOf(' ')) == -1 || index >= 64)) {
+ if (packet.getCommand().length() > 64 && ((index = packet.getCommand().indexOf(' ')) == -1 || index >= 64) && !org.bxteam.divinemc.config.DivineConfig.NetworkCategory.disableDisconnectSpam) { // DivineMC - Option to disable disconnect.spam
this.disconnectAsync(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - add proper async disconnect
return;
}
@@ -924,6 +924,7 @@ public class ServerGamePacketListenerImpl
ParseResults<CommandSourceStack> parseResults = this.server.getCommands().getDispatcher().parse(stringReader, this.player.createCommandSourceStack());
// Paper start - Handle non-recoverable exceptions
if (!parseResults.getExceptions().isEmpty()
+ && !org.bxteam.divinemc.config.DivineConfig.NetworkCategory.disableDisconnectSpam // DivineMC - Option to disable disconnect.spam
&& parseResults.getExceptions().values().stream().anyMatch(e -> e instanceof io.papermc.paper.brigadier.TagParseCommandSyntaxException)) {
this.disconnect(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM);
return;
@@ -2638,6 +2639,7 @@ public class ServerGamePacketListenerImpl
// this.chatSpamThrottler.increment();
if (!this.chatSpamThrottler.isIncrementAndUnderThreshold()
// CraftBukkit end
+ && !org.bxteam.divinemc.config.DivineConfig.NetworkCategory.disableDisconnectSpam // DivineMC - Option to disable disconnect.spam
&& !this.server.getPlayerList().isOp(this.player.nameAndId())
&& !this.server.isSingleplayerOwner(this.player.nameAndId())) {
this.disconnectAsync(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - kick event cause & add proper async disconnect
@@ -3348,7 +3350,7 @@ public class ServerGamePacketListenerImpl
public void handlePlaceRecipe(ServerboundPlaceRecipePacket packet) {
// Paper start - auto recipe limit
if (!org.bukkit.Bukkit.isPrimaryThread()) {
- if (!this.recipeSpamPackets.isIncrementAndUnderThreshold()) {
+ if (!this.recipeSpamPackets.isIncrementAndUnderThreshold() && !org.bxteam.divinemc.config.DivineConfig.NetworkCategory.disableDisconnectSpam) { // DivineMC - Option to disable disconnect.spam
this.disconnectAsync(net.minecraft.network.chat.Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - kick event cause // Paper - add proper async disconnect
return;
}

View File

@@ -0,0 +1,44 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sun, 23 Feb 2025 01:04:29 +0300
Subject: [PATCH] Option to disable saving of snowball and firework
diff --git a/net/minecraft/world/entity/projectile/FireworkRocketEntity.java b/net/minecraft/world/entity/projectile/FireworkRocketEntity.java
index 9b13991088a5e5f4d7697588777e5252ae67b73a..449d86680bd7c084287cab6ba31466a889ae52cd 100644
--- a/net/minecraft/world/entity/projectile/FireworkRocketEntity.java
+++ b/net/minecraft/world/entity/projectile/FireworkRocketEntity.java
@@ -350,4 +350,14 @@ public class FireworkRocketEntity extends Projectile implements ItemSupplier {
double d1 = entity.position().z - this.position().z;
return DoubleDoubleImmutablePair.of(d, d1);
}
+
+ // DivineMC start - Option to disable saving firework
+ @Override
+ public boolean shouldBeSaved() {
+ if (this.level().divineConfig.disableFireworkSaving) {
+ return false;
+ }
+ return super.shouldBeSaved();
+ }
+ // DivineMC end - Option to disable saving firework
}
diff --git a/net/minecraft/world/entity/projectile/Snowball.java b/net/minecraft/world/entity/projectile/Snowball.java
index 49694d7fe529fb8197ab4a3a7412d6923ee1bda7..bfa08f51aab4f5cfa0ef0a66075c6a266f586d0a 100644
--- a/net/minecraft/world/entity/projectile/Snowball.java
+++ b/net/minecraft/world/entity/projectile/Snowball.java
@@ -94,4 +94,14 @@ public class Snowball extends ThrowableItemProjectile {
this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.HIT); // CraftBukkit - add Bukkit remove cause
}
}
+
+ // DivineMC start - Option to disable snowball saving
+ @Override
+ public boolean shouldBeSaved() {
+ if (this.level().divineConfig.disableSnowballSaving) {
+ return false;
+ }
+ return super.shouldBeSaved();
+ }
+ // DivineMC end - Option to disable snowball saving
}

View File

@@ -0,0 +1,42 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sat, 1 Feb 2025 19:24:47 +0300
Subject: [PATCH] Snowball and Egg knockback
diff --git a/net/minecraft/world/entity/projectile/Snowball.java b/net/minecraft/world/entity/projectile/Snowball.java
index bfa08f51aab4f5cfa0ef0a66075c6a266f586d0a..9fb9244ad95a5fa64df6beb3e7208c7598216132 100644
--- a/net/minecraft/world/entity/projectile/Snowball.java
+++ b/net/minecraft/world/entity/projectile/Snowball.java
@@ -54,6 +54,12 @@ public class Snowball extends ThrowableItemProjectile {
Entity entity = result.getEntity();
int i = entity.level().purpurConfig.snowballDamage >= 0 ? entity.level().purpurConfig.snowballDamage : entity instanceof Blaze ? 3 : 0; // Purpur - Add configurable snowball damage
entity.hurt(this.damageSources().thrown(this, this.getOwner()), i);
+ // DivineMC start - Make snowball can knockback player
+ if (this.level().divineConfig.snowballCanKnockback && entity instanceof net.minecraft.server.level.ServerPlayer serverPlayer) {
+ entity.hurt(this.damageSources().thrown(this, this.getOwner()), 0.0000001F);
+ serverPlayer.knockback(0.4000000059604645D, this.getX() - entity.getX(), this.getZ() - entity.getZ());
+ }
+ // DivineMC end - Make snowball can knockback player
}
// Purpur start - options to extinguish fire blocks with snowballs - borrowed and modified code from ThrownPotion#onHitBlock and ThrownPotion#dowseFire
diff --git a/net/minecraft/world/entity/projectile/ThrownEgg.java b/net/minecraft/world/entity/projectile/ThrownEgg.java
index 57cb5b73dae86379be857ffa0f55ef7a056a0b4a..d3dea3e36297da11c3b753394f3c4c72c9c5b4f2 100644
--- a/net/minecraft/world/entity/projectile/ThrownEgg.java
+++ b/net/minecraft/world/entity/projectile/ThrownEgg.java
@@ -54,7 +54,14 @@ public class ThrownEgg extends ThrowableItemProjectile {
@Override
protected void onHitEntity(EntityHitResult result) {
super.onHitEntity(result);
+ net.minecraft.world.entity.Entity entity = result.getEntity(); // DivineMC - make egg can knockback player
result.getEntity().hurt(this.damageSources().thrown(this, this.getOwner()), 0.0F);
+ // DivineMC start - Make egg can knockback player
+ if (this.level().divineConfig.eggCanKnockback && entity instanceof net.minecraft.server.level.ServerPlayer serverPlayer) {
+ entity.hurt(this.damageSources().thrown(this, this.getOwner()), 0.0000001F);
+ serverPlayer.knockback(0.4000000059604645D, this.getX() - entity.getX(), this.getZ() - entity.getZ());
+ }
+ // DivineMC end - Make egg can knockback player
}
@Override

View File

@@ -0,0 +1,34 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Wed, 11 Jun 2025 16:40:43 +0300
Subject: [PATCH] Optimize suffocation
Original license: GPL v3
Original project: https://github.com/pufferfish-gg/Pufferfish
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
index 4e8594985442fe4371504fca697569bd3c58339d..5c2d1d78dce9fed6649eda2dea14bf32f8f591dc 100644
--- a/net/minecraft/world/entity/LivingEntity.java
+++ b/net/minecraft/world/entity/LivingEntity.java
@@ -426,6 +426,12 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
return this.getDeltaMovement().y() < 1.0E-5F && this.isInLiquid();
}
+ // DivineMC start - Optimize suffocation
+ public boolean couldPossiblyBeHurt(float amount) {
+ return !((float) this.invulnerableTime > (float) this.invulnerableDuration / 2.0F) || !(amount <= this.lastHurt);
+ }
+ // DivineMC end - Optimize suffocation
+
@Override
public void baseTick() {
this.oAttackAnim = this.attackAnim;
@@ -440,7 +446,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
super.baseTick();
if (this.isAlive() && this.level() instanceof ServerLevel serverLevel1) {
boolean flag = this instanceof Player;
- if (this.isInWall()) {
+ if ((!org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.enableSuffocationOptimization || this instanceof WitherBoss || (tickCount % 10 == 0 && couldPossiblyBeHurt(1.0F))) && this.isInWall()) { // DivineMC - Optimize suffocation
this.hurtServer(serverLevel1, this.damageSources().inWall(), 1.0F);
} else if (flag && !serverLevel1.getWorldBorder().isWithinBounds(this.getBoundingBox())) {
double d = serverLevel1.getWorldBorder().getDistanceToBorder(this) + serverLevel1.getWorldBorder().getSafeZone();

View File

@@ -0,0 +1,44 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Wed, 11 Jun 2025 19:12:00 +0300
Subject: [PATCH] Reduce chunk loading & lookups
Original license: GPL v3
Original project: https://github.com/pufferfish-gg/Pufferfish
diff --git a/net/minecraft/world/entity/monster/EnderMan.java b/net/minecraft/world/entity/monster/EnderMan.java
index bf5fedd1dc7eea2a36edfb1420efe207ac409085..f4e90ced5ce5acd3a51f99cea905c92aa9c40e4f 100644
--- a/net/minecraft/world/entity/monster/EnderMan.java
+++ b/net/minecraft/world/entity/monster/EnderMan.java
@@ -336,11 +336,28 @@ public class EnderMan extends Monster implements NeutralMob {
private boolean teleport(double x, double y, double z) {
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(x, y, z);
- while (mutableBlockPos.getY() > this.level().getMinY() && !this.level().getBlockState(mutableBlockPos).blocksMotion()) {
- mutableBlockPos.move(Direction.DOWN);
+ // DivineMC start - Reduce chunk loading & lookups
+ BlockState blockState;
+ if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.reduceChuckLoadAndLookup) {
+ net.minecraft.world.level.chunk.LevelChunk chunk = this.level().getChunkIfLoaded(mutableBlockPos);
+ if (chunk == null) {
+ return false;
+ }
+
+ while (mutableBlockPos.getY() > this.level().getMinY() && !chunk.getBlockState(mutableBlockPos).blocksMotion()) {
+ mutableBlockPos.move(Direction.DOWN);
+ }
+
+ blockState = chunk.getBlockState(mutableBlockPos);
+ } else {
+ while (mutableBlockPos.getY() > this.level().getMinY() && !this.level().getBlockState(mutableBlockPos).blocksMotion()) {
+ mutableBlockPos.move(Direction.DOWN);
+ }
+
+ blockState = this.level().getBlockState(mutableBlockPos);
}
+ // DivineMC end - Reduce chunk loading & lookups
- BlockState blockState = this.level().getBlockState(mutableBlockPos);
boolean flag = blockState.blocksMotion();
boolean isWater = blockState.getFluidState().is(FluidTags.WATER);
if (flag && !isWater) {

View File

@@ -0,0 +1,82 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sun, 23 Mar 2025 01:26:54 +0300
Subject: [PATCH] Configurable movement speed for entities
diff --git a/net/minecraft/world/entity/monster/Drowned.java b/net/minecraft/world/entity/monster/Drowned.java
index 592b4f139a184f0522e350f5b83777b235eb243c..12650e3981fe48ab0cb4398e021e768eea03bd60 100644
--- a/net/minecraft/world/entity/monster/Drowned.java
+++ b/net/minecraft/world/entity/monster/Drowned.java
@@ -98,6 +98,7 @@ public class Drowned extends Zombie implements RangedAttackMob {
public void initAttributes() {
this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.drownedMaxHealth);
this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.drownedScale);
+ this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.level().purpurConfig.drownedMovementSpeed); // DivineMC - Configurable movement speed for entities
}
@Override
diff --git a/net/minecraft/world/entity/monster/Husk.java b/net/minecraft/world/entity/monster/Husk.java
index f2c55aa23ec647af6907b349b937c98fffd95523..33b32d0a9f29628ea4c59e72a1157a83992c222e 100644
--- a/net/minecraft/world/entity/monster/Husk.java
+++ b/net/minecraft/world/entity/monster/Husk.java
@@ -42,6 +42,7 @@ public class Husk extends Zombie {
@Override
public void initAttributes() {
this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.huskMaxHealth);
+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MOVEMENT_SPEED).setBaseValue(this.level().purpurConfig.huskMovementSpeed); // DivineMC - Configurable movement speed for entities
}
@Override
diff --git a/net/minecraft/world/entity/monster/Zombie.java b/net/minecraft/world/entity/monster/Zombie.java
index 63b29933e262115062a082dcaed0d16146a181bd..6c41126ad4b485a3dad8fafc8e26defccb426c47 100644
--- a/net/minecraft/world/entity/monster/Zombie.java
+++ b/net/minecraft/world/entity/monster/Zombie.java
@@ -125,6 +125,7 @@ public class Zombie extends Monster {
public void initAttributes() {
this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.zombieMaxHealth);
this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.zombieScale);
+ this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.level().purpurConfig.zombieMovementSpeed); // DivineMC - Configurable movement speed for entities
}
// Purpur end - Configurable entity base attributes
@@ -192,7 +193,6 @@ public class Zombie extends Monster {
public static AttributeSupplier.Builder createAttributes() {
return Monster.createMonsterAttributes()
.add(Attributes.FOLLOW_RANGE, 35.0)
- .add(Attributes.MOVEMENT_SPEED, 0.23F)
.add(Attributes.ATTACK_DAMAGE, 3.0)
.add(Attributes.ARMOR, 2.0)
.add(Attributes.SPAWN_REINFORCEMENTS_CHANCE);
diff --git a/net/minecraft/world/entity/monster/ZombieVillager.java b/net/minecraft/world/entity/monster/ZombieVillager.java
index 24378d21c12038c3b649fa4766bd9c66cb4afd54..3667ef042057bb1f6006c421e67cfd1c5dd6da36 100644
--- a/net/minecraft/world/entity/monster/ZombieVillager.java
+++ b/net/minecraft/world/entity/monster/ZombieVillager.java
@@ -94,6 +94,7 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder {
@Override
public void initAttributes() {
this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.zombieVillagerMaxHealth);
+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MOVEMENT_SPEED).setBaseValue(this.level().purpurConfig.zombieVillagerMovementSpeed); // DivineMC - Configurable movement speed for entities
}
@Override
diff --git a/net/minecraft/world/entity/monster/ZombifiedPiglin.java b/net/minecraft/world/entity/monster/ZombifiedPiglin.java
index 1955ea6cff628234342989249de6efd1180b0999..2b1fb75c490d4e4f6aa65dbc74454143d29c3fc9 100644
--- a/net/minecraft/world/entity/monster/ZombifiedPiglin.java
+++ b/net/minecraft/world/entity/monster/ZombifiedPiglin.java
@@ -86,6 +86,7 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob {
public void initAttributes() {
this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.zombifiedPiglinMaxHealth);
this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.zombifiedPiglinScale);
+ this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.level().purpurConfig.zombifiedPiglinMovementSpeed); // DivineMC - Configurable movement speed for entities
}
// Purpur end - Configurable entity base attributes
@@ -137,7 +138,6 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob {
public static AttributeSupplier.Builder createAttributes() {
return Zombie.createAttributes()
.add(Attributes.SPAWN_REINFORCEMENTS_CHANCE, 0.0)
- .add(Attributes.MOVEMENT_SPEED, 0.23F)
.add(Attributes.ATTACK_DAMAGE, 5.0);
}

View File

@@ -0,0 +1,73 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sat, 1 Feb 2025 18:03:13 +0300
Subject: [PATCH] Option to allow weird movement and disable teleporting
players when they move too quickly
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 46cf071e34c8b46c0dafadb14eb6fc1bb1c4d9a8..93fc67f8cf60be489249d83a748490b653953e7a 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -617,7 +617,7 @@ public class ServerGamePacketListenerImpl
return;
}
// Paper end - Prevent moving into unloaded chunks
- if (d7 - d6 > Math.max(100.0, Mth.square(org.spigotmc.SpigotConfig.movedTooQuicklyMultiplier * (float) i * speed)) && !this.isSingleplayerOwner()) {
+ if (!org.bxteam.divinemc.config.DivineConfig.FixesCategory.alwaysAllowWeirdMovement && (d7 - d6 > Math.max(100.0, Mth.square(org.spigotmc.SpigotConfig.movedTooQuicklyMultiplier * (float) i * speed)) && !this.isSingleplayerOwner())) { // DivineMC - stop weird movement
// CraftBukkit end
LOGGER.warn("{} (vehicle of {}) moved too quickly! {},{},{}", rootVehicle.getPlainTextName(), this.player.getPlainTextName(), d3, d4, d5);
this.send(ClientboundMoveVehiclePacket.fromEntity(rootVehicle));
@@ -645,7 +645,7 @@ public class ServerGamePacketListenerImpl
d5 = d2 - rootVehicle.getZ();
d7 = d3 * d3 + d4 * d4 + d5 * d5;
boolean flag1 = false;
- if (d7 > org.spigotmc.SpigotConfig.movedWronglyThreshold) { // Spigot
+ if (!org.bxteam.divinemc.config.DivineConfig.FixesCategory.alwaysAllowWeirdMovement && (d7 > org.spigotmc.SpigotConfig.movedWronglyThreshold)) { // Spigot // DivineMC - stop weird movement
flag1 = true; // Paper - diff on change, this should be moved wrongly
LOGGER.warn("{} (vehicle of {}) moved wrongly! {}", rootVehicle.getPlainTextName(), this.player.getPlainTextName(), Math.sqrt(d7));
}
@@ -1562,20 +1562,24 @@ public class ServerGamePacketListenerImpl
if (this.shouldCheckPlayerMovement(isFallFlying)) {
float f2 = isFallFlying ? 300.0F : 100.0F;
if (d7 - d6 > Math.max(f2, Mth.square(org.spigotmc.SpigotConfig.movedTooQuicklyMultiplier * (float) i * speed))) {
- // CraftBukkit end
- // Paper start - Add fail move event
- io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.MOVED_TOO_QUICKLY,
+ // DivineMC start - Stop teleporting players when they move too quickly
+ if (!org.bxteam.divinemc.config.DivineConfig.FixesCategory.alwaysAllowWeirdMovement && !(org.bxteam.divinemc.config.DivineConfig.FixesCategory.ignoreMovedTooQuicklyWhenLagging && player.level().getServer().lagging)) {
+ // CraftBukkit end
+ // Paper start - Add fail move event
+ io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.MOVED_TOO_QUICKLY,
toX, toY, toZ, toYaw, toPitch, true);
- if (!event.isAllowed()) {
- if (event.getLogWarning()) {
- LOGGER.warn("{} moved too quickly! {},{},{}", this.player.getPlainTextName(), d3, d4, d5);
- }
- this.teleport(
+ if (!event.isAllowed()) {
+ if (event.getLogWarning()) {
+ LOGGER.warn("{} moved too quickly! {},{},{}", this.player.getPlainTextName(), d3, d4, d5);
+ }
+ this.teleport(
this.player.getX(), this.player.getY(), this.player.getZ(), this.player.getYRot(), this.player.getXRot()
- );
- return;
+ );
+ return;
+ }
+ // Paper end - Add fail move event
}
- // Paper end - Add fail move event
+ // DivineMC end - Stop teleporting players when they move too quickly
}
}
}
@@ -1636,6 +1640,7 @@ public class ServerGamePacketListenerImpl
d7 = d3 * d3 + d4 * d4 + d5 * d5;
boolean movedWrongly = false; // Paper - Add fail move event; rename
if (!this.player.isChangingDimension()
+ && !org.bxteam.divinemc.config.DivineConfig.FixesCategory.alwaysAllowWeirdMovement // DivineMC - Stop teleporting players when they move too quickly
&& d7 > org.spigotmc.SpigotConfig.movedWronglyThreshold // Spigot
&& !this.player.isSleeping()
&& !this.player.isCreative()

View File

@@ -0,0 +1,137 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sun, 6 Apr 2025 20:53:48 +0300
Subject: [PATCH] Optimize Raids
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index cea6213837b001d7a494d17aa7e6e326abd1b2fc..06d3cbc33f41517028b5998f11695f68eb19aa5e 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -223,6 +223,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
public boolean hasEntityMoveEvent; // Paper - Add EntityMoveEvent
private final alternate.current.wire.WireHandler wireHandler = new alternate.current.wire.WireHandler(this); // Paper - optimize redstone (Alternate Current)
public boolean hasRidableMoveEvent = false; // Purpur - Ridables
+ public net.minecraft.world.item.ItemStack ominousBanner; // DivineMC - Optimize Raids
@Override
public @Nullable LevelChunk getChunkIfLoaded(int x, int z) {
@@ -718,6 +719,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
// Paper end - rewrite chunk system
this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit
this.preciseTime = this.serverLevelData.getDayTime(); // Purpur - Configurable daylight cycle
+ this.ominousBanner = Objects.requireNonNullElse(this.registryAccess(), net.minecraft.core.RegistryAccess.EMPTY).lookup(Registries.BANNER_PATTERN).map(Raid::getOminousBannerInstance).orElse(null); // DivineMC - Optimize Raids
}
// Paper start
diff --git a/net/minecraft/world/entity/raid/Raid.java b/net/minecraft/world/entity/raid/Raid.java
index adae4ef6b2f5019b79838eddf0255200096bccd9..97b9676c03b4b2d2aea7fd7c7dff3e6c4cecd57f 100644
--- a/net/minecraft/world/entity/raid/Raid.java
+++ b/net/minecraft/world/entity/raid/Raid.java
@@ -128,6 +128,7 @@ public class Raid {
private Raid.RaidStatus status;
private int celebrationTicks;
private Optional<BlockPos> waveSpawnPos = Optional.empty();
+ private boolean isBarDirty; // DivineMC - Optimize Raids
public Raid(BlockPos center, Difficulty difficulty) {
this.active = true;
@@ -280,6 +281,12 @@ public class Raid {
}
public void tick(ServerLevel level) {
+ // DivineMC start - Optimize Raids
+ if (this.isBarDirty) {
+ this.raidEvent.setProgress(Mth.clamp(this.getHealthOfLivingRaiders() / this.totalHealth, 0.0F, 1.0F));
+ this.isBarDirty = false;
+ }
+ // DivineMC end - Optimize Raids
if (!this.isStopped()) {
if (this.status == Raid.RaidStatus.ONGOING) {
boolean flag = this.active;
@@ -610,7 +617,7 @@ public class Raid {
}
public void updateBossbar() {
- this.raidEvent.setProgress(Mth.clamp(this.getHealthOfLivingRaiders() / this.totalHealth, 0.0F, 1.0F));
+ this.isBarDirty = true; // DivineMC - Optimize Raids
}
public float getHealthOfLivingRaiders() {
diff --git a/net/minecraft/world/entity/raid/Raider.java b/net/minecraft/world/entity/raid/Raider.java
index 2254493c889b8967011c09dc448ba375d82e2035..7802c5235a543c9f7147d27e122f4936f305f8ba 100644
--- a/net/minecraft/world/entity/raid/Raider.java
+++ b/net/minecraft/world/entity/raid/Raider.java
@@ -43,9 +43,25 @@ import net.minecraft.world.phys.Vec3;
public abstract class Raider extends PatrollingMonster {
protected static final EntityDataAccessor<Boolean> IS_CELEBRATING = SynchedEntityData.defineId(Raider.class, EntityDataSerializers.BOOLEAN);
- static final Predicate<ItemEntity> ALLOWED_ITEMS = item -> !item.hasPickUpDelay()
- && item.isAlive()
- && ItemStack.matches(item.getItem(), Raid.getOminousBannerInstance(item.registryAccess().lookupOrThrow(Registries.BANNER_PATTERN)));
+ // DivineMC start - Optimize Raids
+ static final Predicate<ItemEntity> ALLOWED_ITEMS = (itemEntity) -> {
+ ItemStack ominousBanner = ((ServerLevel) itemEntity.level()).ominousBanner;
+ if (ominousBanner == null) {
+ ominousBanner = Raid.getOminousBannerInstance(itemEntity.registryAccess().lookupOrThrow(Registries.BANNER_PATTERN));
+ }
+
+ return !itemEntity.hasPickUpDelay() && itemEntity.isAlive() &&
+ ItemStack.matches(itemEntity.getItem(), ominousBanner);
+ };
+
+ private ItemStack getOminousBanner(net.minecraft.core.HolderGetter<net.minecraft.world.level.block.entity.BannerPattern> bannerPatternLookup) {
+ ItemStack ominousBanner = ((ServerLevel) this.level()).ominousBanner;
+ if (ominousBanner == null) {
+ ominousBanner = Raid.getOminousBannerInstance(bannerPatternLookup);
+ }
+ return ominousBanner;
+ }
+ // DivineMC end - Optimize Raids
private static final int DEFAULT_WAVE = 0;
private static final boolean DEFAULT_CAN_JOIN_RAID = false;
@Nullable
@@ -150,7 +166,7 @@ public abstract class Raider extends PatrollingMonster {
public boolean isCaptain() {
ItemStack itemBySlot = this.getItemBySlot(EquipmentSlot.HEAD);
boolean flag = !itemBySlot.isEmpty()
- && ItemStack.matches(itemBySlot, Raid.getOminousBannerInstance(this.registryAccess().lookupOrThrow(Registries.BANNER_PATTERN)));
+ && ItemStack.matches(itemBySlot, getOminousBanner(this.registryAccess().lookupOrThrow(Registries.BANNER_PATTERN))); // DivineMC - Optimize Raids
boolean isPatrolLeader = this.isPatrolLeader();
return flag && isPatrolLeader;
}
@@ -213,7 +229,7 @@ public abstract class Raider extends PatrollingMonster {
boolean flag = this.hasActiveRaid() && this.getCurrentRaid().getLeader(this.getWave()) != null;
if (this.hasActiveRaid()
&& !flag
- && ItemStack.matches(item, Raid.getOminousBannerInstance(this.registryAccess().lookupOrThrow(Registries.BANNER_PATTERN)))) {
+ && ItemStack.matches(item, getOminousBanner(this.registryAccess().lookupOrThrow(Registries.BANNER_PATTERN)))) { // DivineMC - Optimize Raids
// Paper start - EntityPickupItemEvent fixes
if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPickupItemEvent(this, entity, 0, false).isCancelled()) {
return;
@@ -400,6 +416,16 @@ public abstract class Raider extends PatrollingMonster {
&& !this.cannotPickUpBanner();
}
+ // DivineMC start - Optimize Raids
+ private ItemStack getOminousBanner(net.minecraft.core.HolderGetter<net.minecraft.world.level.block.entity.BannerPattern> bannerPatternLookup) {
+ ItemStack ominousBanner = ((ServerLevel) this.mob.level()).ominousBanner;
+ if (ominousBanner == null) {
+ ominousBanner = Raid.getOminousBannerInstance(bannerPatternLookup);
+ }
+ return ominousBanner;
+ }
+ // DivineMC end - Optimize Raids
+
private boolean cannotPickUpBanner() {
if (!getServerLevel(this.mob).getGameRules().getBoolean(net.minecraft.world.level.GameRules.RULE_MOBGRIEFING, this.mob.level().purpurConfig.pillagerMobGriefingOverride)) return true; // Paper - respect game and entity rules for picking up items // Purpur - Add mobGriefing override to everything affected
if (!this.mob.hasActiveRaid()) {
@@ -409,7 +435,7 @@ public abstract class Raider extends PatrollingMonster {
} else if (!this.mob.canBeLeader()) {
return true;
} else if (ItemStack.matches(
- this.mob.getItemBySlot(EquipmentSlot.HEAD), Raid.getOminousBannerInstance(this.mob.registryAccess().lookupOrThrow(Registries.BANNER_PATTERN))
+ this.mob.getItemBySlot(EquipmentSlot.HEAD), getOminousBanner(this.mob.registryAccess().lookupOrThrow(Registries.BANNER_PATTERN)) // DivineMC - Optimize Raids
)) {
return true;
} else {

View File

@@ -0,0 +1,65 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Mon, 7 Jul 2025 04:21:38 +0300
Subject: [PATCH] Small optimization to LinearPalette
diff --git a/net/minecraft/world/level/chunk/LinearPalette.java b/net/minecraft/world/level/chunk/LinearPalette.java
index 9aa55456d18ffa3ad5cc300ed321582b2a7af1d1..af687853650d9fc58b23d88a7c6e19f36615e8b7 100644
--- a/net/minecraft/world/level/chunk/LinearPalette.java
+++ b/net/minecraft/world/level/chunk/LinearPalette.java
@@ -10,7 +10,7 @@ import org.apache.commons.lang3.Validate;
public class LinearPalette<T> implements Palette<T>, ca.spottedleaf.moonrise.patches.fast_palette.FastPalette<T> { // Paper - optimise palette reads
private final T[] values;
private final int bits;
- private int size;
+ private volatile int size; // DivineMC - Small optimization to LinearPalette
// Paper start - optimise palette reads
@Override
@@ -43,11 +43,16 @@ public class LinearPalette<T> implements Palette<T>, ca.spottedleaf.moonrise.pat
@Override
public int idFor(T state, PaletteResize<T> resizeHandler) {
- for (int i = 0; i < this.size; i++) {
- if (this.values[i] == state) {
+ // DivineMC start - Small optimization to LinearPalette
+ final T[] values = this.values;
+ final int currentSize = this.size;
+
+ for (int i = 0; i < currentSize; i++) {
+ if (values[i] == state) {
return i;
}
}
+ // DivineMC end - Small optimization to LinearPalette
int ix = this.size;
if (ix < this.values.length) {
@@ -61,17 +66,23 @@ public class LinearPalette<T> implements Palette<T>, ca.spottedleaf.moonrise.pat
@Override
public boolean maybeHas(Predicate<T> filter) {
- for (int i = 0; i < this.size; i++) {
- if (filter.test(this.values[i])) {
+ // DivineMC start - Small optimization to LinearPalette
+ final T[] values = this.values;
+ final int currentSize = this.size;
+
+ for (int i = 0; i < currentSize; i++) {
+ T value = values[i];
+ if (value != null && filter.test(value)) {
return true;
}
}
+ // DivineMC end - Small optimization to LinearPalette
return false;
}
@Override
- public T valueFor(int id) {
+ public synchronized T valueFor(int id) { // DivineMC - Small optimization to LinearPalette
if (id >= 0 && id < this.size) {
return this.values[id];
} else {

View File

@@ -0,0 +1,213 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Wed, 9 Jul 2025 02:08:35 +0300
Subject: [PATCH] Optimize VarInt and VarLong write
This patch is based on the following commit:
"Reapply "Optimize varint writing""
By: Andrew Steinborn <git@steinborn.me>
As part of: Velocity (https://github.com/PaperMC/Velocity)
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/net/minecraft/network/VarInt.java b/net/minecraft/network/VarInt.java
index 7a1cafc3fb56054a72591bfb446a433f959a6b28..9bbec7288734899d0b08d0ef2061f0d3c6f265be 100644
--- a/net/minecraft/network/VarInt.java
+++ b/net/minecraft/network/VarInt.java
@@ -50,28 +50,45 @@ public class VarInt {
return i;
}
- public static ByteBuf write(ByteBuf buffer, int value) {
- // Paper start - Optimize VarInts
- // Peel the one and two byte count cases explicitly as they are the most common VarInt sizes
- // that the proxy will write, to improve inlining.
- if ((value & (0xFFFFFFFF << 7)) == 0) {
- buffer.writeByte(value);
- } else if ((value & (0xFFFFFFFF << 14)) == 0) {
- int w = (value & 0x7F | 0x80) << 8 | (value >>> 7);
- buffer.writeShort(w);
- } else {
- writeOld(buffer, value);
- }
- return buffer;
- }
- public static ByteBuf writeOld(ByteBuf buffer, int value) {
- // Paper end - Optimize VarInts
- while ((value & -128) != 0) {
- buffer.writeByte(value & 127 | 128);
- value >>>= 7;
+ // DivineMC start - Optimize VarInt
+ public static ByteBuf write(ByteBuf buffer, int value) {
+ int bytesNeeded = getByteSize(value);
+
+ switch (bytesNeeded) {
+ case 1:
+ buffer.writeByte(value);
+ break;
+ case 2:
+ int w2 = ((value & 0x7F) << 8) | (value >>> 7) | 0x00008000;
+ buffer.writeShort(w2);
+ break;
+ case 3:
+ int w3 = (value & 0x7F) << 16
+ | (value & 0x3F80) << 1
+ | (value >>> 14)
+ | 0x00808000;
+ buffer.writeMedium(w3);
+ break;
+ case 4:
+ int w4 = (value & 0x7F) << 24
+ | ((value & 0x3F80) << 9)
+ | (value & 0x1FC000) >> 6
+ | (value >>> 21)
+ | 0x80808000;
+ buffer.writeInt(w4);
+ break;
+ case 5:
+ int w5 = (value & 0x7F) << 24
+ | (value & 0x3F80) << 9
+ | (value & 0x1FC000) >> 6
+ | ((value >>> 21) & 0x7F)
+ | 0x80808080;
+ buffer.writeInt(w5);
+ buffer.writeByte(value >>> 28);
+ break;
}
- buffer.writeByte(value);
return buffer;
}
+ // DivineMC end - Optimize VarInt
}
diff --git a/net/minecraft/network/VarLong.java b/net/minecraft/network/VarLong.java
index df9a85b19a9767c85f02837af6835f7ddb6c7dc3..77f090bc1b9225dd5a61d8c57c902fcdea8ed7cd 100644
--- a/net/minecraft/network/VarLong.java
+++ b/net/minecraft/network/VarLong.java
@@ -38,13 +38,122 @@ public class VarLong {
return l;
}
+ // DivineMC start - Optimize VarLong
public static ByteBuf write(ByteBuf buffer, long value) {
- while ((value & -128L) != 0L) {
- buffer.writeByte((int)(value & 127L) | 128);
- value >>>= 7;
+ if ((value & 0xFFFFFFFFFFFFFF80L) == 0) {
+ buffer.writeByte((int) value);
+ } else if (value < 0) {
+ int least7bits = (int) (value & 0xFFFFFFFL);
+ int w = (least7bits & 0x7F) << 24
+ | (least7bits & 0x3F80) << 9
+ | (least7bits & 0x1FC000) >> 6
+ | ((least7bits >>> 21) & 0x7F)
+ | 0x80808080;
+ long nonLeast7Bits = value >>> 28;
+ int secondLeast7bits = (int) (nonLeast7Bits & 0xFFFFFFFL);
+ int w2 = (secondLeast7bits & 0x7F) << 24
+ | ((secondLeast7bits & 0x3F80) << 9)
+ | (secondLeast7bits & 0x1FC000) >> 6
+ | (secondLeast7bits >>> 21)
+ | 0x80808080;
+ int thirdLeast7Bits = (int) (nonLeast7Bits >>> 28);
+ int w3 = (thirdLeast7Bits & 0x7F) << 8
+ | (thirdLeast7Bits >>> 7)
+ | 0x00008000;
+ buffer.writeInt(w);
+ buffer.writeInt(w2);
+ buffer.writeShort(w3);
+ } else if ((value & 0xFFFFFFFFFFFFC000L) == 0) {
+ int least7bits = (int) value;
+ int w = (least7bits & 0x7F) << 8
+ | (least7bits >>> 7)
+ | 0x00008000;
+ buffer.writeShort(w);
+ } else if ((value & 0xFFFFFFFFFFE00000L) == 0) {
+ int least7bits = (int) value;
+ int w = (least7bits & 0x7F) << 16
+ | (least7bits & 0x3F80) << 1
+ | (least7bits >>> 14)
+ | 0x00808000;
+ buffer.writeMedium(w);
+ } else if ((value & 0xFFFFFFFFF0000000L) == 0) {
+ int least7bits = (int) value;
+ int w = (least7bits & 0x7F) << 24
+ | ((least7bits & 0x3F80) << 9)
+ | (least7bits & 0x1FC000) >> 6
+ | (least7bits >>> 21)
+ | 0x80808000;
+ buffer.writeInt(w);
+ } else if ((value & 0xFFFFFFF800000000L) == 0) {
+ int least7bits = (int) (value & 0xFFFFFFFL);
+ int w = (least7bits & 0x7F) << 24
+ | (least7bits & 0x3F80) << 9
+ | (least7bits & 0x1FC000) >> 6
+ | ((least7bits >>> 21) & 0x7F)
+ | 0x80808080;
+ buffer.writeInt(w);
+ buffer.writeByte((int) (value >>> 28));
+ } else if ((value & 0xFFFFFC0000000000L) == 0) {
+ int least7bits = (int) (value & 0xFFFFFFFL);
+ int w = (least7bits & 0x7F) << 24
+ | (least7bits & 0x3F80) << 9
+ | (least7bits & 0x1FC000) >> 6
+ | ((least7bits >>> 21) & 0x7F)
+ | 0x80808080;
+ int secondLeast7bits = (int) (value >>> 28);
+ int w2 = (secondLeast7bits & 0x7F) << 8
+ | (secondLeast7bits >>> 7)
+ | 0x00008000;
+ buffer.writeInt(w);
+ buffer.writeShort(w2);
+ } else if ((value & 0xFFFE000000000000L) == 0) {
+ int least7bits = (int) (value & 0xFFFFFFFL);
+ int w = (least7bits & 0x7F) << 24
+ | (least7bits & 0x3F80) << 9
+ | (least7bits & 0x1FC000) >> 6
+ | ((least7bits >>> 21) & 0x7F)
+ | 0x80808080;
+ int secondLeast7bits = (int) (value >>> 28);
+ int w2 = (secondLeast7bits & 0x7F) << 16
+ | (secondLeast7bits & 0x3F80) << 1
+ | (secondLeast7bits >>> 14)
+ | 0x00808000;
+ buffer.writeInt(w);
+ buffer.writeMedium(w2);
+ } else if ((value & 0xFF00000000000000L) == 0) {
+ int least7bits = (int) (value & 0xFFFFFFFL);
+ int w = (least7bits & 0x7F) << 24
+ | (least7bits & 0x3F80) << 9
+ | (least7bits & 0x1FC000) >> 6
+ | ((least7bits >>> 21) & 0x7F)
+ | 0x80808080;
+ int secondLeast7bits = (int) (value >>> 28);
+ int w2 = (secondLeast7bits & 0x7F) << 24
+ | ((secondLeast7bits & 0x3F80) << 9)
+ | (secondLeast7bits & 0x1FC000) >> 6
+ | (secondLeast7bits >>> 21)
+ | 0x80808000;
+ buffer.writeInt(w);
+ buffer.writeInt(w2);
+ } else {
+ int least7bits = (int) (value & 0xFFFFFFFL);
+ int w = (least7bits & 0x7F) << 24
+ | (least7bits & 0x3F80) << 9
+ | (least7bits & 0x1FC000) >> 6
+ | ((least7bits >>> 21) & 0x7F)
+ | 0x80808080;
+ long nonLeast7Bits = value >>> 28;
+ int secondLeast7bits = (int) (nonLeast7Bits & 0xFFFFFFFL);
+ int w2 = (secondLeast7bits & 0x7F) << 24
+ | ((secondLeast7bits & 0x3F80) << 9)
+ | (secondLeast7bits & 0x1FC000) >> 6
+ | (secondLeast7bits >>> 21)
+ | 0x80808080;
+ buffer.writeInt(w);
+ buffer.writeInt(w2);
+ buffer.writeByte((int) (nonLeast7Bits >>> 28));
}
-
- buffer.writeByte((int)value);
return buffer;
}
+ // DivineMC end - Optimize VarLong
}

View File

@@ -0,0 +1,145 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Fri, 31 Jan 2025 22:40:54 +0300
Subject: [PATCH] Optimize Fluids
diff --git a/net/minecraft/world/level/block/LiquidBlock.java b/net/minecraft/world/level/block/LiquidBlock.java
index 34a10afcc242e4e37673918ee271d683e35385b7..2f278b528acfd3df1aff4438e31e6e81fbd6cf70 100644
--- a/net/minecraft/world/level/block/LiquidBlock.java
+++ b/net/minecraft/world/level/block/LiquidBlock.java
@@ -197,6 +197,7 @@ public class LiquidBlock extends Block implements BucketPickup {
Block block = level.getFluidState(pos).isSource() ? Blocks.OBSIDIAN : Blocks.COBBLESTONE;
// CraftBukkit start
if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(level, pos, block.defaultBlockState(), 3)) {
+ level.setBlock(pos, block.defaultBlockState(), 3); // DivineMC - Optimize Fluids
this.fizz(level, pos);
}
// CraftBukkit end
diff --git a/net/minecraft/world/level/material/FlowingFluid.java b/net/minecraft/world/level/material/FlowingFluid.java
index e1a96f2448de90efef48e21ce4d673edbceda59a..7a14227f07b2b94bff7c67eb7f7f11cdd45c7b62 100644
--- a/net/minecraft/world/level/material/FlowingFluid.java
+++ b/net/minecraft/world/level/material/FlowingFluid.java
@@ -200,6 +200,7 @@ public abstract class FlowingFluid extends Fluid {
BlockPos blockPos = pos.relative(direction);
final BlockState blockStateIfLoaded = level.getBlockStateIfLoaded(blockPos); // Paper - Prevent chunk loading from fluid flowing
if (blockStateIfLoaded == null) continue; // Paper - Prevent chunk loading from fluid flowing
+ if (!shouldSpreadLiquid(level, blockPos, blockStateIfLoaded)) continue; // DivineMC - Optimize Fluids
// CraftBukkit start
org.bukkit.block.Block source = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos);
org.bukkit.event.block.BlockFromToEvent event = new org.bukkit.event.block.BlockFromToEvent(source, org.bukkit.craftbukkit.block.CraftBlock.notchToBlockFace(direction));
@@ -214,6 +215,39 @@ public abstract class FlowingFluid extends Fluid {
}
}
+ // DivineMC start - Optimize Fluids
+ private boolean shouldSpreadLiquid(Level level, BlockPos pos, BlockState state) {
+ if (state.is(Blocks.LAVA)) {
+ boolean isSoulSoil = level.getBlockState(pos.below()).is(Blocks.SOUL_SOIL);
+
+ for (Direction direction : net.minecraft.world.level.block.LiquidBlock.POSSIBLE_FLOW_DIRECTIONS) {
+ BlockPos blockPos = pos.relative(direction.getOpposite());
+ if (level.getFluidState(blockPos).is(net.minecraft.tags.FluidTags.WATER)) {
+ Block block = level.getFluidState(pos).isSource() ? Blocks.OBSIDIAN : Blocks.COBBLESTONE;
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(level, pos, block.defaultBlockState(), 3)) {
+ this.fizz(level, pos);
+ level.setBlock(pos, block.defaultBlockState(), 3);
+ }
+ return false;
+ }
+
+ if (isSoulSoil && level.getBlockState(blockPos).is(Blocks.BLUE_ICE)) {
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(level, pos, Blocks.BASALT.defaultBlockState(), 3)) {
+ this.fizz(level, pos);
+ }
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private void fizz(LevelAccessor level, BlockPos pos) {
+ level.levelEvent(1501, pos, 0);
+ }
+ // DivineMC end - Optimize Fluids
+
protected FluidState getNewLiquid(ServerLevel level, BlockPos pos, BlockState state) {
int i = 0;
int i1 = 0;
@@ -343,33 +377,46 @@ public abstract class FlowingFluid extends Fluid {
protected abstract void beforeDestroyingBlock(LevelAccessor level, BlockPos pos, BlockState state);
+ // DivineMC start - Optimize Fluids
protected int getSlopeDistance(LevelReader level, BlockPos pos, int depth, Direction direction, BlockState state, FlowingFluid.SpreadContext spreadContext) {
- int i = 1000;
+ int slopeFindDistance = this.getSlopeFindDistance(level);
+ int minDistance = slopeFindDistance;
- for (Direction direction1 : Direction.Plane.HORIZONTAL) {
- if (direction1 != direction) {
- BlockPos blockPos = pos.relative(direction1);
- BlockState blockState = spreadContext.getBlockStateIfLoaded(blockPos); // Paper - Prevent chunk loading from fluid flowing
- if (blockState == null) continue; // Paper - Prevent chunk loading from fluid flowing
- FluidState fluidState = blockState.getFluidState();
- if (this.canPassThrough(level, this.getFlowing(), pos, state, direction1, blockPos, blockState, fluidState)) {
- if (spreadContext.isHole(blockPos)) {
- return depth;
+ java.util.Deque<net.minecraft.world.level.material.FlowingFluid.Node> stack = new java.util.ArrayDeque<>();
+ stack.push(new Node(pos, depth, direction));
+
+ while (!stack.isEmpty()) {
+ Node current = stack.pop();
+ BlockPos currentPos = current.pos;
+ int currentDepth = current.depth;
+ Direction fromDirection = current.direction;
+
+ for (Direction dir : Direction.Plane.HORIZONTAL) {
+ if (dir == fromDirection) continue;
+
+ BlockPos neighborPos = currentPos.relative(dir);
+ BlockState neighborState = spreadContext.getBlockStateIfLoaded(neighborPos);
+ if (neighborState == null) continue; // Prevent chunk loading
+
+ FluidState fluidState = neighborState.getFluidState();
+ if (this.canPassThrough(level, this.getFlowing(), currentPos, state, dir, neighborPos, neighborState, fluidState)) {
+ if (spreadContext.isHole(neighborPos)) {
+ return currentDepth;
}
- if (depth < this.getSlopeFindDistance(level)) {
- int slopeDistance = this.getSlopeDistance(level, blockPos, depth + 1, direction1.getOpposite(), blockState, spreadContext);
- if (slopeDistance < i) {
- i = slopeDistance;
- }
+ if (currentDepth + 1 < slopeFindDistance && currentDepth + 1 < minDistance) {
+ stack.push(new Node(neighborPos, currentDepth + 1, dir.getOpposite()));
}
}
}
}
- return i;
+ return minDistance;
}
+ private record Node(BlockPos pos, int depth, Direction direction) { }
+ // DivineMC end - Optimize Fluids
+
boolean isWaterHole(BlockGetter level, BlockPos pos, BlockState state, BlockPos belowPos, BlockState belowState) {
return canPassThroughWall(Direction.DOWN, level, pos, state, belowPos, belowState)
&& (belowState.getFluidState().getType().isSame(this) || canHoldFluid(level, belowPos, belowState, this.getFlowing()));
diff --git a/net/minecraft/world/level/material/LavaFluid.java b/net/minecraft/world/level/material/LavaFluid.java
index 545c75cfd03a36d4e8b96a69fc8dcd893b548eeb..bbc658abfb2df1b3ee3a31d46c4b9014bb34e0a2 100644
--- a/net/minecraft/world/level/material/LavaFluid.java
+++ b/net/minecraft/world/level/material/LavaFluid.java
@@ -237,6 +237,7 @@ public abstract class LavaFluid extends FlowingFluid {
// CraftBukkit end
}
+ level.setBlock(pos, Blocks.STONE.defaultBlockState(), 3); // DivineMC - Optimize Fluids
this.fizz(level, pos);
return;
}

View File

@@ -0,0 +1,277 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Tue, 4 Feb 2025 19:52:24 +0300
Subject: [PATCH] Optimize Structure Generation
Original project: https://github.com/TelepathicGrunt/StructureLayoutOptimizer
Original license: MIT
diff --git a/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java b/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java
index 4e3302e8f28c7f12dc5fbaeb6c1f5715a9afc8e5..3b66b16612b8328eaea786cc8ba3a59fd67900e4 100644
--- a/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java
+++ b/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java
@@ -4,6 +4,8 @@ import com.google.common.collect.Lists;
import com.mojang.logging.LogUtils;
import java.util.List;
import java.util.Optional;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Holder;
@@ -288,6 +290,108 @@ public class JigsawPlacement {
this.random = random;
}
+ // DivineMC start - Optimize Structure Generation
+ private boolean structureLayoutOptimizer$optimizeJigsawConnecting(StructureTemplate.JigsawBlockInfo jigsaw1, StructureTemplate.JigsawBlockInfo jigsaw2) {
+ if (!org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.enableStructureLayoutOptimizer) {
+ return JigsawBlock.canAttach(jigsaw1, jigsaw2);
+ }
+ return org.bxteam.divinemc.util.structure.GeneralUtils.canJigsawsAttach(jigsaw1, jigsaw2);
+ }
+
+ private void structureLayoutOptimizer$replaceVoxelShape3(MutableObject<VoxelShape> instance, BoundingBox pieceBounds) {
+ org.bxteam.divinemc.util.structure.TrojanVoxelShape trojanVoxelShape = new org.bxteam.divinemc.util.structure.TrojanVoxelShape(new org.bxteam.divinemc.util.structure.BoxOctree(AABB.of(pieceBounds)));
+ instance.setValue(trojanVoxelShape);
+ }
+
+ private void structureLayoutOptimizer$replaceVoxelShape4(MutableObject<VoxelShape> instance, BoundingBox pieceBounds) {
+ if (instance.getValue() instanceof org.bxteam.divinemc.util.structure.TrojanVoxelShape trojanVoxelShape) {
+ trojanVoxelShape.boxOctree.addBox(AABB.of(pieceBounds));
+ }
+ }
+
+ private List<StructurePoolElement> structureLayoutOptimizer$removeDuplicateTemplatePoolElementLists(StructureTemplatePool instance, RandomSource random) {
+ if (!org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.enableStructureLayoutOptimizer || !org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.deduplicateShuffledTemplatePoolElementList) {
+ return instance.getShuffledTemplates(random);
+ }
+
+ // Linked hashset keeps order of elements.
+ LinkedHashSet<StructurePoolElement> uniquePieces = new LinkedHashSet<>((instance).rawTemplates.size());
+
+ // Don't use addAll. Want to keep it simple in case of inefficiency in collection's addAll.
+ // Set will ignore duplicates after first appearance of an element.
+ for (StructurePoolElement piece : instance.getShuffledTemplates(random)) {
+ //noinspection UseBulkOperation
+ uniquePieces.add(piece);
+ }
+
+ // Move the elements from set to the list in the same order.
+ int uniquePiecesFound = uniquePieces.size();
+ List<StructurePoolElement> deduplicatedListOfPieces = new ArrayList<>(uniquePiecesFound);
+ for (int i = 0; i < uniquePiecesFound; i++) {
+ deduplicatedListOfPieces.add(uniquePieces.removeFirst());
+ }
+
+ return deduplicatedListOfPieces;
+ }
+
+ private ArrayList<StructurePoolElement> structureLayoutOptimizer$skipDuplicateTemplatePoolElementLists1() {
+ // Swap with trojan list, so we can record what pieces we visited
+ return org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.deduplicateShuffledTemplatePoolElementList ? Lists.newArrayList() : new org.bxteam.divinemc.util.structure.TrojanArrayList<>();
+ }
+
+ private List structureLayoutOptimizer$skipBlockedJigsaws(
+ List original,
+ boolean useExpansionHack,
+ MutableObject<VoxelShape> voxelShapeMutableObject,
+ StructurePoolElement structurePoolElement,
+ StructureTemplate.StructureBlockInfo parentJigsawBlockInfo,
+ BlockPos parentTargetPosition)
+ {
+ if (!org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.enableStructureLayoutOptimizer) {
+ return original;
+ }
+ if (voxelShapeMutableObject.getValue() instanceof org.bxteam.divinemc.util.structure.TrojanVoxelShape trojanVoxelShape) {
+ // If rigid and target position is already an invalid spot, do not run rest of logic.
+ StructureTemplatePool.Projection candidatePlacementBehavior = structurePoolElement.getProjection();
+ boolean isCandidateRigid = candidatePlacementBehavior == StructureTemplatePool.Projection.RIGID;
+ if (isCandidateRigid && (!trojanVoxelShape.boxOctree.boundaryContains(parentTargetPosition) || trojanVoxelShape.boxOctree.withinAnyBox(parentTargetPosition))) {
+ return new ArrayList<>();
+ }
+ }
+ return original;
+ }
+
+ private List<Rotation> structureLayoutOptimizer$skipDuplicateTemplatePoolElementLists2(List<Rotation> original,
+ List<StructurePoolElement> list,
+ StructurePoolElement structurepoolelement1)
+ {
+ if (!org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.enableStructureLayoutOptimizer) {
+ return original;
+ }
+ if (!org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.deduplicateShuffledTemplatePoolElementList && list instanceof org.bxteam.divinemc.util.structure.TrojanArrayList<net.minecraft.world.level.levelgen.structure.pools.StructurePoolElement> trojanArrayList) {
+ // Do not run this piece's logic since we already checked its 4 rotations in the past.
+ if (trojanArrayList.elementsAlreadyParsed.contains(structurepoolelement1)) {
+
+ // Prime the random with the random calls we would've skipped.
+ // Maintains vanilla compat.
+ for (Rotation rotation1 : original) {
+ structurepoolelement1.getShuffledJigsawBlocks(this.structureTemplateManager, BlockPos.ZERO, rotation1, this.random);
+ }
+
+ // Short circuit the Rotation loop
+ return new ArrayList<>();
+ }
+ // Record piece as it will go through the 4 rotation checks for spawning.
+ else {
+ trojanArrayList.elementsAlreadyParsed.add(structurepoolelement1);
+ }
+ }
+
+ // Allow the vanilla code to run normally.
+ return original;
+ }
+ // DivineMC end - Optimize Structure Generation
+
void tryPlacingChildren(
PoolElementStructurePiece piece,
MutableObject<VoxelShape> free,
@@ -345,9 +449,9 @@ public class JigsawPlacement {
mutableObject1 = free;
}
- List<StructurePoolElement> list = Lists.newArrayList();
+ List<StructurePoolElement> list = structureLayoutOptimizer$skipDuplicateTemplatePoolElementLists1(); // DivineMC - Optimize Structure Generation
if (depth != this.maxDepth) {
- list.addAll(holder.value().getShuffledTemplates(this.random));
+ list.addAll(structureLayoutOptimizer$removeDuplicateTemplatePoolElementLists(holder.value(), this.random)); // DivineMC - Optimize Structure Generation
}
list.addAll(fallback.value().getShuffledTemplates(this.random));
@@ -358,10 +462,14 @@ public class JigsawPlacement {
break;
}
- for (Rotation rotation1 : Rotation.getShuffled(this.random)) {
- List<StructureTemplate.JigsawBlockInfo> shuffledJigsawBlocks = structurePoolElement.getShuffledJigsawBlocks(
+ // DivineMC start - Optimize Structure Generation
+ for (Rotation rotation1 : structureLayoutOptimizer$skipDuplicateTemplatePoolElementLists2(Rotation.getShuffled(this.random), list, structurePoolElement)) {
+ List<StructureTemplate.JigsawBlockInfo> shuffledJigsawBlocks = structureLayoutOptimizer$skipBlockedJigsaws(
+ structurePoolElement.getShuffledJigsawBlocks(
this.structureTemplateManager, BlockPos.ZERO, rotation1, this.random
+ ), useExpansionHack, mutableObject1, structurePoolElement, structureBlockInfo, blockPos1
);
+ // DivineMC end - Optimize Structure Generation
BoundingBox boundingBox1 = structurePoolElement.getBoundingBox(this.structureTemplateManager, BlockPos.ZERO, rotation1);
int i2;
if (useExpansionHack && boundingBox1.getYSpan() <= 16) {
@@ -394,7 +502,7 @@ public class JigsawPlacement {
}
for (StructureTemplate.JigsawBlockInfo jigsawBlockInfo1 : shuffledJigsawBlocks) {
- if (JigsawBlock.canAttach(jigsawBlockInfo, jigsawBlockInfo1)) {
+ if (structureLayoutOptimizer$optimizeJigsawConnecting(jigsawBlockInfo, jigsawBlockInfo1)) { // DivineMC - Optimize Structure Generation
BlockPos blockPos2 = jigsawBlockInfo1.info().pos();
BlockPos blockPos3 = blockPos1.subtract(blockPos2);
BoundingBox boundingBox2 = structurePoolElement.getBoundingBox(this.structureTemplateManager, blockPos3, rotation1);
@@ -423,9 +531,26 @@ public class JigsawPlacement {
boundingBox3.encapsulate(new BlockPos(boundingBox3.minX(), boundingBox3.minY() + max, boundingBox3.minZ()));
}
- if (!Shapes.joinIsNotEmpty(
- mutableObject1.getValue(), Shapes.create(AABB.of(boundingBox3).deflate(0.25)), BooleanOp.ONLY_SECOND
- )) {
+ // DivineMC start - Optimize Structure Generation
+ boolean internal$joinIsNotEmpty;
+ VoxelShape parentBounds = mutableObject1.getValue();
+ java.util.function.Supplier<Boolean> original = () -> Shapes.joinIsNotEmpty(
+ parentBounds, Shapes.create(AABB.of(boundingBox3).deflate(0.25)), BooleanOp.ONLY_SECOND
+ );
+ if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.enableStructureLayoutOptimizer) {
+ if (parentBounds instanceof org.bxteam.divinemc.util.structure.TrojanVoxelShape trojanVoxelShape) {
+ AABB pieceAABB = AABB.of(boundingBox3).deflate(0.25D);
+
+ // Have to inverse because of an ! outside our wrap
+ internal$joinIsNotEmpty = !trojanVoxelShape.boxOctree.withinBoundsButNotIntersectingChildren(pieceAABB);
+ } else {
+ internal$joinIsNotEmpty = original.get();
+ }
+ } else {
+ internal$joinIsNotEmpty = original.get();
+ }
+ if (!internal$joinIsNotEmpty) {
+ // DivineMC end - Optimize Structure Generation
mutableObject1.setValue(
Shapes.joinUnoptimized(
mutableObject1.getValue(), Shapes.create(AABB.of(boundingBox3)), BooleanOp.ONLY_FIRST
diff --git a/net/minecraft/world/level/levelgen/structure/pools/SinglePoolElement.java b/net/minecraft/world/level/levelgen/structure/pools/SinglePoolElement.java
index 5c081a5b3d10f713e4e82fe1a43758f553fe50e0..85e84603a19964f05d9d5e62eb096ca76c36ab00 100644
--- a/net/minecraft/world/level/levelgen/structure/pools/SinglePoolElement.java
+++ b/net/minecraft/world/level/levelgen/structure/pools/SinglePoolElement.java
@@ -119,8 +119,16 @@ public class SinglePoolElement extends StructurePoolElement {
StructureTemplateManager structureTemplateManager, BlockPos pos, Rotation rotation, RandomSource random
) {
List<StructureTemplate.JigsawBlockInfo> jigsaws = this.getTemplate(structureTemplateManager).getJigsaws(pos, rotation);
- Util.shuffle(jigsaws, random);
- sortBySelectionPriority(jigsaws);
+ // DivineMC start - Optimize Structure Generation
+ if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.enableStructureLayoutOptimizer) {
+ structureLayoutOptimizer$fasterJigsawListShuffling1(jigsaws, random);
+ structureLayoutOptimizer$fasterJigsawListShuffling2(jigsaws);
+ } else {
+ Util.shuffle(jigsaws, random);
+ sortBySelectionPriority(jigsaws);
+ }
+ // DivineMC end - Optimize Structure Generation
+
return jigsaws;
}
@@ -196,4 +204,12 @@ public class SinglePoolElement extends StructurePoolElement {
public ResourceLocation getTemplateLocation() {
return this.template.orThrow();
}
+
+ // DivineMC start - Optimize Structure Generation
+ private void structureLayoutOptimizer$fasterJigsawListShuffling1(List<StructureTemplate.JigsawBlockInfo> list, RandomSource randomSource) {
+ org.bxteam.divinemc.util.structure.GeneralUtils.shuffleAndPrioritize(list, randomSource);
+ }
+
+ private void structureLayoutOptimizer$fasterJigsawListShuffling2(List<StructureTemplate.JigsawBlockInfo> structureBlockInfos) { }
+ // DivineMC end - Optimize Structure Generation
}
diff --git a/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java b/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java
index 4068becb9eca9cbbb67e6d6fb9ff848bfc25877e..d8756e4bfc669eb17c60429c4b532b13f1c308ec 100644
--- a/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java
+++ b/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java
@@ -256,6 +256,12 @@ public class StructureTemplate {
return transform(pos, decorator.getMirror(), decorator.getRotation(), decorator.getRotationPivot());
}
+ // DivineMC start - Optimize Structure Generation
+ private List<StructureTemplate.StructureBlockInfo> structureLayoutOptimizer$shrinkStructureTemplateBlocksList(StructureTemplate.Palette palette, BlockPos offset, StructurePlaceSettings settings) {
+ return org.bxteam.divinemc.util.structure.StructureTemplateOptimizer.getStructureBlockInfosInBounds(palette, offset, settings);
+ }
+ // DivineMC end - Optimize Structure Generation
+
public boolean placeInWorld(ServerLevelAccessor serverLevel, BlockPos offset, BlockPos pos, StructurePlaceSettings settings, RandomSource random, int flags) {
if (this.palettes.isEmpty()) {
return false;
@@ -273,7 +279,11 @@ public class StructureTemplate {
}
}
// CraftBukkit end
- List<StructureTemplate.StructureBlockInfo> list = settings.getRandomPalette(this.palettes, offset).blocks();
+ // DivineMC start - Optimize Structure Generation
+ List<StructureTemplate.StructureBlockInfo> list = org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.enableStructureLayoutOptimizer
+ ? structureLayoutOptimizer$shrinkStructureTemplateBlocksList(settings.getRandomPalette(this.palettes, offset), offset, settings)
+ : settings.getRandomPalette(this.palettes, offset).blocks();
+ // DivineMC end - Optimize Structure Generation
if ((!list.isEmpty() || !settings.isIgnoreEntities() && !this.entityInfoList.isEmpty())
&& this.size.getX() >= 1
&& this.size.getY() >= 1
@@ -885,7 +895,11 @@ public class StructureTemplate {
private List<StructureTemplate.JigsawBlockInfo> cachedJigsaws;
Palette(List<StructureTemplate.StructureBlockInfo> blocks) {
- this.blocks = blocks;
+ // DivineMC start - Optimize Structure Generation
+ this.blocks = org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.enableStructureLayoutOptimizer
+ ? new org.bxteam.divinemc.util.structure.PalettedStructureBlockInfoList(blocks)
+ : blocks;
+ // DivineMC end - Optimize Structure Generation
}
public List<StructureTemplate.JigsawBlockInfo> jigsaws() {

View File

@@ -0,0 +1,309 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Tue, 4 Feb 2025 01:49:17 +0300
Subject: [PATCH] Implement NoChatReports
diff --git a/net/minecraft/network/FriendlyByteBuf.java b/net/minecraft/network/FriendlyByteBuf.java
index 70b35188e4762c8dcd46b2df14d4efd85e147dcd..8861148b0598891e7851d2c9cc6d79edfc26e55e 100644
--- a/net/minecraft/network/FriendlyByteBuf.java
+++ b/net/minecraft/network/FriendlyByteBuf.java
@@ -105,7 +105,28 @@ public class FriendlyByteBuf extends ByteBuf {
return this;
}
+ @SuppressWarnings({"unchecked", "rawtypes"}) // DivineMC - Implement NoChatReports
public <T> T readLenientJsonWithCodec(Codec<T> codec) {
+ // DivineMC start - Implement NoChatReports
+ if (org.bxteam.divinemc.config.DivineConfig.NetworkCategory.noChatReportsEnabled) {
+ if (codec == net.minecraft.network.protocol.status.ServerStatus.CODEC) {
+ JsonElement jsonElement = LenientJsonParser.parse(this.readUtf());
+ DataResult dataResult = codec.parse(JsonOps.INSTANCE, jsonElement);
+ Object result;
+ try {
+ result = dataResult.getOrThrow(string -> new DecoderException("Failed to decode json: " + string));
+ } catch (Throwable e) {
+ throw new RuntimeException("Unable to decode json!", e);
+ }
+
+ if (jsonElement.getAsJsonObject().has("preventsChatReports")) {
+ ((net.minecraft.network.protocol.status.ServerStatus) result).setPreventsChatReports(jsonElement.getAsJsonObject().get("preventsChatReports").getAsBoolean());
+ }
+
+ return (T) (result);
+ }
+ }
+ // DivineMC end - Implement NoChatReports
JsonElement jsonElement = LenientJsonParser.parse(this.readUtf());
DataResult<T> dataResult = codec.parse(JsonOps.INSTANCE, jsonElement);
return dataResult.getOrThrow(string -> new DecoderException("Failed to decode JSON: " + string));
@@ -117,6 +138,19 @@ public class FriendlyByteBuf extends ByteBuf {
}
public <T> void writeJsonWithCodec(Codec<T> codec, T value, int maxLength) {
// Paper end - Adventure; add max length parameter
+ // DivineMC start - Implement NoChatReports
+ if (org.bxteam.divinemc.config.DivineConfig.NetworkCategory.noChatReportsEnabled) {
+ if (org.bxteam.divinemc.config.DivineConfig.NetworkCategory.noChatReportsAddQueryData && codec == net.minecraft.network.protocol.status.ServerStatus.CODEC) {
+ DataResult<JsonElement> dataResult = codec.encodeStart(JsonOps.INSTANCE, value);
+ JsonElement element = dataResult.getOrThrow(string -> new EncoderException("Failed to encode: " + string + " " + value));
+
+ element.getAsJsonObject().addProperty("preventsChatReports", true);
+
+ this.writeUtf(GSON.toJson(element));
+ return;
+ }
+ }
+ // DivineMC end - Implement NoChatReports
DataResult<JsonElement> dataResult = codec.encodeStart(JsonOps.INSTANCE, value);
this.writeUtf(GSON.toJson(dataResult.getOrThrow(exception -> new EncoderException("Failed to encode: " + exception + " " + value))), maxLength); // Paper - Adventure; add max length parameter
}
diff --git a/net/minecraft/network/protocol/game/ServerboundChatCommandSignedPacket.java b/net/minecraft/network/protocol/game/ServerboundChatCommandSignedPacket.java
index 07943553b562b95076bdce232d6f0796f469400f..478c07e8c569d35761ce138cf1deed9511b826d6 100644
--- a/net/minecraft/network/protocol/game/ServerboundChatCommandSignedPacket.java
+++ b/net/minecraft/network/protocol/game/ServerboundChatCommandSignedPacket.java
@@ -36,4 +36,15 @@ public record ServerboundChatCommandSignedPacket(
public void handle(ServerGamePacketListener handler) {
handler.handleSignedChatCommand(this);
}
+
+ // DivineMC start - Implement NoChatReports
+ @Override
+ public ArgumentSignatures argumentSignatures() {
+ if (org.bxteam.divinemc.config.DivineConfig.NetworkCategory.noChatReportsEnabled) {
+ return ArgumentSignatures.EMPTY;
+ }
+
+ return argumentSignatures;
+ }
+ // DivineMC end - Implement NoChatReports
}
diff --git a/net/minecraft/network/protocol/game/ServerboundChatPacket.java b/net/minecraft/network/protocol/game/ServerboundChatPacket.java
index b5afc05924ae899e020c303c8b86398e1d4ab8a0..2a6fdec4faae3512060cbb21a2043129765a480e 100644
--- a/net/minecraft/network/protocol/game/ServerboundChatPacket.java
+++ b/net/minecraft/network/protocol/game/ServerboundChatPacket.java
@@ -36,4 +36,16 @@ public record ServerboundChatPacket(String message, Instant timeStamp, long salt
public void handle(ServerGamePacketListener handler) {
handler.handleChat(this);
}
+
+ // DivineMC start - Implement NoChatReports
+ @Override
+ @Nullable
+ public MessageSignature signature() {
+ if (org.bxteam.divinemc.config.DivineConfig.NetworkCategory.noChatReportsEnabled) {
+ return null;
+ }
+
+ return signature;
+ }
+ // DivineMC end - Implement NoChatReports
}
diff --git a/net/minecraft/network/protocol/game/ServerboundChatSessionUpdatePacket.java b/net/minecraft/network/protocol/game/ServerboundChatSessionUpdatePacket.java
index 1df628ac0b414511aaed6e09d78f884c4170f730..1543f730843c1736c4db9a6ebe30be9cc9fbe36a 100644
--- a/net/minecraft/network/protocol/game/ServerboundChatSessionUpdatePacket.java
+++ b/net/minecraft/network/protocol/game/ServerboundChatSessionUpdatePacket.java
@@ -26,6 +26,19 @@ public record ServerboundChatSessionUpdatePacket(RemoteChatSession.Data chatSess
@Override
public void handle(ServerGamePacketListener handler) {
+ // DivineMC start - Implement NoChatReports
+ if (org.bxteam.divinemc.config.DivineConfig.NetworkCategory.noChatReportsEnabled) {
+ var impl = (net.minecraft.server.network.ServerGamePacketListenerImpl) handler;
+
+ if (!impl.getPlayer().getServer().isSingleplayerOwner(impl.getPlayer().getGameProfile())) {
+ if (org.bxteam.divinemc.config.DivineConfig.NetworkCategory.noChatReportsDemandOnClient) {
+ impl.disconnect(net.minecraft.network.chat.Component.literal(org.bxteam.divinemc.config.DivineConfig.NetworkCategory.noChatReportsDisconnectDemandOnClientMessage));
+ }
+ }
+
+ return;
+ }
+ // DivineMC end - Implement NoChatReports
handler.handleChatSessionUpdate(this);
}
}
diff --git a/net/minecraft/network/protocol/status/ServerStatus.java b/net/minecraft/network/protocol/status/ServerStatus.java
index 88447fc2108126ccfad2fb7eb79ac94537f132d3..571cbe6844ebb330801dd893a45c1f735ee87531 100644
--- a/net/minecraft/network/protocol/status/ServerStatus.java
+++ b/net/minecraft/network/protocol/status/ServerStatus.java
@@ -14,13 +14,7 @@ import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.ComponentSerialization;
import net.minecraft.server.players.NameAndId;
-public record ServerStatus(
- Component description,
- Optional<ServerStatus.Players> players,
- Optional<ServerStatus.Version> version,
- Optional<ServerStatus.Favicon> favicon,
- boolean enforcesSecureChat
-) {
+public final class ServerStatus {
public static final Codec<ServerStatus> CODEC = RecordCodecBuilder.create(
instance -> instance.group(
ComponentSerialization.CODEC.lenientOptionalFieldOf("description", CommonComponents.EMPTY).forGetter(ServerStatus::description),
@@ -32,6 +26,63 @@ public record ServerStatus(
.apply(instance, ServerStatus::new)
);
+ // DivineMC start - Implement NoChatReports - convert to class
+ private final Component description;
+ private final Optional<Players> players;
+ private final Optional<Version> version;
+ private final Optional<Favicon> favicon;
+ private final boolean enforcesSecureChat;
+ private boolean preventsChatReports;
+
+ public ServerStatus(
+ Component description,
+ Optional<Players> players,
+ Optional<Version> version,
+ Optional<Favicon> favicon,
+ boolean enforcesSecureChat
+ ) {
+ this.description = description;
+ this.players = players;
+ this.version = version;
+ this.favicon = favicon;
+ this.enforcesSecureChat = enforcesSecureChat;
+ }
+
+ public Component description() {
+ return description;
+ }
+
+ public Optional<Players> players() {
+ return players;
+ }
+
+ public Optional<Version> version() {
+ return version;
+ }
+
+ public Optional<Favicon> favicon() {
+ return favicon;
+ }
+
+ public boolean enforcesSecureChat() {
+ return enforcesSecureChat;
+ }
+
+ public boolean preventsChatReports() {
+ var self = (ServerStatus) (Object) this;
+
+ if (self.version().isPresent() && self.version().get().protocol() < 759
+ && self.version().get().protocol() > 0)
+ return true;
+
+ return this.preventsChatReports;
+ }
+
+ public void setPreventsChatReports(boolean prevents) {
+ this.preventsChatReports = prevents;
+ }
+ // DivineMC end - Implement NoChatReports
+
public record Favicon(byte[] iconBytes) {
private static final String PREFIX = "data:image/png;base64,";
public static final Codec<ServerStatus.Favicon> CODEC = Codec.STRING.comapFlatMap(string -> {
diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java
index 7486df33cedea98695c614bb3d584e1d8e84314f..f9ec63ed7feb67e558abed99746726c10e9e10f4 100644
--- a/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/net/minecraft/server/dedicated/DedicatedServer.java
@@ -833,6 +833,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
@Override
public boolean enforceSecureProfile() {
+ if (org.bxteam.divinemc.config.DivineConfig.NetworkCategory.noChatReportsEnabled) return false; // DivineMC - Implement NoChatReports
DedicatedServerProperties properties = this.getProperties();
// Paper start - Add setting for proxy online mode status
return properties.enforceSecureProfile
diff --git a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
index 4a333e87af17bd5b673b57c0fa0d3a239fb8db59..e5569978a23c5bde673146421963a2ff0905d514 100644
--- a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
@@ -344,10 +344,64 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
}
public void send(Packet<?> packet) {
+ // DivineMC start - Implement NoChatReports
+ if (org.bxteam.divinemc.config.DivineConfig.NetworkCategory.noChatReportsEnabled) {
+ Object self = this;
+ boolean cancel = false;
+
+ if (self instanceof ServerGamePacketListenerImpl listener) {
+ if (org.bxteam.divinemc.config.DivineConfig.NetworkCategory.noChatReportsDebugLog && packet instanceof net.minecraft.network.protocol.game.ClientboundPlayerChatPacket chat) {
+ MinecraftServer.LOGGER.info("Sending message: {}", chat.unsignedContent() != null ? chat.unsignedContent()
+ : chat.body().content());
+ }
+
+ if (org.bxteam.divinemc.config.DivineConfig.NetworkCategory.noChatReportsConvertToGameMessage) {
+ if (packet instanceof net.minecraft.network.protocol.game.ClientboundPlayerChatPacket chat) {
+ packet = new net.minecraft.network.protocol.game.ClientboundSystemChatPacket(chat.chatType().decorate(
+ chat.unsignedContent() != null ? chat.unsignedContent()
+ : Component.literal(chat.body().content())
+ ), false);
+
+ cancel = true;
+ listener.send(packet);
+ }
+ }
+ }
+
+ if (cancel) {
+ return;
+ }
+ }
+ // DivineMC end - Implement NoChatReports
this.send(packet, null);
}
public void send(Packet<?> packet, @Nullable ChannelFutureListener sendListener) {
+ // DivineMC start - Implement NoChatReports
+ if (org.bxteam.divinemc.config.DivineConfig.NetworkCategory.noChatReportsEnabled) {
+ Object self = this;
+ boolean cancel = false;
+
+ if (self instanceof ServerGamePacketListenerImpl listenerImpl) {
+ if (org.bxteam.divinemc.config.DivineConfig.NetworkCategory.noChatReportsDebugLog && packet instanceof net.minecraft.network.protocol.game.ClientboundPlayerChatPacket chat) {
+ MinecraftServer.LOGGER.info("Sending message: {}", chat.unsignedContent() != null ? chat.unsignedContent()
+ : chat.body().content());
+ }
+
+ if (org.bxteam.divinemc.config.DivineConfig.NetworkCategory.noChatReportsConvertToGameMessage) {
+ if (packet instanceof net.minecraft.network.protocol.game.ClientboundPlayerChatPacket chat && sendListener != null) {
+ cancel = true;
+ listenerImpl.send(chat);
+ }
+ }
+
+ }
+
+ if (cancel) {
+ return;
+ }
+ }
+ // DivineMC end - Implement NoChatReports
// CraftBukkit start
if (packet == null || this.processedDisconnect) { // Spigot
return;
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index 6c6213323df278391b30dd9f22c408a34ec052c8..c8e68bbb210457366822f2c4a01afb49693035ac 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -181,7 +181,7 @@ public abstract class PlayerList {
!_boolean,
_boolean2,
player.createCommonSpawnInfo(serverLevel),
- this.server.enforceSecureProfile()
+ org.bxteam.divinemc.config.DivineConfig.NetworkCategory.noChatReportsEnabled || this.server.enforceSecureProfile() // DivineMC - Implement NoChatReports
)
);
player.getBukkitEntity().sendSupportedChannels(); // CraftBukkit
@@ -1255,6 +1255,7 @@ public abstract class PlayerList {
}
public boolean verifyChatTrusted(PlayerChatMessage message) {
+ if (org.bxteam.divinemc.config.DivineConfig.NetworkCategory.noChatReportsEnabled) return true; // DivineMC - Implement NoChatReports
return message.hasSignature() && !message.hasExpiredServer(Instant.now());
}

View File

@@ -0,0 +1,335 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sat, 1 Feb 2025 18:38:26 +0300
Subject: [PATCH] Lag compensation
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 207726e54ccad6b41d5e6f509076903eab00e870..fd3ecd4f9b5a7c34a22cd290f1869d90061a4c65 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -286,6 +286,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
public static final long SERVER_INIT = System.nanoTime(); // Paper - Lag compensation
public boolean lagging = false; // Purpur - Lagging threshold
protected boolean upnp = false; // Purpur - UPnP Port Forwarding
+ public final org.bxteam.divinemc.util.tps.TPSCalculator tpsCalculator = new org.bxteam.divinemc.util.tps.TPSCalculator(); // DivineMC - Lag compensation
// Paper start - improve tick loop
public final ca.spottedleaf.moonrise.common.time.TickData tickTimes1s = new ca.spottedleaf.moonrise.common.time.TickData(java.util.concurrent.TimeUnit.SECONDS.toNanos(1L));
public final ca.spottedleaf.moonrise.common.time.TickData tickTimes5s = new ca.spottedleaf.moonrise.common.time.TickData(java.util.concurrent.TimeUnit.SECONDS.toNanos(5L));
@@ -1568,6 +1569,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
this.server.spark.tickStart(); // Paper - spark
+ this.tpsCalculator.doTick(); // DivineMC - Lag compenstation
new com.destroystokyo.paper.event.server.ServerTickStartEvent(this.tickCount+1).callEvent(); // Paper - Server Tick Events
this.tickCount++;
this.tickRateManager.tick();
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 06d3cbc33f41517028b5998f11695f68eb19aa5e..2f09f9c02e63563828dd7000352baf2723a0ac59 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -224,6 +224,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
private final alternate.current.wire.WireHandler wireHandler = new alternate.current.wire.WireHandler(this); // Paper - optimize redstone (Alternate Current)
public boolean hasRidableMoveEvent = false; // Purpur - Ridables
public net.minecraft.world.item.ItemStack ominousBanner; // DivineMC - Optimize Raids
+ public org.bxteam.divinemc.util.tps.TPSCalculator tpsCalculator = new org.bxteam.divinemc.util.tps.TPSCalculator(); // DivineMC - Lag Compensation
@Override
public @Nullable LevelChunk getChunkIfLoaded(int x, int z) {
@@ -787,6 +788,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
}
}
+ this.tpsCalculator.doTick(); // DivineMC - Lag compensation
+
this.updateSkyBrightness();
if (runsNormally) {
this.tickTime();
@@ -880,11 +883,18 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
this.setDayTime(this.preciseTime);
} else
// Purpur end - Configurable daylight cycle
- this.setDayTime(this.levelData.getDayTime() + 1L);
+ this.setDayTime(lagCompensation(this.levelData.getDayTime()) + 1L); // DivineMC - Lag compensation
}
}
}
+ // DivineMC start - Lag compensation
+ private long lagCompensation(long original) {
+ if (!org.bxteam.divinemc.config.DivineConfig.MiscCategory.lagCompensationEnabled || !org.bxteam.divinemc.config.DivineConfig.MiscCategory.timeAcceleration) return original;
+ return original + this.tpsCalculator.applicableMissedTicks();
+ }
+ // DivineMC end - Lag compensation
+
public void setDayTime(long time) {
this.serverLevelData.setDayTime(time);
// Purpur start - Configurable daylight cycle
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
index 5c2d1d78dce9fed6649eda2dea14bf32f8f591dc..49f83d3dedc16d977f7904971af13cc17ed32882 100644
--- a/net/minecraft/world/entity/LivingEntity.java
+++ b/net/minecraft/world/entity/LivingEntity.java
@@ -525,6 +525,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
}
}
+ lagCompensation(); // DivineMC - Lag Compensation
this.tickEffects();
this.yHeadRotO = this.yHeadRot;
this.yBodyRotO = this.yBodyRot;
@@ -532,6 +533,17 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
this.xRotO = this.getXRot();
}
+ // DivineMC start - Lag Compensation
+ private void lagCompensation() {
+ if (!org.bxteam.divinemc.config.DivineConfig.MiscCategory.lagCompensationEnabled || !org.bxteam.divinemc.config.DivineConfig.MiscCategory.potionEffectAcceleration) return;
+ if (this.level().isClientSide()) return;
+
+ for (int i = 0; i < ((ServerLevel) this.level()).tpsCalculator.applicableMissedTicks(); i++) {
+ tickEffects();
+ }
+ }
+ // DivineMC end - Lag Compensation
+
protected boolean shouldTakeDrowningDamage() {
return this.getAirSupply() <= -this.level().purpurConfig.drowningDamageInterval; // Purpur - Drowning Settings
}
diff --git a/net/minecraft/world/entity/PortalProcessor.java b/net/minecraft/world/entity/PortalProcessor.java
index 88b07fbb96b20124777889830afa480673629d43..91f6d43b3785ddad7db8eb529ba3293c45f3588d 100644
--- a/net/minecraft/world/entity/PortalProcessor.java
+++ b/net/minecraft/world/entity/PortalProcessor.java
@@ -24,10 +24,20 @@ public class PortalProcessor {
return false;
} else {
this.insidePortalThisTick = false;
- return canChangeDimensions && this.portalTime++ >= this.portal.getPortalTransitionTime(level, entity);
+ return canChangeDimensions && lagCompensation(this.portalTime++, level) >= this.portal.getPortalTransitionTime(level, entity); // DivineMC - Lag compensation
}
}
+ // DivineMC start - Lag compensation
+ private int lagCompensation(int original, ServerLevel world) {
+ if (!org.bxteam.divinemc.config.DivineConfig.MiscCategory.lagCompensationEnabled || !org.bxteam.divinemc.config.DivineConfig.MiscCategory.portalAcceleration) return original;
+ if (world.isClientSide()) return original;
+
+ portalTime = portalTime + world.tpsCalculator.applicableMissedTicks();
+ return portalTime;
+ }
+ // DivineMC end - Lag compensation
+
@Nullable
public TeleportTransition getPortalDestination(ServerLevel level, Entity entity) {
return this.portal.getPortalDestination(level, entity, this.entryPosition);
diff --git a/net/minecraft/world/entity/item/ItemEntity.java b/net/minecraft/world/entity/item/ItemEntity.java
index 5aef1e6cf4f325c330579a268ac5f5540759da92..b5d7e5738ce043fdc08cd4872c9daaf952251b9a 100644
--- a/net/minecraft/world/entity/item/ItemEntity.java
+++ b/net/minecraft/world/entity/item/ItemEntity.java
@@ -149,8 +149,25 @@ public class ItemEntity extends Entity implements TraceableEntity {
}
// Paper end - EAR 2
+ // DivineMC start - Lag compensation
+ private void lagCompensation() {
+ if (!org.bxteam.divinemc.config.DivineConfig.MiscCategory.lagCompensationEnabled || !org.bxteam.divinemc.config.DivineConfig.MiscCategory.pickupAcceleration) return;
+ if ((this).level().isClientSide()) return;
+
+ if (pickupDelay == 0) return;
+
+ if (pickupDelay - ((ServerLevel) this.level()).tpsCalculator.applicableMissedTicks() <= 0) {
+ pickupDelay = 0;
+ return;
+ }
+
+ pickupDelay = pickupDelay - ((ServerLevel) this.level()).tpsCalculator.applicableMissedTicks();
+ }
+ // DivineMC end - Lag compensation
+
@Override
public void tick() {
+ lagCompensation(); // DivineMC - Lag compensation
if (this.getItem().isEmpty()) {
this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
} else {
diff --git a/net/minecraft/world/item/Item.java b/net/minecraft/world/item/Item.java
index 6702b7199e159b16ecd06c57d87c692d04dbf7dc..1cced6128ad4ea0a00e0c772c34431a8ef689d0f 100644
--- a/net/minecraft/world/item/Item.java
+++ b/net/minecraft/world/item/Item.java
@@ -286,10 +286,25 @@ public class Item implements FeatureElement, ItemLike {
}
}
+ // DivineMC start - Lag compensation
+ private int lagCompensation(int original, net.minecraft.server.level.ServerLevel level) {
+ if (!org.bxteam.divinemc.config.DivineConfig.MiscCategory.lagCompensationEnabled || !org.bxteam.divinemc.config.DivineConfig.MiscCategory.eatingAcceleration || original == 0) return original;
+ return org.bxteam.divinemc.util.tps.TPSUtil.tt20(original, true, level);
+ }
+ // DivineMC end - Lag compensation
+
public int getUseDuration(ItemStack stack, LivingEntity entity) {
Consumable consumable = stack.get(DataComponents.CONSUMABLE);
if (consumable != null) {
- return consumable.consumeTicks();
+ // DivineMC start - Lag compensation
+ int original = consumable.consumeTicks();
+
+ if (entity.level() instanceof net.minecraft.server.level.ServerLevel serverLevel) {
+ return lagCompensation(original, serverLevel);
+ }
+
+ return original;
+ // DivineMC end - Lag compensation
} else {
BlocksAttacks blocksAttacks = stack.get(DataComponents.BLOCKS_ATTACKS);
return blocksAttacks != null ? 72000 : 0;
diff --git a/net/minecraft/world/level/GameRules.java b/net/minecraft/world/level/GameRules.java
index 45132e6608b57a30b1b1228ef89879b58d4600ef..6b86e9606073f1666d06237185af81f09f8c73d2 100644
--- a/net/minecraft/world/level/GameRules.java
+++ b/net/minecraft/world/level/GameRules.java
@@ -379,8 +379,31 @@ public class GameRules {
}
public int getInt(GameRules.Key<GameRules.IntegerValue> key) {
- return this.getRule(key).get();
+ return lagCompensation(this.getRule(key).get(), key); // DivineMC - Lag compensation
+ }
+
+ // DivineMC start - Lag compensation
+ private final java.util.concurrent.atomic.AtomicReference<net.minecraft.server.level.ServerLevel> level = new java.util.concurrent.atomic.AtomicReference<>();
+
+ private int lagCompensation(int original, GameRules.Key<GameRules.IntegerValue> rule) {
+ ServerLevel level = getOrCacheLevel();
+ if (!org.bxteam.divinemc.config.DivineConfig.MiscCategory.lagCompensationEnabled || !org.bxteam.divinemc.config.DivineConfig.MiscCategory.randomTickSpeedAcceleration) return original;
+ if (!(rule == GameRules.RULE_RANDOMTICKING)) return original;
+ return (int) (original * org.bxteam.divinemc.util.tps.TPSCalculator.MAX_TPS / (float) level.tpsCalculator.getMostAccurateTPS());
+ }
+
+ private ServerLevel getOrCacheLevel() {
+ if (level.get() == null) {
+ for (final ServerLevel level : MinecraftServer.getServer().getAllLevels()) {
+ if (level.getGameRules() == this) {
+ this.level.set(level);
+ break;
+ }
+ }
+ }
+ return level.get();
}
+ // DivineMC end - Lag compensation
public static class BooleanValue extends GameRules.Value<GameRules.BooleanValue> {
private boolean value;
diff --git a/net/minecraft/world/level/block/state/BlockBehaviour.java b/net/minecraft/world/level/block/state/BlockBehaviour.java
index a7dadab83644e9555dec4f597fee682be37e85c8..5f6fbc7eaa5522bd5e0692c9c2d280457a284e71 100644
--- a/net/minecraft/world/level/block/state/BlockBehaviour.java
+++ b/net/minecraft/world/level/block/state/BlockBehaviour.java
@@ -350,13 +350,21 @@ public abstract class BlockBehaviour implements FeatureElement {
protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
}
+ // DivineMC start - Lag compensation
+ private float lagCompensation(float original, Player player) {
+ if (!org.bxteam.divinemc.config.DivineConfig.MiscCategory.lagCompensationEnabled || !org.bxteam.divinemc.config.DivineConfig.MiscCategory.blockBreakingAcceleration) return original;
+ if (player.level().isClientSide) return original;
+ return original * org.bxteam.divinemc.util.tps.TPSCalculator.MAX_TPS / (float) ((ServerLevel) player.level()).tpsCalculator.getMostAccurateTPS();
+ }
+ // DivineMC end - Lag compensation
+
protected float getDestroyProgress(BlockState state, Player player, BlockGetter level, BlockPos pos) {
float destroySpeed = state.getDestroySpeed(level, pos);
if (destroySpeed == -1.0F) {
- return 0.0F;
+ return lagCompensation(0.0F, player); // DivineMC - Lag compensation
} else {
int i = player.hasCorrectToolForDrops(state) ? 30 : 100;
- return player.getDestroySpeed(state) / destroySpeed / i;
+ return lagCompensation(player.getDestroySpeed(state) / destroySpeed / i, player); // DivineMC - Lag compensation
}
}
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
index b03fa199a532ff7e0e14d9ad449ee8308e6e76bc..3a9843c30f685d2e1f0cd54ace5dddfa9e2314fa 100644
--- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -957,6 +957,19 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
this.ticker = ticker;
}
+ // DivineMC start - Lag compensation
+ private <T extends BlockEntity> void lagCompensation(Runnable original) {
+ original.run();
+ if (!org.bxteam.divinemc.config.DivineConfig.MiscCategory.lagCompensationEnabled) return;
+ if (!org.bxteam.divinemc.config.DivineConfig.MiscCategory.blockEntityAcceleration) return;
+ if (LevelChunk.this.level.isClientSide()) return;
+
+ for (int i = 0; i < ((ServerLevel) this.blockEntity.getLevel()).tpsCalculator.applicableMissedTicks(); i++) {
+ original.run();
+ }
+ }
+ // DivineMC end - Lag compensation
+
@Override
public void tick() {
if (!this.blockEntity.isRemoved() && this.blockEntity.hasLevel()) {
@@ -967,7 +980,11 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
profilerFiller.push(this::getType);
BlockState blockState = LevelChunk.this.getBlockState(blockPos);
if (this.blockEntity.getType().isValid(blockState)) {
- this.ticker.tick(LevelChunk.this.level, this.blockEntity.getBlockPos(), blockState, this.blockEntity);
+ // DivineMC start - Lag compensation
+ lagCompensation(() -> {
+ this.ticker.tick(LevelChunk.this.level, this.blockEntity.getBlockPos(), blockState, this.blockEntity);
+ });
+ // DivineMC end - Lag compensation
this.loggedInvalidBlockState = false;
// Paper start - Remove the Block Entity if it's invalid
} else {
diff --git a/net/minecraft/world/level/material/LavaFluid.java b/net/minecraft/world/level/material/LavaFluid.java
index bbc658abfb2df1b3ee3a31d46c4b9014bb34e0a2..38d4392a09d1d3ccebe602cf2319395217d55649 100644
--- a/net/minecraft/world/level/material/LavaFluid.java
+++ b/net/minecraft/world/level/material/LavaFluid.java
@@ -188,9 +188,22 @@ public abstract class LavaFluid extends FlowingFluid {
return fluidState.getHeight(blockReader, pos) >= 0.44444445F && fluid.is(FluidTags.WATER);
}
+ // DivineMC start - Lag compensation
+ private int lagCompensation(int original, ServerLevel level) {
+ if (!org.bxteam.divinemc.config.DivineConfig.MiscCategory.lagCompensationEnabled || !org.bxteam.divinemc.config.DivineConfig.MiscCategory.fluidAcceleration) return original;
+ return org.bxteam.divinemc.util.tps.TPSUtil.tt20(original, true, level);
+ }
+ // DivineMC end - Lag compensation
+
@Override
public int getTickDelay(LevelReader level) {
- return level.dimensionType().ultraWarm() ? level.getWorldBorder().world.purpurConfig.lavaSpeedNether : level.getWorldBorder().world.purpurConfig.lavaSpeedNotNether; // Purpur - Make lava flow speed configurable
+ // DivineMC start - Lag compensation
+ int original = level.dimensionType().ultraWarm() ? level.getWorldBorder().world.purpurConfig.lavaSpeedNether : level.getWorldBorder().world.purpurConfig.lavaSpeedNotNether; // Purpur - Make lava flow speed configurable
+ if (level instanceof ServerLevel serverLevel) {
+ return lagCompensation(original, serverLevel);
+ }
+ return original;
+ // DivineMC end - Lag compensation
}
@Override
diff --git a/net/minecraft/world/level/material/WaterFluid.java b/net/minecraft/world/level/material/WaterFluid.java
index b248fe1d66940c05d56fc322df61c52ece72e77f..2a35dcf66dc01e787f9767a90c08a6cb283576e4 100644
--- a/net/minecraft/world/level/material/WaterFluid.java
+++ b/net/minecraft/world/level/material/WaterFluid.java
@@ -124,8 +124,16 @@ public abstract class WaterFluid extends FlowingFluid {
return 1;
}
+ // DivineMC start - Lag compensation
+ private int lagCompensation(ServerLevel level) {
+ if (!org.bxteam.divinemc.config.DivineConfig.MiscCategory.lagCompensationEnabled || !org.bxteam.divinemc.config.DivineConfig.MiscCategory.fluidAcceleration) return 5;
+ return org.bxteam.divinemc.util.tps.TPSUtil.tt20(5, true, level);
+ }
+ // DivineMC end - Lag compensation
+
@Override
public int getTickDelay(LevelReader level) {
+ if (level instanceof ServerLevel serverLevel) return lagCompensation(serverLevel); // DivineMC - Lag compensation
return 5;
}

View File

@@ -0,0 +1,72 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Mon, 24 Feb 2025 19:29:58 +0300
Subject: [PATCH] Virtual Threads
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
index 9a7a40c1dcea57ba674d8431077d2477104a456f..8e41097f978800039e6e057c5bbcfcf03f5a9c89 100644
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -482,7 +482,7 @@ public class Commands {
}
// Fixed pool, but with discard policy
- public static final java.util.concurrent.ExecutorService COMMAND_SENDING_POOL = new java.util.concurrent.ThreadPoolExecutor(
+ public static final java.util.concurrent.ExecutorService COMMAND_SENDING_POOL = org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.virtualThreadsEnabled && org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.virtualCommandBuilderScheduler ? java.util.concurrent.Executors.newVirtualThreadPerTaskExecutor() : new java.util.concurrent.ThreadPoolExecutor( // DivineMC - Virtual Threads
2, 2, 0, java.util.concurrent.TimeUnit.MILLISECONDS,
new java.util.concurrent.LinkedBlockingQueue<>(),
new com.google.common.util.concurrent.ThreadFactoryBuilder()
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index fd3ecd4f9b5a7c34a22cd290f1869d90061a4c65..a876c7c47788218a86cef53330b2c07f7008b0b0 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -2709,8 +2709,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
}
- public final java.util.concurrent.ExecutorService chatExecutor = java.util.concurrent.Executors.newCachedThreadPool(
- new com.google.common.util.concurrent.ThreadFactoryBuilder().setDaemon(true).setNameFormat("Async Chat Thread - #%d").setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(net.minecraft.server.MinecraftServer.LOGGER)).build()); // Paper
+ // DivineMC start - Virtual Threads
+ public final java.util.concurrent.ExecutorService chatExecutor = org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.virtualThreadsEnabled && org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.virtualChatScheduler
+ ? java.util.concurrent.Executors.newVirtualThreadPerTaskExecutor()
+ : java.util.concurrent.Executors.newCachedThreadPool(new com.google.common.util.concurrent.ThreadFactoryBuilder().setDaemon(true).setNameFormat("Async Chat Thread - #%d").setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(net.minecraft.server.MinecraftServer.LOGGER)).build()); // Paper
+ // DivineMC end - Virtual Threads
public final ChatDecorator improvedChatDecorator = new io.papermc.paper.adventure.ImprovedChatDecorator(this); // Paper - adventure
public ChatDecorator getChatDecorator() {
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 93fc67f8cf60be489249d83a748490b653953e7a..2638343fcccbaf81d19dcd4fd09534b2e7bee796 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -844,8 +844,11 @@ public class ServerGamePacketListenerImpl
}
// Paper start - AsyncTabCompleteEvent
- private static final java.util.concurrent.ExecutorService TAB_COMPLETE_EXECUTOR = java.util.concurrent.Executors.newFixedThreadPool(4,
- new com.google.common.util.concurrent.ThreadFactoryBuilder().setDaemon(true).setNameFormat("Async Tab Complete Thread - #%d").setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(MinecraftServer.LOGGER)).build());
+ // DivineMC start - Virtual Threads
+ private static final java.util.concurrent.ExecutorService TAB_COMPLETE_EXECUTOR = org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.virtualThreadsEnabled && org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.virtualTabCompleteScheduler
+ ? java.util.concurrent.Executors.newVirtualThreadPerTaskExecutor()
+ : java.util.concurrent.Executors.newFixedThreadPool(4, new com.google.common.util.concurrent.ThreadFactoryBuilder().setDaemon(true).setNameFormat("Async Tab Complete Thread - #%d").setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(MinecraftServer.LOGGER)).build());
+ // DivineMC end - Virtual Threads
// Paper end - AsyncTabCompleteEvent
@Override
diff --git a/net/minecraft/server/network/ServerTextFilter.java b/net/minecraft/server/network/ServerTextFilter.java
index 37f46955af227c390329f44cbe2c58bf33672daf..c3f90c251a86ff72eb678d9ecdc572a950aeb2fd 100644
--- a/net/minecraft/server/network/ServerTextFilter.java
+++ b/net/minecraft/server/network/ServerTextFilter.java
@@ -48,7 +48,11 @@ public abstract class ServerTextFilter implements AutoCloseable {
final ExecutorService workerPool;
protected static ExecutorService createWorkerPool(int size) {
- return Executors.newFixedThreadPool(size, THREAD_FACTORY);
+ // DivineMC start - Virtual Threads
+ return org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.virtualThreadsEnabled && org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.virtualServerTextFilterPool
+ ? Executors.newVirtualThreadPerTaskExecutor()
+ : Executors.newFixedThreadPool(size, THREAD_FACTORY);
+ // DivineMC end - Virtual Threads
}
protected ServerTextFilter(

View File

@@ -0,0 +1,185 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Mon, 3 Mar 2025 19:29:13 +0300
Subject: [PATCH] Async Chunk Sending
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
index 4fc29f7aae32180f86af971f7f80a37aa6e797e4..ea3d63856ed487c4d23b0448c97169c230932832 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
@@ -441,7 +441,13 @@ public final class RegionizedPlayerChunkLoader {
// Note: drop isAlive() check so that chunks properly unload client-side when the player dies
((ChunkSystemChunkHolder)((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager
.getChunkHolder(chunkX, chunkZ).vanillaChunkHolder).moonrise$removeReceivedChunk(this.player);
- this.player.connection.send(new ClientboundForgetLevelChunkPacket(new ChunkPos(chunkX, chunkZ)));
+ // DivineMC start - Async Chunk Sending
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.asyncChunkSendingEnabled) {
+ org.bxteam.divinemc.async.AsyncChunkSend.POOL.execute(() -> this.player.connection.send(new ClientboundForgetLevelChunkPacket(new ChunkPos(chunkX, chunkZ))));
+ } else {
+ this.player.connection.send(new ClientboundForgetLevelChunkPacket(new ChunkPos(chunkX, chunkZ)));
+ }
+ // DivineMC end - Async Chunk Sending
// Paper start - PlayerChunkUnloadEvent
if (io.papermc.paper.event.packet.PlayerChunkUnloadEvent.getHandlerList().getRegisteredListeners().length > 0) {
new io.papermc.paper.event.packet.PlayerChunkUnloadEvent(player.getBukkitEntity().getWorld().getChunkAt(new ChunkPos(chunkX, chunkZ).longKey), player.getBukkitEntity()).callEvent();
diff --git a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
index 6987eeace609fbfba967922e558e09268e0f6d44..5af3bb5b9f8670977479cbc5ced39663d46d261d 100644
--- a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
+++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
@@ -75,6 +75,52 @@ public class ClientboundLevelChunkPacketData {
}
}
+ // DivineMC start - Async Chunk Sending
+ public ClientboundLevelChunkPacketData(LevelChunk levelChunk, io.papermc.paper.antixray.ChunkPacketInfo<net.minecraft.world.level.block.state.BlockState> chunkPacketInfo, BlockEntity[] blockEntities, Map<Heightmap.Types, long[]> heightmaps) {
+ this.heightmaps = heightmaps;
+
+ if (Thread.currentThread() instanceof org.bxteam.divinemc.async.AsyncChunkSend.AsyncChunkSendThread) {
+ int size = calculateChunkSize(levelChunk);
+ ByteBuf buffer = Unpooled.buffer(size);
+ extractChunkData(new FriendlyByteBuf(buffer), levelChunk, chunkPacketInfo);
+ // make sure all sections is latest
+ while (size != buffer.writerIndex()) {
+ buffer.writerIndex(0);
+ size = calculateChunkSize(levelChunk);
+ extractChunkData(new FriendlyByteBuf(buffer), levelChunk, chunkPacketInfo);
+ }
+ byte[] array = it.unimi.dsi.fastutil.bytes.ByteArrays.setLength(buffer.array(), buffer.writerIndex());
+ if (chunkPacketInfo != null) {
+ chunkPacketInfo.setBuffer(array);
+ }
+ this.buffer = array;
+ } else {
+ this.buffer = new byte[calculateChunkSize(levelChunk)];
+ // Paper start - Anti-Xray - Add chunk packet info
+ if (chunkPacketInfo != null) {
+ chunkPacketInfo.setBuffer(this.buffer);
+ }
+ extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), levelChunk, chunkPacketInfo);
+ }
+
+ this.blockEntitiesData = Lists.newArrayList();
+ int totalTileEntities = 0; // Paper - Handle oversized block entities in chunks
+
+ for (BlockEntity blockEntity : blockEntities) {
+ // Paper start - Handle oversized block entities in chunks
+ if (++totalTileEntities > BLOCK_ENTITY_LIMIT) {
+ net.minecraft.network.protocol.Packet<ClientGamePacketListener> packet = blockEntity.getUpdatePacket();
+ if (packet != null) {
+ this.extraPackets.add(packet);
+ continue;
+ }
+ }
+ // Paper end - Handle oversized block entities in chunks
+ this.blockEntitiesData.add(ClientboundLevelChunkPacketData.BlockEntityInfo.create(blockEntity));
+ }
+ }
+ // DivineMC end - Async Chunk Sending
+
public ClientboundLevelChunkPacketData(RegistryFriendlyByteBuf buffer, int x, int z) {
this.heightmaps = HEIGHTMAPS_STREAM_CODEC.decode(buffer);
int varInt = buffer.readVarInt();
@@ -123,6 +169,8 @@ public class ClientboundLevelChunkPacketData {
// Paper end - Anti-Xray - Add chunk packet info
}
+ if (Thread.currentThread() instanceof org.bxteam.divinemc.async.AsyncChunkSend.AsyncChunkSendThread) return; // DivineMC - Async Chunk Sending
+
if (buffer.writerIndex() != buffer.capacity()) {
throw new IllegalStateException("Didn't fill chunk buffer: expected " + buffer.capacity() + " bytes, got " + buffer.writerIndex());
}
diff --git a/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java b/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
index 8578d1f78ddd1bb75f3230f04bfaa35af9f5f822..7c55fabd264e4e813d68798433dfccfb170537a2 100644
--- a/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
+++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
@@ -45,6 +45,18 @@ public class ClientboundLevelChunkWithLightPacket implements Packet<ClientGamePa
chunk.getLevel().chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo); // Paper - Anti-Xray - Modify blocks
}
+ // DivineMC start - Async Chunk Sending
+ public ClientboundLevelChunkWithLightPacket(LevelChunk chunk, LevelLightEngine lightEngine, @Nullable BitSet skyLight, @Nullable BitSet blockLight, boolean modifyBlocks, net.minecraft.world.level.block.entity.BlockEntity[] blockEntities, java.util.Map<net.minecraft.world.level.levelgen.Heightmap.Types, long[]> heightmaps) {
+ ChunkPos pos = chunk.getPos();
+ this.x = pos.x;
+ this.z = pos.z;
+ io.papermc.paper.antixray.ChunkPacketInfo<net.minecraft.world.level.block.state.BlockState> chunkPacketInfo = modifyBlocks ? chunk.getLevel().chunkPacketBlockController.getChunkPacketInfo(this, chunk) : null; // Paper - Ant-Xray
+ this.chunkData = new ClientboundLevelChunkPacketData(chunk, chunkPacketInfo, blockEntities, heightmaps); // Paper - Anti-Xray
+ this.lightData = new ClientboundLightUpdatePacketData(pos, lightEngine, skyLight, blockLight);
+ chunk.getLevel().chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo); // Paper - Anti-Xray - Modify blocks
+ }
+ // DivineMC end - Async Chunk Sending
+
private ClientboundLevelChunkWithLightPacket(RegistryFriendlyByteBuf buffer) {
this.x = buffer.readInt();
this.z = buffer.readInt();
diff --git a/net/minecraft/server/network/PlayerChunkSender.java b/net/minecraft/server/network/PlayerChunkSender.java
index 644948d64791d0ffa4166375d0f4419f1ffa214a..f2d563076fd4e723372042df108f7f6c7ab45304 100644
--- a/net/minecraft/server/network/PlayerChunkSender.java
+++ b/net/minecraft/server/network/PlayerChunkSender.java
@@ -64,13 +64,25 @@ public class PlayerChunkSender {
if (!list.isEmpty()) {
ServerGamePacketListenerImpl serverGamePacketListenerImpl = player.connection;
this.unacknowledgedBatches++;
- serverGamePacketListenerImpl.send(ClientboundChunkBatchStartPacket.INSTANCE);
+ // DivineMC start - Async Chunk Sending
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.asyncChunkSendingEnabled) {
+ org.bxteam.divinemc.async.AsyncChunkSend.POOL.execute(() -> serverGamePacketListenerImpl.send(ClientboundChunkBatchStartPacket.INSTANCE));
+ } else {
+ serverGamePacketListenerImpl.send(ClientboundChunkBatchStartPacket.INSTANCE);
+ }
+ // DivineMC end - Async Chunk Sending
for (LevelChunk levelChunk : list) {
sendChunk(serverGamePacketListenerImpl, serverLevel, levelChunk);
}
- serverGamePacketListenerImpl.send(new ClientboundChunkBatchFinishedPacket(list.size()));
+ // DivineMC start - Async Chunk Sending
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.asyncChunkSendingEnabled) {
+ org.bxteam.divinemc.async.AsyncChunkSend.POOL.execute(() -> serverGamePacketListenerImpl.send(new ClientboundChunkBatchFinishedPacket(list.size())));
+ } else {
+ serverGamePacketListenerImpl.send(new ClientboundChunkBatchFinishedPacket(list.size()));
+ }
+ // DivineMC end - Async Chunk Sending
this.batchQuota = this.batchQuota - list.size();
}
}
@@ -81,7 +93,24 @@ public class PlayerChunkSender {
public static void sendChunk(ServerGamePacketListenerImpl packetListener, ServerLevel level, LevelChunk chunk) { // Paper - rewrite chunk system - public
// Paper start - Anti-Xray
final boolean shouldModify = level.chunkPacketBlockController.shouldModify(packetListener.player, chunk);
- packetListener.send(new ClientboundLevelChunkWithLightPacket(chunk, level.getLightEngine(), null, null, shouldModify));
+
+ // DivineMC start - Async Chunk Sending
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.asyncChunkSendingEnabled) {
+ var blockEntities = chunk.blockEntities.values().toArray(new net.minecraft.world.level.block.entity.BlockEntity[0]);
+ java.util.Map<net.minecraft.world.level.levelgen.Heightmap.Types, long[]> heightmaps = new java.util.concurrent.ConcurrentHashMap<>();
+
+ for (var entry : chunk.getHeightmaps()) {
+ if (entry.getKey().sendToClient()) {
+ heightmaps.put(entry.getKey(), entry.getValue().getRawData());
+ }
+ }
+
+ org.bxteam.divinemc.async.AsyncChunkSend.POOL.execute(() -> packetListener.send(new ClientboundLevelChunkWithLightPacket(chunk, level.getLightEngine(), null, null, shouldModify, blockEntities, heightmaps)));
+ } else {
+ packetListener.send(new ClientboundLevelChunkWithLightPacket(chunk, level.getLightEngine(), null, null, shouldModify));
+ }
+ // DivineMC end - Async Chunk Sending
+
// Paper end - Anti-Xray
// Paper start - PlayerChunkLoadEvent
if (io.papermc.paper.event.packet.PlayerChunkLoadEvent.getHandlerList().getRegisteredListeners().length > 0) {
diff --git a/net/minecraft/world/level/chunk/LevelChunkSection.java b/net/minecraft/world/level/chunk/LevelChunkSection.java
index 66ef3678fd70c5f15a13eee95f4046697e46b077..ecb04bc9f42cd55949aab9f21e052619a6d0afca 100644
--- a/net/minecraft/world/level/chunk/LevelChunkSection.java
+++ b/net/minecraft/world/level/chunk/LevelChunkSection.java
@@ -14,7 +14,7 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
public static final int SECTION_HEIGHT = 16;
public static final int SECTION_SIZE = 4096;
public static final int BIOME_CONTAINER_BITS = 2;
- short nonEmptyBlockCount; // Paper - package private
+ volatile short nonEmptyBlockCount; // Paper - package private // DivineMC - Async Chunk Sending
private short tickingBlockCount;
private short tickingFluidCount;
public final PalettedContainer<BlockState> states;

View File

@@ -0,0 +1,83 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sat, 15 Mar 2025 22:01:08 +0300
Subject: [PATCH] Command block parse results caching
diff --git a/net/minecraft/world/level/BaseCommandBlock.java b/net/minecraft/world/level/BaseCommandBlock.java
index 8bb72b05707250774fca1def7764036f04d2a896..4a6ccf073db21a40edbedf1122258e7cef68b857 100644
--- a/net/minecraft/world/level/BaseCommandBlock.java
+++ b/net/minecraft/world/level/BaseCommandBlock.java
@@ -35,6 +35,10 @@ public abstract class BaseCommandBlock {
private String command = "";
@Nullable
private Component customName;
+ // DivineMC start - Caching command block parse results
+ private String lastExecutedCommand;
+ private com.mojang.brigadier.ParseResults<CommandSourceStack> parseResultsCache;
+ // DivineMC end - Caching command block parse results
// CraftBukkit start
protected abstract org.bukkit.command.CommandSender getBukkitSender(CommandSourceStack wrapper);
// CraftBukkit end
@@ -115,13 +119,41 @@ public abstract class BaseCommandBlock {
this.successCount++;
}
});
- // Paper start - ServerCommandEvent
- org.bukkit.event.server.ServerCommandEvent event = new org.bukkit.event.server.ServerCommandEvent(commandSourceStack.getBukkitSender(), net.minecraft.commands.Commands.trimOptionalPrefix(this.command));
- if (!event.callEvent()) {
- return true;
+ // DivineMC start - Command block parse results caching
+ if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.commandBlockParseResultsCaching) {
+ String commandCache = this.command;
+ // noinspection DuplicatedCode
+ com.google.common.base.Joiner joiner = com.google.common.base.Joiner.on(" ");
+
+ if (commandCache.startsWith("/")) {
+ commandCache = commandCache.substring(1);
+ }
+
+ org.bukkit.event.server.ServerCommandEvent event = new org.bukkit.event.server.ServerCommandEvent(commandSourceStack.getBukkitSender(), commandCache);
+ org.bukkit.Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ commandCache = event.getCommand();
+ String[] args = commandCache.split(" ");
+
+ if (args.length != 0) {
+ String newCommand = joiner.join(args);
+ if (!newCommand.equals(lastExecutedCommand) || parseResultsCache == null) {
+ MinecraftServer.LOGGER.info("Recompiling parse results cache for command block at ({}, {}, {})", this.getPosition().x, this.getPosition().y, this.getPosition().z);
+ this.cache(server.getCommands().getDispatcher(), commandSourceStack, newCommand);
+ }
+ server.getCommands().performCommand(parseResultsCache, newCommand);
+ }
+ }
+ } else {
+ // Paper start - ServerCommandEvent
+ org.bukkit.event.server.ServerCommandEvent event = new org.bukkit.event.server.ServerCommandEvent(commandSourceStack.getBukkitSender(), net.minecraft.commands.Commands.trimOptionalPrefix(this.command));
+ if (!event.callEvent()) {
+ return true;
+ }
+ server.getCommands().performPrefixedCommand(commandSourceStack, event.getCommand());
+ // Paper end - ServerCommandEvent
}
- server.getCommands().performPrefixedCommand(commandSourceStack, event.getCommand());
- // Paper end - ServerCommandEvent
+ // DivineMC end - Command block parse results caching
}
} catch (Throwable var8) {
CrashReport crashReport = CrashReport.forThrowable(var8, "Executing command block");
@@ -142,6 +174,13 @@ public abstract class BaseCommandBlock {
}
}
+ // DivineMC start - Command block parse results caching
+ private void cache(com.mojang.brigadier.CommandDispatcher<CommandSourceStack> dispatcher, CommandSourceStack commandSourceStack, String commandCache) {
+ this.parseResultsCache = dispatcher.parse(commandCache, commandSourceStack);
+ this.lastExecutedCommand = commandCache;
+ }
+ // DivineMC end - Command block parse results caching
+
@Nullable
private BaseCommandBlock.CloseableCommandBlockSource createSource() {
return this.trackOutput ? new BaseCommandBlock.CloseableCommandBlockSource() : null;

View File

@@ -0,0 +1,52 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sat, 26 Apr 2025 22:30:35 +0300
Subject: [PATCH] Player ProfileResult caching
diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
index ace65f36d6562988665c530dd3fdbfaa5d2e2071..68e88a923d8dd8cd91f0ccd2b82cd60a492ad359 100644
--- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
@@ -72,6 +72,11 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
public @Nullable java.util.UUID requestedUuid; // Paper
private final io.papermc.paper.connection.PaperPlayerLoginConnection paperLoginConnection; // Paper - Config API
private volatile boolean disconnecting = false; // Paper - Fix disconnect still ticking login
+ // DivineMC start - Player ProfileResult caching
+ private static final com.google.common.cache.Cache<String, ProfileResult> playerProfileResultCache = com.google.common.cache.CacheBuilder.newBuilder()
+ .expireAfterWrite(org.bxteam.divinemc.config.DivineConfig.NetworkCategory.playerProfileResultCachingTimeout, java.util.concurrent.TimeUnit.MINUTES)
+ .build();
+ // DivineMC end - Player ProfileResult caching
public ServerLoginPacketListenerImpl(MinecraftServer server, Connection connection, boolean transferred) {
this.server = server;
@@ -258,10 +263,25 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
String string1 = Objects.requireNonNull(ServerLoginPacketListenerImpl.this.requestedUsername, "Player name not initialized");
try {
- ProfileResult profileResult = ServerLoginPacketListenerImpl.this.server
- .services()
- .sessionService()
- .hasJoinedServer(string1, string, this.getAddress());
+ // DivineMC start - Player ProfileResult caching
+ ProfileResult profileResult;
+ if (org.bxteam.divinemc.config.DivineConfig.NetworkCategory.playerProfileResultCachingEnabled) {
+ profileResult = playerProfileResultCache.getIfPresent(string1);
+
+ if (profileResult == null) {
+ profileResult = ServerLoginPacketListenerImpl.this.server
+ .services()
+ .sessionService()
+ .hasJoinedServer(string1, string, this.getAddress());
+ playerProfileResultCache.put(string1, profileResult);
+ }
+ } else {
+ profileResult = ServerLoginPacketListenerImpl.this.server
+ .services()
+ .sessionService()
+ .hasJoinedServer(string1, string, this.getAddress());
+ }
+ // DivineMC end - Player ProfileResult caching
if (profileResult != null) {
GameProfile gameProfile = profileResult.profile();
// CraftBukkit start - fire PlayerPreLoginEvent

View File

@@ -0,0 +1,214 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sat, 1 Feb 2025 16:47:09 +0300
Subject: [PATCH] Clump experience orbs
diff --git a/net/minecraft/world/entity/ExperienceOrb.java b/net/minecraft/world/entity/ExperienceOrb.java
index 850da365c388801f2083fd1471cc32ce86297de0..1c02152f1761476b19ac36e367d945c111854c0a 100644
--- a/net/minecraft/world/entity/ExperienceOrb.java
+++ b/net/minecraft/world/entity/ExperienceOrb.java
@@ -50,6 +50,10 @@ public class ExperienceOrb extends Entity {
@Nullable
public java.util.UUID triggerEntityId;
public org.bukkit.entity.ExperienceOrb.SpawnReason spawnReason = org.bukkit.entity.ExperienceOrb.SpawnReason.UNKNOWN;
+ // DivineMC start - Clump experience orbs
+ public java.util.Map<Integer, Integer> clumps$clumpedMap;
+ public Optional<EnchantedItemInUse> clumps$currentEntry;
+ // DivineMC end - Clump experience orbs
private void loadPaperNBT(ValueInput input) {
input.read("Paper.ExpData", net.minecraft.nbt.CompoundTag.CODEC).ifPresent(expData -> {
@@ -272,6 +276,28 @@ public class ExperienceOrb extends Entity {
}
private static boolean tryMergeToExisting(ServerLevel level, Vec3 pos, int amount) {
+ // DivineMC start - Clump experience orbs
+ if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.clumpOrbs) {
+ AABB aABB = AABB.ofSize(pos, 1.0D, 1.0D, 1.0D);
+ int id = level.getRandom().nextInt(40);
+ List<ExperienceOrb> list = level.getEntities(EntityTypeTest.forClass(ExperienceOrb.class), aABB, (experienceOrbx) -> canMerge(experienceOrbx, id, amount));
+ if (!list.isEmpty()) {
+ ExperienceOrb experienceOrb = list.getFirst();
+ java.util.Map<Integer, Integer> clumpedMap = (experienceOrb).clumps$getClumpedMap();
+ (experienceOrb).clumps$setClumpedMap(java.util.stream.Stream.of(clumpedMap, java.util.Collections.singletonMap(amount, 1))
+ .flatMap(map -> map.entrySet().stream())
+ .collect(java.util.stream.Collectors.toMap(java.util.Map.Entry::getKey, java.util.Map.Entry::getValue, Integer::sum)));
+ (experienceOrb).count = (clumpedMap.values()
+ .stream()
+ .reduce(Integer::sum)
+ .orElse(1));
+ (experienceOrb).age = (0);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ // DivineMC end - Clump experience orbs
// Paper - TODO some other event for this kind of merge
AABB aabb = AABB.ofSize(pos, 1.0, 1.0, 1.0);
int randomInt = level.getRandom().nextInt(io.papermc.paper.configuration.GlobalConfiguration.get().misc.xpOrbGroupsPerArea.or(ORB_GROUPS_PER_AREA)); // Paper - Configure how many orb groups per area
@@ -289,11 +315,11 @@ public class ExperienceOrb extends Entity {
}
private boolean canMerge(ExperienceOrb orb) {
- return orb != this && canMerge(orb, this.getId(), this.getValue());
+ return org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.clumpOrbs ? orb.isAlive() && !this.is(orb) : orb != this && ExperienceOrb.canMerge(orb, this.getId(), this.getValue()); // DivineMC - Clump experience orbs
}
private static boolean canMerge(ExperienceOrb orb, int amount, int other) {
- return !orb.isRemoved() && (orb.getId() - amount) % io.papermc.paper.configuration.GlobalConfiguration.get().misc.xpOrbGroupsPerArea.or(ORB_GROUPS_PER_AREA) == 0 && orb.getValue() == other; // Paper - Configure how many orbs will merge together
+ return org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.clumpOrbs ? orb.isAlive() : !orb.isRemoved() && (orb.getId() - amount) % io.papermc.paper.configuration.GlobalConfiguration.get().misc.xpOrbGroupsPerArea.or(ORB_GROUPS_PER_AREA) == 0 && orb.getValue() == other; // Paper - Configure how many orbs will merge together // Canvas - optimize orbs
}
private void merge(ExperienceOrb orb) {
@@ -302,6 +328,18 @@ public class ExperienceOrb extends Entity {
return;
}
// Paper end - call orb merge event
+ // DivineMC start - Clump experience orbs
+ if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.clumpOrbs) {
+ java.util.Map<Integer, Integer> otherMap = (orb).clumps$getClumpedMap();
+ this.count = clumps$getClumpedMap().values().stream().reduce(Integer::sum).orElse(1);
+ this.age = Math.min(this.age, (orb).age);
+ clumps$setClumpedMap(java.util.stream.Stream.of(clumps$getClumpedMap(), otherMap)
+ .flatMap(map -> map.entrySet().stream())
+ .collect(java.util.stream.Collectors.toMap(java.util.Map.Entry::getKey, java.util.Map.Entry::getValue, Integer::sum)));
+ orb.discard();
+ return;
+ }
+ // DivineMC end - Clump experience orbs
this.count = this.count + orb.count;
this.age = Math.min(this.age, orb.age);
orb.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.MERGE); // CraftBukkit - add Bukkit remove cause
@@ -343,6 +381,13 @@ public class ExperienceOrb extends Entity {
output.putInt("Value", this.getValue()); // Paper - save as Integer
output.putInt("Count", this.count);
this.savePaperNBT(output); // Paper
+ // DivineMC start - Clump experience orbs
+ if (clumps$clumpedMap != null) {
+ net.minecraft.nbt.CompoundTag map = new net.minecraft.nbt.CompoundTag();
+ clumps$getClumpedMap().forEach((value, count) -> map.putInt(String.valueOf(value), count));
+ output.store("clumpedMap", net.minecraft.nbt.CompoundTag.CODEC, map);
+ }
+ // DivineMC end - Clump experience orbs
}
@Override
@@ -352,10 +397,52 @@ public class ExperienceOrb extends Entity {
this.setValue(input.getIntOr("Value", 0)); // Paper - load as Integer
this.count = input.read("Count", ExtraCodecs.POSITIVE_INT).orElse(1);
this.loadPaperNBT(input); // Paper
+ // DivineMC start - Clump experience orbs
+ java.util.Map<Integer, Integer> map = new java.util.HashMap<>();
+ input.read("clumpedMap", net.minecraft.nbt.CompoundTag.CODEC).ifPresentOrElse(clumpedMap -> {
+ for (String s : clumpedMap.keySet()) {
+ clumpedMap.getInt(s).ifPresent(value -> {
+ map.put(Integer.parseInt(s), value);
+ });
+ }
+ }, () -> map.put(getValue(), count));
+
+ clumps$setClumpedMap(map);
+ // DivineMC end - Clump experience orbs
}
@Override
public void playerTouch(Player entity) {
+ // DivineMC start - Clump experience orbs
+ if (entity instanceof ServerPlayer serverPlayer && org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.clumpOrbs && new com.destroystokyo.paper.event.player.PlayerPickupExperienceEvent(serverPlayer.getBukkitEntity(), (org.bukkit.entity.ExperienceOrb) this.getBukkitEntity()).callEvent()) {
+ entity.takeXpDelay = 0;
+ entity.take(this, 1);
+
+ if (this.getValue() != 0 || clumps$resolve()) {
+ java.util.concurrent.atomic.AtomicInteger toGive = new java.util.concurrent.atomic.AtomicInteger();
+ clumps$getClumpedMap().forEach((value, amount) -> {
+ int actualValue = value;
+ for (int i = 0; i < amount; i++) {
+ int leftOver = actualValue;
+ if (leftOver == actualValue) {
+ leftOver = this.repairPlayerItems((ServerPlayer) entity, actualValue);
+ }
+ if (leftOver > 0) {
+ toGive.addAndGet(leftOver);
+ }
+ }
+ });
+ if (toGive.get() > 0) {
+ entity.giveExperiencePoints(toGive.get());
+ }
+ }
+
+ this.count = 0;
+ this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.PICKUP);
+
+ return;
+ }
+ // Canvas end
if (entity instanceof ServerPlayer serverPlayer) {
if (entity.takeXpDelay == 0 && new com.destroystokyo.paper.event.player.PlayerPickupExperienceEvent(serverPlayer.getBukkitEntity(), (org.bukkit.entity.ExperienceOrb) this.getBukkitEntity()).callEvent()) { // Paper - PlayerPickupExperienceEvent
entity.takeXpDelay = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerXpCooldownEvent(entity, this.level().purpurConfig.playerExpPickupDelay, org.bukkit.event.player.PlayerExpCooldownChangeEvent.ChangeReason.PICKUP_ORB).getNewCooldown(); // CraftBukkit - entity.takeXpDelay = 2; // Purpur - Configurable player pickup exp delay
@@ -373,10 +460,60 @@ public class ExperienceOrb extends Entity {
}
}
+ // DivineMC start - Clump experience orbs
+ public Optional<EnchantedItemInUse> clumps$captureCurrentEntry(Optional<EnchantedItemInUse> entry) {
+ clumps$currentEntry = entry;
+ return entry;
+ }
+
+ public java.util.Map<Integer, Integer> clumps$getClumpedMap() {
+ if (clumps$clumpedMap == null) {
+ clumps$clumpedMap = new java.util.HashMap<>();
+ clumps$clumpedMap.put(this.getValue(), 1);
+ }
+
+ return clumps$clumpedMap;
+ }
+
+ public void clumps$setClumpedMap(java.util.Map<Integer, Integer> map) {
+ clumps$clumpedMap = map;
+ clumps$resolve();
+ }
+
+ public boolean clumps$resolve() {
+ this.setValue(clumps$getClumpedMap().entrySet()
+ .stream()
+ .map(entry -> entry.getKey() * entry.getValue())
+ .reduce(Integer::sum)
+ .orElse(1));
+
+ return this.getValue() > 0;
+ }
+ // DivineMC end - Clump experience orbs
+
private int repairPlayerItems(ServerPlayer player, int value) {
- Optional<EnchantedItemInUse> randomItemWith = level().purpurConfig.useBetterMending ? EnchantmentHelper.getMostDamagedItemWith(EnchantmentEffectComponents.REPAIR_WITH_XP, player) : EnchantmentHelper.getRandomItemWith( // Purpur - Add option to mend the most damaged equipment first
- EnchantmentEffectComponents.REPAIR_WITH_XP, player, ItemStack::isDamaged
- );
+ Optional<EnchantedItemInUse> randomItemWith = clumps$captureCurrentEntry(level().purpurConfig.useBetterMending ? EnchantmentHelper.getMostDamagedItemWith(EnchantmentEffectComponents.REPAIR_WITH_XP, player) : EnchantmentHelper.getRandomItemWith(EnchantmentEffectComponents.REPAIR_WITH_XP, player, ItemStack::isDamaged)); // Purpur - Add option to mend the most damaged equipment first // DivineMC - Clump experience orbs
+
+ // DivineMC start - Clump experience orbs
+ if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.clumpOrbs) {
+ return clumps$currentEntry
+ .map(foundItem -> {
+ ItemStack itemstack = foundItem.itemStack();
+ int xpToRepair = EnchantmentHelper.modifyDurabilityToRepairFromXp(player.level(), itemstack, (int) (value * 1));
+ int toRepair = Math.min(xpToRepair, itemstack.getDamageValue());
+ itemstack.setDamageValue(itemstack.getDamageValue() - toRepair);
+ if (toRepair > 0) {
+ int used = value - toRepair * value / xpToRepair;
+ if (used > 0) {
+ return this.repairPlayerItems(player, used);
+ }
+ }
+ return 0;
+ })
+ .orElse(value);
+ }
+ // DivineMC end - Clump experience orbs
+
if (randomItemWith.isPresent()) {
ItemStack itemStack = randomItemWith.get().itemStack();
int i = EnchantmentHelper.modifyDurabilityToRepairFromXp(player.level(), itemStack, value);

View File

@@ -0,0 +1,445 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sun, 23 Mar 2025 01:16:35 +0300
Subject: [PATCH] Dynamic Activation of Brain
diff --git a/io/papermc/paper/entity/activation/ActivationRange.java b/io/papermc/paper/entity/activation/ActivationRange.java
index ca21597263cb430e2a5ae07e8cecfb0d53a270d2..226088405c019922085285ba5d04d7c131470c69 100644
--- a/io/papermc/paper/entity/activation/ActivationRange.java
+++ b/io/papermc/paper/entity/activation/ActivationRange.java
@@ -167,6 +167,21 @@ public final class ActivationRange {
}
ActivationRange.activateEntity(entity);
+
+ // DivineMC start - Dynamic Activation of Brain
+ if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.dabEnabled && entity.getType().dabEnabled && (!org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.dabDontEnableIfInWater || entity.getType().is(net.minecraft.tags.EntityTypeTags.CAN_BREATHE_UNDER_WATER) || !entity.isInWaterOrRain())) {
+ if (!entity.activatedPriorityReset) {
+ entity.activatedPriorityReset = true;
+ entity.activatedPriority = org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.dabMaximumActivationFrequency;
+ }
+ int squaredDistance = (int) player.distanceToSqr(entity);
+ entity.activatedPriority = squaredDistance > org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.dabStartDistanceSquared ?
+ Math.max(1, Math.min(squaredDistance >> org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.dabActivationDistanceMod, entity.activatedPriority)) :
+ 1;
+ } else {
+ entity.activatedPriority = 1;
+ }
+ // DivineMC end - Dynamic Activation of Brain
}
}
}
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 2f09f9c02e63563828dd7000352baf2723a0ac59..e1497f28b870e014e89ec6b189ee7989b07bb933 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -829,6 +829,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
this.entityTickList
.forEach(
entity -> {
+ entity.activatedPriorityReset = false; // DivineMC - Dynamic Activation of Brain
if (!entity.isRemoved()) {
if (!tickRateManager.isEntityFrozen(entity)) {
entity.checkDespawn();
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index b5004de3ca8951a5884a3b62b129aa2588a67af7..6724ef32e4d0de6ca0965b8b96430b68179b4bd7 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -367,6 +367,8 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name
public boolean fixedPose = false; // Paper - Expand Pose API
private final int despawnTime; // Paper - entity despawn time limit
public int totalEntityAge; // Paper - age-like counter for all entities
+ public boolean activatedPriorityReset = false; // DivineMC - Dynamic Activation of Brain
+ public int activatedPriority = org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.dabMaximumActivationFrequency; // DivineMC - Dynamic Activation of Brain
public final io.papermc.paper.entity.activation.ActivationType activationType = io.papermc.paper.entity.activation.ActivationType.activationTypeFor(this); // Paper - EAR 2/tracking ranges
// Paper start - EAR 2
public final boolean defaultActivationState;
diff --git a/net/minecraft/world/entity/EntityType.java b/net/minecraft/world/entity/EntityType.java
index c7544cad07293a504204cfa8bf9d8322ef16118c..e680780a0e46e9e5f9126bd11a20b918e8c36066 100644
--- a/net/minecraft/world/entity/EntityType.java
+++ b/net/minecraft/world/entity/EntityType.java
@@ -1192,6 +1192,7 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT
private final boolean canSpawnFarFromPlayer;
private final int clientTrackingRange;
private final int updateInterval;
+ public boolean dabEnabled = false; // DivineMC - Dynamic Activation of Brain
private final String descriptionId;
@Nullable
private Component description;
diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java
index c3a7a41b399590e2c004fda4e97b1935575c4972..dd25b049615c4465c1bf9ea0004dd649f5c55c5f 100644
--- a/net/minecraft/world/entity/Mob.java
+++ b/net/minecraft/world/entity/Mob.java
@@ -220,10 +220,10 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab
@Override
public void inactiveTick() {
super.inactiveTick();
- if (this.goalSelector.inactiveTick()) {
+ if (this.goalSelector.inactiveTick(this.activatedPriority, true)) { // DivineMC - Dynamic Activation of Brain
this.goalSelector.tick();
}
- if (this.targetSelector.inactiveTick()) {
+ if (this.targetSelector.inactiveTick(this.activatedPriority, true)) { // DivineMC - Dynamic Activation of Brain
this.targetSelector.tick();
}
}
@@ -784,13 +784,19 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab
// Paper end - Allow nerfed mobs to jump and float
this.sensing.tick();
int i = this.tickCount + this.getId();
+ // DivineMC start - Dynamic Activation of Brain
if (i % 2 != 0 && this.tickCount > 1) {
- this.targetSelector.tickRunningGoals(false);
- this.goalSelector.tickRunningGoals(false);
+ if (this.targetSelector.inactiveTick(this.activatedPriority, false))
+ this.targetSelector.tickRunningGoals(false);
+ if (this.goalSelector.inactiveTick(this.activatedPriority, false))
+ this.goalSelector.tickRunningGoals(false);
} else {
- this.targetSelector.tick();
- this.goalSelector.tick();
+ if (this.targetSelector.inactiveTick(this.activatedPriority, false))
+ this.targetSelector.tick();
+ if (this.goalSelector.inactiveTick(this.activatedPriority, false))
+ this.goalSelector.tick();
}
+ // DivineMC end - Dynamic Activation of Brain
this.navigation.tick();
this.customServerAiStep((ServerLevel)this.level());
diff --git a/net/minecraft/world/entity/ai/behavior/VillagerPanicTrigger.java b/net/minecraft/world/entity/ai/behavior/VillagerPanicTrigger.java
index f6c673b1abe53afcb14fd68d590431027ed29f67..1e5312e02298c63c168526a960d688dc03581cee 100644
--- a/net/minecraft/world/entity/ai/behavior/VillagerPanicTrigger.java
+++ b/net/minecraft/world/entity/ai/behavior/VillagerPanicTrigger.java
@@ -36,7 +36,11 @@ public class VillagerPanicTrigger extends Behavior<Villager> {
@Override
protected void tick(ServerLevel level, Villager owner, long gameTime) {
- if (gameTime % 100L == 0L) {
+ // DivineMC start - Dynamic Activation of Brain
+ if (owner.nextGolemPanic < 0) owner.nextGolemPanic = gameTime + 100;
+ if (--owner.nextGolemPanic < gameTime) {
+ owner.nextGolemPanic = -1;
+ // DivineMC end - Dynamic Activation of Brain
owner.spawnGolemIfNeeded(level, gameTime, 3);
}
}
diff --git a/net/minecraft/world/entity/ai/goal/GoalSelector.java b/net/minecraft/world/entity/ai/goal/GoalSelector.java
index 653c58c7637c46c8b46a5082f671324a2221d431..a4328a427636aa845d6627ecb75a9efe7320bb15 100644
--- a/net/minecraft/world/entity/ai/goal/GoalSelector.java
+++ b/net/minecraft/world/entity/ai/goal/GoalSelector.java
@@ -34,10 +34,14 @@ public class GoalSelector {
}
// Paper start - EAR 2
- public boolean inactiveTick() {
+ // DivineMC start - Dynamic Activation of Brain
+ public boolean inactiveTick(int tickRate, boolean inactive) {
+ if (inactive && !org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.dabEnabled) tickRate = 4;
+ tickRate = Math.min(tickRate, 3);
this.curRate++;
- return this.curRate % 3 == 0; // TODO newGoalRate was already unused in 1.20.4, check if this is correct
+ return this.curRate % tickRate == 0;
}
+ // DivineMC end - Dynamic Activation of Brain
public boolean hasTasks() {
for (WrappedGoal task : this.availableGoals) {
diff --git a/net/minecraft/world/entity/animal/allay/Allay.java b/net/minecraft/world/entity/animal/allay/Allay.java
index 3a2451bfa5e59de51d703eac8a536284e1d3348b..2422895ca4a2939f52f119384f498bd23bf9b7cc 100644
--- a/net/minecraft/world/entity/animal/allay/Allay.java
+++ b/net/minecraft/world/entity/animal/allay/Allay.java
@@ -112,6 +112,7 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS
private float spinningAnimationTicks0;
public boolean forceDancing = false; // CraftBukkit
private org.purpurmc.purpur.controller.FlyingMoveControllerWASD purpurController; // Purpur - Ridables
+ private int behaviorTick = 0; // DivineMC - Dynamic Activation of Brain
public Allay(EntityType<? extends Allay> entityType, Level level) {
super(entityType, level);
@@ -266,10 +267,13 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS
@Override
protected void customServerAiStep(ServerLevel level) {
- if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider
- this.getBrain().tick(level, this);
- AllayAi.updateActivity(this);
- super.customServerAiStep(level);
+ // DivineMC start - Dynamic Activation of Brain
+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) { // Purpur - only use brain if no rider
+ this.getBrain().tick(level, this);
+ AllayAi.updateActivity(this);
+ super.customServerAiStep(level);
+ }
+ // DivineMC end - Dynamic Activation of Brain
}
@Override
diff --git a/net/minecraft/world/entity/animal/axolotl/Axolotl.java b/net/minecraft/world/entity/animal/axolotl/Axolotl.java
index 1927f8be451258d9f0f8eec1617d103a4140e1e7..781ce63ff615d718cdf14bf6c3a4c742f628a920 100644
--- a/net/minecraft/world/entity/animal/axolotl/Axolotl.java
+++ b/net/minecraft/world/entity/animal/axolotl/Axolotl.java
@@ -111,6 +111,7 @@ public class Axolotl extends Animal implements Bucketable {
public final BinaryAnimator onGroundAnimator = new BinaryAnimator(10, Mth::easeInOutSine);
public final BinaryAnimator movingAnimator = new BinaryAnimator(10, Mth::easeInOutSine);
private static final int REGEN_BUFF_BASE_DURATION = 100;
+ private int behaviorTick = 0; // DivineMC - Dynamic Activation of Brain
public Axolotl(EntityType<? extends Axolotl> entityType, Level level) {
super(entityType, level);
@@ -372,13 +373,16 @@ public class Axolotl extends Animal implements Bucketable {
@Override
protected void customServerAiStep(ServerLevel level) {
- if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider
- this.getBrain().tick(level, this);
- AxolotlAi.updateActivity(this);
- if (!this.isNoAi()) {
- Optional<Integer> memory = this.getBrain().getMemory(MemoryModuleType.PLAY_DEAD_TICKS);
- this.setPlayingDead(memory.isPresent() && memory.get() > 0);
+ // DivineMC start - Dynamic Activation of Brain
+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) { // Purpur - only use brain if no rider
+ this.getBrain().tick(level, this);
+ AxolotlAi.updateActivity(this);
+ if (!this.isNoAi()) {
+ Optional<Integer> memory = this.getBrain().getMemory(MemoryModuleType.PLAY_DEAD_TICKS);
+ this.setPlayingDead(memory.isPresent() && memory.get() > 0);
+ }
}
+ // DivineMC end - Dynamic Activation of Brain
}
public static AttributeSupplier.Builder createAttributes() {
diff --git a/net/minecraft/world/entity/animal/frog/Frog.java b/net/minecraft/world/entity/animal/frog/Frog.java
index 1a504e30602d7d8feaf7a9180adf6382596aae02..9656c1bf22b1b6c945a8ba5603742261db650fd5 100644
--- a/net/minecraft/world/entity/animal/frog/Frog.java
+++ b/net/minecraft/world/entity/animal/frog/Frog.java
@@ -105,6 +105,7 @@ public class Frog extends Animal {
public final AnimationState swimIdleAnimationState = new AnimationState();
private org.purpurmc.purpur.controller.MoveControllerWASD purpurLandController; // Purpur - Ridables
private org.purpurmc.purpur.controller.WaterMoveControllerWASD purpurWaterController; // Purpur - Ridables
+ private int behaviorTick = 0; // DivineMC - Dynamic Activation of Brain
public Frog(EntityType<? extends Animal> entityType, Level level) {
super(entityType, level);
@@ -258,10 +259,13 @@ public class Frog extends Animal {
@Override
protected void customServerAiStep(ServerLevel level) {
- if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider
- this.getBrain().tick(level, this);
- FrogAi.updateActivity(this);
- super.customServerAiStep(level);
+ // DivineMC start - Dynamic Activation of Brain
+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) { // Purpur - only use brain if no rider
+ this.getBrain().tick(level, this);
+ FrogAi.updateActivity(this);
+ super.customServerAiStep(level);
+ }
+ // DivineMC end - Dynamic Activation of Brain
}
@Override
diff --git a/net/minecraft/world/entity/animal/frog/Tadpole.java b/net/minecraft/world/entity/animal/frog/Tadpole.java
index 8312a0d5229043fd84125db809c4346bb80f2bc0..1a437d3e09484f4bacc4ef22b3e5f1c3c58ca42f 100644
--- a/net/minecraft/world/entity/animal/frog/Tadpole.java
+++ b/net/minecraft/world/entity/animal/frog/Tadpole.java
@@ -64,6 +64,7 @@ public class Tadpole extends AbstractFish {
);
public boolean ageLocked; // Paper
private org.purpurmc.purpur.controller.WaterMoveControllerWASD purpurController; // Purpur - Ridables
+ private int behaviorTick = 0; // DivineMC - Dynamic Activation of Brain
public Tadpole(EntityType<? extends AbstractFish> entityType, Level level) {
super(entityType, level);
@@ -134,10 +135,13 @@ public class Tadpole extends AbstractFish {
@Override
protected void customServerAiStep(ServerLevel level) {
- if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider
- this.getBrain().tick(level, this);
- TadpoleAi.updateActivity(this);
- super.customServerAiStep(level);
+ // DivineMC start - Dynamic Activation of Brain
+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) { // Purpur - only use brain if no rider
+ this.getBrain().tick(level, this);
+ TadpoleAi.updateActivity(this);
+ super.customServerAiStep(level);
+ }
+ // DivineMC end - Dynamic Activation of Brain
}
public static AttributeSupplier.Builder createAttributes() {
diff --git a/net/minecraft/world/entity/animal/goat/Goat.java b/net/minecraft/world/entity/animal/goat/Goat.java
index 73fe7a8c216aed7dc2274fd53fb126d85ec5137d..d4de51a8fd1f2adfc35eb6e23cda6905bd376515 100644
--- a/net/minecraft/world/entity/animal/goat/Goat.java
+++ b/net/minecraft/world/entity/animal/goat/Goat.java
@@ -93,6 +93,7 @@ public class Goat extends Animal {
private static final boolean DEFAULT_HAS_RIGHT_HORN = true;
private boolean isLoweringHead;
private int lowerHeadTick;
+ private int behaviorTick = 0; // DivineMC - Dynamic Activation of Brain
public Goat(EntityType<? extends Goat> entityType, Level level) {
super(entityType, level);
@@ -232,10 +233,13 @@ public class Goat extends Animal {
@Override
protected void customServerAiStep(ServerLevel level) {
- if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider
- this.getBrain().tick(level, this);
- GoatAi.updateActivity(this);
- super.customServerAiStep(level);
+ // DivineMC start - Dynamic Activation of Brain
+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) { // Purpur - only use brain if no rider
+ this.getBrain().tick(level, this);
+ GoatAi.updateActivity(this);
+ super.customServerAiStep(level);
+ }
+ // DivineMC end - Dynamic Activation of Brain
}
@Override
diff --git a/net/minecraft/world/entity/monster/hoglin/Hoglin.java b/net/minecraft/world/entity/monster/hoglin/Hoglin.java
index b22519a6d39bd52381fa6c17b9a415944374368b..10e36ec8d5e3ee44400e916632771a9cc1381664 100644
--- a/net/minecraft/world/entity/monster/hoglin/Hoglin.java
+++ b/net/minecraft/world/entity/monster/hoglin/Hoglin.java
@@ -87,6 +87,7 @@ public class Hoglin extends Animal implements Enemy, HoglinBase {
MemoryModuleType.PACIFIED,
MemoryModuleType.IS_PANICKING
);
+ private int behaviorTick; // DivineMC - Dynamic Activation of Brain
public Hoglin(EntityType<? extends Hoglin> entityType, Level level) {
super(entityType, level);
@@ -205,18 +206,21 @@ public class Hoglin extends Animal implements Enemy, HoglinBase {
@Override
protected void customServerAiStep(ServerLevel level) {
- if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider
- this.getBrain().tick(level, this);
- HoglinAi.updateActivity(this);
- if (this.isConverting()) {
- this.timeInOverworld++;
- if (this.timeInOverworld > 300) {
- this.makeSound(SoundEvents.HOGLIN_CONVERTED_TO_ZOMBIFIED);
- this.finishConversion();
+ // DivineMC start - Dynamic Activation of Brain
+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) { // Purpur - only use brain if no rider
+ this.getBrain().tick(level, this);
+ HoglinAi.updateActivity(this);
+ if (this.isConverting()) {
+ this.timeInOverworld++;
+ if (this.timeInOverworld > 300) {
+ this.makeSound(SoundEvents.HOGLIN_CONVERTED_TO_ZOMBIFIED);
+ this.finishConversion();
+ }
+ } else {
+ this.timeInOverworld = 0;
}
- } else {
- this.timeInOverworld = 0;
}
+ // DivineMC end - Dynamic Activation of Brain
}
@Override
diff --git a/net/minecraft/world/entity/monster/piglin/Piglin.java b/net/minecraft/world/entity/monster/piglin/Piglin.java
index dea7c211a2d2ee8f1833eaad49513b3690b06c55..e2b467cfc19dfcf1fb617e57251451aa88de727d 100644
--- a/net/minecraft/world/entity/monster/piglin/Piglin.java
+++ b/net/minecraft/world/entity/monster/piglin/Piglin.java
@@ -128,6 +128,7 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento
private static final com.mojang.serialization.Codec<java.util.Set<net.minecraft.world.item.Item>> ITEM_SET_CODEC = net.minecraft.core.registries.BuiltInRegistries.ITEM
.byNameCodec().listOf().xmap(java.util.HashSet::new, List::copyOf);
// CraftBukkit end
+ private int behaviorTick; // DivineMC - Dynamic Activation of Brain
public Piglin(EntityType<? extends AbstractPiglin> entityType, Level level) {
super(entityType, level);
@@ -345,10 +346,13 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento
@Override
protected void customServerAiStep(ServerLevel level) {
- if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider
- this.getBrain().tick(level, this);
- PiglinAi.updateActivity(this);
- super.customServerAiStep(level);
+ // DivineMC start - Dynamic Activation of Brain
+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) { // Purpur - only use brain if no rider
+ this.getBrain().tick(level, this);
+ PiglinAi.updateActivity(this);
+ super.customServerAiStep(level);
+ }
+ // DivineMC end - Dynamic Activation of Brain
}
@Override
diff --git a/net/minecraft/world/entity/monster/warden/Warden.java b/net/minecraft/world/entity/monster/warden/Warden.java
index ac7691a2a3e4c9db040408d1ecba780a89dea50e..e3478244e430faa614f23c288019303bd6bb0e04 100644
--- a/net/minecraft/world/entity/monster/warden/Warden.java
+++ b/net/minecraft/world/entity/monster/warden/Warden.java
@@ -107,6 +107,7 @@ public class Warden extends Monster implements VibrationSystem {
private final VibrationSystem.User vibrationUser;
private VibrationSystem.Data vibrationData;
AngerManagement angerManagement = new AngerManagement(this::canTargetEntity, Collections.emptyList());
+ private int behaviorTick = 0; // DivineMC - Dynamic Activation of Brain
public Warden(EntityType<? extends Monster> entityType, Level level) {
super(entityType, level);
@@ -300,19 +301,22 @@ public class Warden extends Monster implements VibrationSystem {
@Override
protected void customServerAiStep(ServerLevel level) {
- if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider
- this.getBrain().tick(level, this);
- super.customServerAiStep(level);
- if ((this.tickCount + this.getId()) % 120 == 0) {
- applyDarknessAround(level, this.position(), this, 20);
- }
+ // DivineMC start - Dynamic Activation of Brain
+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) { // Purpur - only use brain if no rider
+ this.getBrain().tick(level, this);
+ super.customServerAiStep(level);
+ if ((this.tickCount + this.getId()) % 120 == 0) {
+ applyDarknessAround(level, this.position(), this, 20);
+ }
- if (this.tickCount % 20 == 0) {
- this.angerManagement.tick(level, this::canTargetEntity);
- this.syncClientAngerLevel();
- }
+ if (this.tickCount % 20 == 0) {
+ this.angerManagement.tick(level, this::canTargetEntity);
+ this.syncClientAngerLevel();
+ }
- WardenAi.updateActivity(this);
+ WardenAi.updateActivity(this);
+ }
+ // DivineMC end - Dynamic Activation of Brain
}
@Override
diff --git a/net/minecraft/world/entity/npc/Villager.java b/net/minecraft/world/entity/npc/Villager.java
index 3bcd3b7c9f7ad408d66fad5b1b70ebee96a61b43..94424c01b0c4a28c7eafd5c02d068b9c41e451e2 100644
--- a/net/minecraft/world/entity/npc/Villager.java
+++ b/net/minecraft/world/entity/npc/Villager.java
@@ -178,6 +178,8 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
);
private boolean isLobotomized = false; public boolean isLobotomized() { return this.isLobotomized; } // Purpur - Lobotomize stuck villagers
private int notLobotomizedCount = 0; // Purpur - Lobotomize stuck villagers
+ public long nextGolemPanic = -1; // DivineMC - Dynamic Activation of Brain
+ private int behaviorTick = 0; // DivineMC - Dynamic Activation of Brain
public Villager(EntityType<? extends Villager> entityType, Level level) {
this(entityType, level, VillagerType.PLAINS);
@@ -398,7 +400,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
} else {
this.isLobotomized = false;
}
- if (!inactive && (getRider() == null || !this.isControllable())) { // Purpur - Ridables
+ if (!inactive && this.behaviorTick++ % this.activatedPriority == 0 && (getRider() == null || !this.isControllable())) { // Purpur - Ridables // DivineMC - Dynamic Activation of Brain
this.getBrain().tick(level, this); // Paper - EAR 2
}
else if (this.isLobotomized && shouldRestock()) restock();

View File

@@ -0,0 +1,430 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Tue, 28 Jan 2025 00:54:57 +0300
Subject: [PATCH] Implement Secure Seed
Original license: GPLv3
Original project: https://github.com/plasmoapp/matter
diff --git a/net/minecraft/server/commands/SeedCommand.java b/net/minecraft/server/commands/SeedCommand.java
index 7c1e18d8362be5ae885c32b05e98b9ef45942d93..a414de3768972157d3031222fd160556d9b99bf4 100644
--- a/net/minecraft/server/commands/SeedCommand.java
+++ b/net/minecraft/server/commands/SeedCommand.java
@@ -12,6 +12,17 @@ public class SeedCommand {
long seed = commandContext.getSource().getLevel().getSeed();
Component component = ComponentUtils.copyOnClickText(String.valueOf(seed));
commandContext.getSource().sendSuccess(() -> Component.translatable("commands.seed.success", component), false);
+
+ // DivineMC start - Implement Secure Seed
+ if (org.bxteam.divinemc.config.DivineConfig.MiscCategory.enableSecureSeed) {
+ su.plo.matter.Globals.setupGlobals(commandContext.getSource().getLevel());
+ String seedStr = su.plo.matter.Globals.seedToString(su.plo.matter.Globals.worldSeed);
+ Component featureSeedComponent = ComponentUtils.copyOnClickText(seedStr);
+
+ commandContext.getSource().sendSuccess(() -> Component.translatable(("Feature seed: %s"), featureSeedComponent), false);
+ }
+ // DivineMC end - Implement Secure Seed
+
return (int)seed;
}));
}
diff --git a/net/minecraft/server/dedicated/DedicatedServerProperties.java b/net/minecraft/server/dedicated/DedicatedServerProperties.java
index a9cd61f0a7bde931e59f1496191f1f8d465aec5e..7711cc269b97811836f1b1dafd0f38eac5cdc7d3 100644
--- a/net/minecraft/server/dedicated/DedicatedServerProperties.java
+++ b/net/minecraft/server/dedicated/DedicatedServerProperties.java
@@ -129,7 +129,17 @@ public class DedicatedServerProperties extends Settings<DedicatedServerPropertie
String string = this.get("level-seed", "");
boolean flag = this.get("generate-structures", true);
long l = WorldOptions.parseSeed(string).orElse(WorldOptions.randomSeed());
- this.worldOptions = new WorldOptions(l, flag, false);
+ // DivineMC start - Implement Secure Seed
+ if (org.bxteam.divinemc.config.DivineConfig.MiscCategory.enableSecureSeed) {
+ String featureSeedStr = this.get("feature-level-seed", "");
+ long[] featureSeed = su.plo.matter.Globals.parseSeed(featureSeedStr)
+ .orElse(su.plo.matter.Globals.createRandomWorldSeed());
+
+ this.worldOptions = new WorldOptions(l, featureSeed, flag, false);
+ } else {
+ this.worldOptions = new WorldOptions(l, flag, false);
+ }
+ // DivineMC end - Implement Secure Seed
this.worldDimensionData = new DedicatedServerProperties.WorldDimensionData(
this.get("generator-settings", property -> GsonHelper.parse(!property.isEmpty() ? property : "{}"), new JsonObject()),
this.get("level-type", property -> property.toLowerCase(Locale.ROOT), WorldPresets.NORMAL.location().toString())
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
index 301b45e375dafeefed56eb0db5dd51eea4c97459..65607de63f6ea900599660485861860b71e1aef3 100644
--- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -614,6 +614,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
}
public ChunkGenerator getGenerator() {
+ su.plo.matter.Globals.setupGlobals(level); // DivineMC - Implement Secure Seed
return this.chunkMap.generator();
}
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index e1497f28b870e014e89ec6b189ee7989b07bb933..b240c7f2579e25d520fbc0ab08e801028bc15192 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -655,6 +655,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
chunkGenerator = new org.bukkit.craftbukkit.generator.CustomChunkGenerator(this, chunkGenerator, gen);
}
// CraftBukkit end
+ su.plo.matter.Globals.setupGlobals(this); // DivineMC - Implement Secure Seed
boolean flag = server.forceSynchronousWrites();
DataFixer fixerUpper = server.getFixerUpper();
// Paper - rewrite chunk system
diff --git a/net/minecraft/world/entity/monster/Slime.java b/net/minecraft/world/entity/monster/Slime.java
index 970105d598964194e167ad243f6e2fc2a19fba08..74893d67a3fb6fd906afe9253f191f0e635f3277 100644
--- a/net/minecraft/world/entity/monster/Slime.java
+++ b/net/minecraft/world/entity/monster/Slime.java
@@ -408,7 +408,11 @@ public class Slime extends Mob implements Enemy {
}
ChunkPos chunkPos = new ChunkPos(pos);
- boolean flag = level.getMinecraftWorld().paperConfig().entities.spawning.allChunksAreSlimeChunks || WorldgenRandom.seedSlimeChunk(chunkPos.x, chunkPos.z, ((WorldGenLevel) level).getSeed(), level.getMinecraftWorld().spigotConfig.slimeSeed).nextInt(10) == 0; // Paper
+ // DivineMC start - Implement Secure Seed
+ boolean flag = level.getMinecraftWorld().paperConfig().entities.spawning.allChunksAreSlimeChunks || org.bxteam.divinemc.config.DivineConfig.MiscCategory.enableSecureSeed
+ ? level.getChunk(chunkPos.x, chunkPos.z).isSlimeChunk()
+ : WorldgenRandom.seedSlimeChunk(chunkPos.x, chunkPos.z, ((WorldGenLevel) level).getSeed(), level.getMinecraftWorld().spigotConfig.slimeSeed).nextInt(10) == 0; // Paper
+ // DivineMC end - Implement Secure Seed
// Paper start - Replace rules for Height in Slime Chunks
final double maxHeightSlimeChunk = level.getMinecraftWorld().paperConfig().entities.spawning.slimeSpawnHeight.slimeChunk.maximum;
if (random.nextInt(10) == 0 && flag && pos.getY() < maxHeightSlimeChunk) {
diff --git a/net/minecraft/world/level/chunk/ChunkAccess.java b/net/minecraft/world/level/chunk/ChunkAccess.java
index 2457247d2bc1c3e3b042a091c3a8290d55203da8..97b17c07b86f188e04cf747770be37b530f1dc61 100644
--- a/net/minecraft/world/level/chunk/ChunkAccess.java
+++ b/net/minecraft/world/level/chunk/ChunkAccess.java
@@ -82,6 +82,10 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh
public final Map<BlockPos, BlockEntity> blockEntities = new Object2ObjectOpenHashMap<>();
protected final LevelHeightAccessor levelHeightAccessor;
protected final LevelChunkSection[] sections;
+ // DivineMC start - Implement Secure Seed
+ private boolean slimeChunk;
+ private boolean hasComputedSlimeChunk;
+ // DivineMC end - Implement Secure Seed
// CraftBukkit start - SPIGOT-6814: move to IChunkAccess to account for 1.17 to 1.18 chunk upgrading.
private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry();
public org.bukkit.craftbukkit.persistence.DirtyCraftPersistentDataContainer persistentDataContainer = new org.bukkit.craftbukkit.persistence.DirtyCraftPersistentDataContainer(ChunkAccess.DATA_TYPE_REGISTRY);
@@ -190,6 +194,17 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh
return GameEventListenerRegistry.NOOP;
}
+ // DivineMC start - Implement Secure Seed
+ public boolean isSlimeChunk() {
+ if (!hasComputedSlimeChunk) {
+ hasComputedSlimeChunk = true;
+ slimeChunk = su.plo.matter.WorldgenCryptoRandom.seedSlimeChunk(chunkPos.x, chunkPos.z).nextInt(10) == 0;
+ }
+
+ return slimeChunk;
+ }
+ // DivineMC end - Implement Secure Seed
+
public abstract BlockState getBlockState(final int x, final int y, final int z); // Paper
@Nullable
diff --git a/net/minecraft/world/level/chunk/ChunkGenerator.java b/net/minecraft/world/level/chunk/ChunkGenerator.java
index 213fd45bd70d347a91c46244b61d8b77f11bf6fb..30cb24503fb2e1e3743e6d19634d77cd7de48e13 100644
--- a/net/minecraft/world/level/chunk/ChunkGenerator.java
+++ b/net/minecraft/world/level/chunk/ChunkGenerator.java
@@ -346,7 +346,11 @@ public abstract class ChunkGenerator {
Registry<Structure> registry = level.registryAccess().lookupOrThrow(Registries.STRUCTURE);
Map<Integer, List<Structure>> map = registry.stream().collect(Collectors.groupingBy(structure1 -> structure1.step().ordinal()));
List<FeatureSorter.StepFeatureData> list = this.featuresPerStep.get();
- WorldgenRandom worldgenRandom = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed()));
+ // DivineMC start - Implement Secure Seed
+ WorldgenRandom worldgenRandom = org.bxteam.divinemc.config.DivineConfig.MiscCategory.enableSecureSeed
+ ? new su.plo.matter.WorldgenCryptoRandom(blockPos.getX(), blockPos.getZ(), su.plo.matter.Globals.Salt.UNDEFINED, 0)
+ : new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed()));
+ // DivineMC end - Implement Secure Seed
long l = worldgenRandom.setDecorationSeed(level.getSeed(), blockPos.getX(), blockPos.getZ());
Set<Holder<Biome>> set = new ObjectArraySet<>();
ChunkPos.rangeClosed(sectionPos.chunk(), 1).forEach(chunkPos -> {
@@ -561,8 +565,18 @@ public abstract class ChunkGenerator {
} else {
ArrayList<StructureSet.StructureSelectionEntry> list1 = new ArrayList<>(list.size());
list1.addAll(list);
- WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L));
- worldgenRandom.setLargeFeatureSeed(structureState.getLevelSeed(), pos.x, pos.z);
+ // DivineMC start - Implement Secure Seed
+ WorldgenRandom worldgenRandom;
+ if (org.bxteam.divinemc.config.DivineConfig.MiscCategory.enableSecureSeed) {
+ worldgenRandom = new su.plo.matter.WorldgenCryptoRandom(
+ pos.x, pos.z, su.plo.matter.Globals.Salt.GENERATE_FEATURE, 0
+ );
+ } else {
+ worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L));
+
+ worldgenRandom.setLargeFeatureSeed(structureState.getLevelSeed(), pos.x, pos.z);
+ }
+ // DivineMC end - Implement Secure Seed
int i = 0;
for (StructureSet.StructureSelectionEntry structureSelectionEntry1 : list1) {
diff --git a/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java b/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java
index b7762440bfd4f5ac105c22e5ded56cc6a1b2815a..8f57ff050356bd53f46eeeacd39c0fd39c5d1d70 100644
--- a/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java
+++ b/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java
@@ -205,14 +205,21 @@ public class ChunkGeneratorStructureState {
List<CompletableFuture<ChunkPos>> list = new ArrayList<>(count);
int spread = placement.spread();
HolderSet<Biome> holderSet = placement.preferredBiomes();
- RandomSource randomSource = RandomSource.create();
- // Paper start - Add missing structure set seed configs
- if (this.conf.strongholdSeed != null && structureSet.is(net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.STRONGHOLDS)) {
- randomSource.setSeed(this.conf.strongholdSeed);
- } else {
- // Paper end - Add missing structure set seed configs
- randomSource.setSeed(this.concentricRingsSeed);
- } // Paper - Add missing structure set seed configs
+ // DivineMC start - Implement Secure Seed
+ RandomSource randomSource = org.bxteam.divinemc.config.DivineConfig.MiscCategory.enableSecureSeed
+ ? new su.plo.matter.WorldgenCryptoRandom(0, 0, su.plo.matter.Globals.Salt.STRONGHOLDS, 0)
+ : RandomSource.create();
+
+ if (!org.bxteam.divinemc.config.DivineConfig.MiscCategory.enableSecureSeed) {
+ // Paper start - Add missing structure set seed configs
+ if (this.conf.strongholdSeed != null && structureSet.is(net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.STRONGHOLDS)) {
+ randomSource.setSeed(this.conf.strongholdSeed);
+ } else {
+ // Paper end - Add missing structure set seed configs
+ randomSource.setSeed(this.concentricRingsSeed);
+ } // Paper - Add missing structure set seed configs
+ }
+ // DivineMC end - Implement Secure Seed
double d = randomSource.nextDouble() * Math.PI * 2.0;
int i = 0;
int i1 = 0;
diff --git a/net/minecraft/world/level/chunk/status/ChunkStep.java b/net/minecraft/world/level/chunk/status/ChunkStep.java
index b8348976e80578d9eff64eea68c04c603fed49ad..9494e559113798fe451a6d0226be3ae0449021dc 100644
--- a/net/minecraft/world/level/chunk/status/ChunkStep.java
+++ b/net/minecraft/world/level/chunk/status/ChunkStep.java
@@ -60,6 +60,7 @@ public final class ChunkStep implements ca.spottedleaf.moonrise.patches.chunk_sy
}
public CompletableFuture<ChunkAccess> apply(WorldGenContext worldGenContext, StaticCache2D<GenerationChunkHolder> cache, ChunkAccess chunk) {
+ su.plo.matter.Globals.setupGlobals(worldGenContext.level()); // DivineMC - Implement Secure Seed
if (chunk.getPersistedStatus().isBefore(this.targetStatus)) {
ProfiledDuration profiledDuration = JvmProfiler.INSTANCE
.onChunkGenerate(chunk.getPos(), worldGenContext.level().dimension(), this.targetStatus.getName());
diff --git a/net/minecraft/world/level/levelgen/WorldOptions.java b/net/minecraft/world/level/levelgen/WorldOptions.java
index c92508741439a8d0d833ea02d0104416adb83c92..c4afe1cc270e6d7b4ffeada75da8265b46afd694 100644
--- a/net/minecraft/world/level/levelgen/WorldOptions.java
+++ b/net/minecraft/world/level/levelgen/WorldOptions.java
@@ -9,17 +9,28 @@ import net.minecraft.util.RandomSource;
import org.apache.commons.lang3.StringUtils;
public class WorldOptions {
+ // DivineMC start - Implement Secure Seed
+ private static final boolean isSecureSeedEnabled = org.bxteam.divinemc.config.DivineConfig.MiscCategory.enableSecureSeed;
public static final MapCodec<WorldOptions> CODEC = RecordCodecBuilder.mapCodec(
- instance -> instance.group(
+ instance -> isSecureSeedEnabled
+ ? instance.group(
Codec.LONG.fieldOf("seed").stable().forGetter(WorldOptions::seed),
+ Codec.LONG_STREAM.fieldOf("feature_seed").stable().forGetter(WorldOptions::featureSeedStream),
Codec.BOOL.fieldOf("generate_features").orElse(true).stable().forGetter(WorldOptions::generateStructures),
Codec.BOOL.fieldOf("bonus_chest").orElse(false).stable().forGetter(WorldOptions::generateBonusChest),
- Codec.STRING.lenientOptionalFieldOf("legacy_custom_options").stable().forGetter(worldOptions -> worldOptions.legacyCustomOptions)
- )
- .apply(instance, instance.stable(WorldOptions::new))
+ Codec.STRING.lenientOptionalFieldOf("legacy_custom_options").stable().forGetter(generatorOptions -> generatorOptions.legacyCustomOptions)).apply(instance, instance.stable(WorldOptions::new))
+ : instance.group(
+ Codec.LONG.fieldOf("seed").stable().forGetter(WorldOptions::seed),
+ Codec.BOOL.fieldOf("generate_features").orElse(true).stable().forGetter(WorldOptions::generateStructures),
+ Codec.BOOL.fieldOf("bonus_chest").orElse(false).stable().forGetter(WorldOptions::generateBonusChest),
+ Codec.STRING.lenientOptionalFieldOf("legacy_custom_options").stable().forGetter(worldOptions -> worldOptions.legacyCustomOptions)).apply(instance, instance.stable(WorldOptions::new))
);
- public static final WorldOptions DEMO_OPTIONS = new WorldOptions("North Carolina".hashCode(), true, true);
+ public static final WorldOptions DEMO_OPTIONS = isSecureSeedEnabled
+ ? new WorldOptions("North Carolina".hashCode(), su.plo.matter.Globals.createRandomWorldSeed(), true, true)
+ : new WorldOptions("North Carolina".hashCode(), true, true);
+ // DivineMC end - Implement Secure Seed
private final long seed;
+ private long[] featureSeed = su.plo.matter.Globals.createRandomWorldSeed(); // DivineMC - Implement Secure Seed
private final boolean generateStructures;
private final boolean generateBonusChest;
private final Optional<String> legacyCustomOptions;
@@ -28,9 +39,21 @@ public class WorldOptions {
this(seed, generateStructures, generateBonusChest, Optional.empty());
}
+ // DivineMC start - Implement Secure Seed
+ public WorldOptions(long seed, long[] featureSeed, boolean generateStructures, boolean bonusChest) {
+ this(seed, featureSeed, generateStructures, bonusChest, Optional.empty());
+ }
+
public static WorldOptions defaultWithRandomSeed() {
- return new WorldOptions(randomSeed(), true, false);
+ return isSecureSeedEnabled
+ ? new WorldOptions(randomSeed(), su.plo.matter.Globals.createRandomWorldSeed(), true, false)
+ : new WorldOptions(randomSeed(), true, false);
+ }
+
+ private WorldOptions(long seed, java.util.stream.LongStream featureSeed, boolean generateStructures, boolean bonusChest, Optional<String> legacyCustomOptions) {
+ this(seed, featureSeed.toArray(), generateStructures, bonusChest, legacyCustomOptions);
}
+ // DivineMC end - Implement Secure Seed
public static WorldOptions testWorldWithRandomSeed() {
return new WorldOptions(randomSeed(), false, false);
@@ -43,10 +66,27 @@ public class WorldOptions {
this.legacyCustomOptions = legacyCustomOptions;
}
+ // DivineMC start - Implement Secure Seed
+ private WorldOptions(long seed, long[] featureSeed, boolean generateStructures, boolean bonusChest, Optional<String> legacyCustomOptions) {
+ this(seed, generateStructures, bonusChest, legacyCustomOptions);
+ this.featureSeed = featureSeed;
+ }
+ // DivineMC end - Implement Secure Seed
+
public long seed() {
return this.seed;
}
+ // DivineMC start - Implement Secure Seed
+ public long[] featureSeed() {
+ return this.featureSeed;
+ }
+
+ public java.util.stream.LongStream featureSeedStream() {
+ return java.util.stream.LongStream.of(this.featureSeed);
+ }
+ // DivineMC end - Implement Secure Seed
+
public boolean generateStructures() {
return this.generateStructures;
}
@@ -59,17 +99,25 @@ public class WorldOptions {
return this.legacyCustomOptions.isPresent();
}
+ // DivineMC start - Implement Secure Seed
public WorldOptions withBonusChest(boolean generateBonusChest) {
- return new WorldOptions(this.seed, this.generateStructures, generateBonusChest, this.legacyCustomOptions);
+ return isSecureSeedEnabled
+ ? new WorldOptions(this.seed, this.featureSeed, this.generateStructures, generateBonusChest, this.legacyCustomOptions)
+ : new WorldOptions(this.seed, this.generateStructures, generateBonusChest, this.legacyCustomOptions);
}
public WorldOptions withStructures(boolean generateStructures) {
- return new WorldOptions(this.seed, generateStructures, this.generateBonusChest, this.legacyCustomOptions);
+ return isSecureSeedEnabled
+ ? new WorldOptions(this.seed, this.featureSeed, generateStructures, this.generateBonusChest, this.legacyCustomOptions)
+ : new WorldOptions(this.seed, generateStructures, this.generateBonusChest, this.legacyCustomOptions);
}
public WorldOptions withSeed(OptionalLong seed) {
- return new WorldOptions(seed.orElse(randomSeed()), this.generateStructures, this.generateBonusChest, this.legacyCustomOptions);
+ return isSecureSeedEnabled
+ ? new WorldOptions(seed.orElse(randomSeed()), su.plo.matter.Globals.createRandomWorldSeed(), this.generateStructures, this.generateBonusChest, this.legacyCustomOptions)
+ : new WorldOptions(seed.orElse(randomSeed()), this.generateStructures, this.generateBonusChest, this.legacyCustomOptions);
}
+ // DivineMC end - Implement Secure Seed
public static OptionalLong parseSeed(String seed) {
seed = seed.trim();
diff --git a/net/minecraft/world/level/levelgen/feature/GeodeFeature.java b/net/minecraft/world/level/levelgen/feature/GeodeFeature.java
index 4e72eb49dbf4c70ae7556ba6eb210fcd5ef36aaa..00e20c8c76ff8d902c3ea85ed96dfa3649c8e301 100644
--- a/net/minecraft/world/level/levelgen/feature/GeodeFeature.java
+++ b/net/minecraft/world/level/levelgen/feature/GeodeFeature.java
@@ -41,7 +41,11 @@ public class GeodeFeature extends Feature<GeodeConfiguration> {
int i1 = geodeConfiguration.maxGenOffset;
List<Pair<BlockPos, Integer>> list = Lists.newLinkedList();
int i2 = geodeConfiguration.distributionPoints.sample(randomSource);
- WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(worldGenLevel.getSeed()));
+ // DivineMC start - Implement Secure Seed
+ WorldgenRandom worldgenRandom = org.bxteam.divinemc.config.DivineConfig.MiscCategory.enableSecureSeed
+ ? new su.plo.matter.WorldgenCryptoRandom(0, 0, su.plo.matter.Globals.Salt.GEODE_FEATURE, 0)
+ : new WorldgenRandom(new LegacyRandomSource(worldGenLevel.getSeed()));
+ // DivineMC end - Implement Secure Seed
NormalNoise normalNoise = NormalNoise.create(worldgenRandom, -4, 1.0);
List<BlockPos> list1 = Lists.newLinkedList();
double d = (double)i2 / geodeConfiguration.outerWallDistance.getMaxValue();
diff --git a/net/minecraft/world/level/levelgen/structure/Structure.java b/net/minecraft/world/level/levelgen/structure/Structure.java
index 8328e864c72b7a358d6bb1f33459b8c4df2ecb1a..95881be2b7a5d16df22e8842acaba037b91a7009 100644
--- a/net/minecraft/world/level/levelgen/structure/Structure.java
+++ b/net/minecraft/world/level/levelgen/structure/Structure.java
@@ -249,6 +249,14 @@ public abstract class Structure {
}
private static WorldgenRandom makeRandom(long seed, ChunkPos chunkPos) {
+ // DivineMC start - Implement Secure Seed
+ if (org.bxteam.divinemc.config.DivineConfig.MiscCategory.enableSecureSeed) {
+ return new su.plo.matter.WorldgenCryptoRandom(
+ chunkPos.x, chunkPos.z, su.plo.matter.Globals.Salt.GENERATE_FEATURE, seed
+ );
+ }
+ // DivineMC end - Implement Secure Seed
+
WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L));
worldgenRandom.setLargeFeatureSeed(seed, chunkPos.x, chunkPos.z);
return worldgenRandom;
diff --git a/net/minecraft/world/level/levelgen/structure/placement/RandomSpreadStructurePlacement.java b/net/minecraft/world/level/levelgen/structure/placement/RandomSpreadStructurePlacement.java
index ee0d9dddb36b6879fa113299e24f1aa3b2b151cc..3af3bf800215ef78b98a4866df572f3ba263055d 100644
--- a/net/minecraft/world/level/levelgen/structure/placement/RandomSpreadStructurePlacement.java
+++ b/net/minecraft/world/level/levelgen/structure/placement/RandomSpreadStructurePlacement.java
@@ -67,8 +67,17 @@ public class RandomSpreadStructurePlacement extends StructurePlacement {
public ChunkPos getPotentialStructureChunk(long seed, int regionX, int regionZ) {
int i = Math.floorDiv(regionX, this.spacing);
int i1 = Math.floorDiv(regionZ, this.spacing);
- WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L));
- worldgenRandom.setLargeFeatureWithSalt(seed, i, i1, this.salt());
+ // DivineMC start - Implement Secure Seed
+ WorldgenRandom worldgenRandom;
+ if (org.bxteam.divinemc.config.DivineConfig.MiscCategory.enableSecureSeed) {
+ worldgenRandom = new su.plo.matter.WorldgenCryptoRandom(
+ i, i1, su.plo.matter.Globals.Salt.POTENTIONAL_FEATURE, this.salt
+ );
+ } else {
+ worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L));
+ worldgenRandom.setLargeFeatureWithSalt(seed, i, i1, this.salt());
+ }
+ // DivineMC end - Implement Secure Seed
int i2 = this.spacing - this.separation;
int i3 = this.spreadType.evaluate(worldgenRandom, i2);
int i4 = this.spreadType.evaluate(worldgenRandom, i2);
diff --git a/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java b/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java
index 26b1b448c95ab38a449843a0ce929a008019b151..5660c6b1a60f78faa3a0e15f8317fea68ced7c83 100644
--- a/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java
+++ b/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java
@@ -119,8 +119,17 @@ public abstract class StructurePlacement {
public abstract StructurePlacementType<?> type();
private static boolean probabilityReducer(long levelSeed, int regionX, int regionZ, int salt, float probability, @javax.annotation.Nullable Integer saltOverride) { // Paper - Add missing structure set seed configs; ignore here
- WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L));
- worldgenRandom.setLargeFeatureWithSalt(levelSeed, regionX, regionZ, salt);
+ // DivineMC start - Implement Secure Seed
+ WorldgenRandom worldgenRandom;
+ if (org.bxteam.divinemc.config.DivineConfig.MiscCategory.enableSecureSeed) {
+ worldgenRandom = new su.plo.matter.WorldgenCryptoRandom(
+ regionX, regionZ, su.plo.matter.Globals.Salt.UNDEFINED, salt
+ );
+ } else {
+ worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L));
+ worldgenRandom.setLargeFeatureWithSalt(levelSeed, regionX, regionZ, salt);
+ }
+ // DivineMC end - Implement Secure Seed
return worldgenRandom.nextFloat() < probability;
}
diff --git a/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java b/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java
index 3b66b16612b8328eaea786cc8ba3a59fd67900e4..fc31c2419fc895942391bbace8323791e0c7bf38 100644
--- a/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java
+++ b/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java
@@ -66,7 +66,11 @@ public class JigsawPlacement {
ChunkGenerator chunkGenerator = context.chunkGenerator();
StructureTemplateManager structureTemplateManager = context.structureTemplateManager();
LevelHeightAccessor levelHeightAccessor = context.heightAccessor();
- WorldgenRandom worldgenRandom = context.random();
+ // DivineMC start - Implement Secure Seed
+ WorldgenRandom worldgenRandom = org.bxteam.divinemc.config.DivineConfig.MiscCategory.enableSecureSeed
+ ? new su.plo.matter.WorldgenCryptoRandom(context.chunkPos().x, context.chunkPos().z, su.plo.matter.Globals.Salt.JIGSAW_PLACEMENT, 0)
+ : context.random();
+ // DivineMC end - Implement Secure Seed
Registry<StructureTemplatePool> registry = registryAccess.lookupOrThrow(Registries.TEMPLATE_POOL);
Rotation random = Rotation.getRandom(worldgenRandom);
StructureTemplatePool structureTemplatePool = startPool.unwrapKey()

View File

@@ -0,0 +1,54 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Wed, 9 Jul 2025 04:37:59 +0300
Subject: [PATCH] C2ME: Limit NBT cache
This patch is based on the following mixins:
* "com/ishland/c2me/opts/chunkio/mixin/limit_nbt_cache/MixinStorageIoWorker.java"
By: ishland <ishlandmc@yeah.net>
As part of: C2ME (https://github.com/RelativityMC/C2ME-fabric)
Licensed under: MIT (https://opensource.org/licenses/MIT)
diff --git a/net/minecraft/world/level/chunk/storage/IOWorker.java b/net/minecraft/world/level/chunk/storage/IOWorker.java
index 27e1edbd8d8ffd80c1a3df17bc47f4a6936619f7..c3326e753ecf8a0ba1930d8c7573ebd2c594cf45 100644
--- a/net/minecraft/world/level/chunk/storage/IOWorker.java
+++ b/net/minecraft/world/level/chunk/storage/IOWorker.java
@@ -212,7 +212,38 @@ public class IOWorker implements ChunkScanAccess, AutoCloseable {
});
}
+ // DivineMC start - C2ME: Limit NBT cache
+ private void checkHardLimit() {
+ if (this.pendingWrites.size() >= org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.chunkDataCacheLimit) {
+ LOGGER.warn("Chunk data cache size exceeded hard limit ({} >= {}), forcing writes to disk (you can increase chunkDataCacheLimit in divinemc.yml)", this.pendingWrites.size(), org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.chunkDataCacheLimit);
+ while (this.pendingWrites.size() >= org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.chunkDataCacheSoftLimit * 0.75) {
+ writeResult0();
+ }
+ }
+ }
+
+ private void writeResult0() {
+ java.util.Iterator<java.util.Map.Entry<net.minecraft.world.level.ChunkPos, net.minecraft.world.level.chunk.storage.IOWorker.PendingStore>> iterator = this.pendingWrites.entrySet().iterator();
+ if (iterator.hasNext()) {
+ java.util.Map.Entry<ChunkPos, IOWorker.PendingStore> entry = iterator.next();
+ iterator.remove();
+ this.runStore(entry.getKey(), entry.getValue());
+ }
+ }
+ // DivineMC end - C2ME: Limit NBT cache
+
private void storePendingChunk() {
+ // DivineMC start - C2ME: Limit NBT cache
+ if (!this.pendingWrites.isEmpty()) {
+ checkHardLimit();
+ if (this.pendingWrites.size() >= org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.chunkDataCacheSoftLimit) {
+ int writeFrequency = Math.min(1, (this.pendingWrites.size() - (int) org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.chunkDataCacheSoftLimit) / 16);
+ for (int i = 0; i < writeFrequency; i++) {
+ writeResult0();
+ }
+ }
+ }
+ // DivineMC end - C2ME: Limit NBT cache
Entry<ChunkPos, IOWorker.PendingStore> entry = this.pendingWrites.pollFirstEntry();
if (entry != null) {
this.runStore(entry.getKey(), entry.getValue());

View File

@@ -0,0 +1,813 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Tue, 28 Jan 2025 01:04:55 +0300
Subject: [PATCH] Petal: Async Pathfinding
Original code by Bloom-host, licensed under GPL v3
You can find the original code on https://github.com/Bloom-host/Petal
Makes most pathfinding-related work happen asynchronously
diff --git a/net/minecraft/world/entity/ai/behavior/AcquirePoi.java b/net/minecraft/world/entity/ai/behavior/AcquirePoi.java
index 21046cde1bd1ede8e7851eb4ea414e33628aa4a9..9fd3b55dc640e96de05c149b90dcbb459b414f4b 100644
--- a/net/minecraft/world/entity/ai/behavior/AcquirePoi.java
+++ b/net/minecraft/world/entity/ai/behavior/AcquirePoi.java
@@ -93,21 +93,18 @@ public class AcquirePoi {
}
}
// Paper end - optimise POI access
- Path path = findPathToPois(mob, set);
- if (path != null && path.canReach()) {
- BlockPos target = path.getTarget();
- poiManager.getType(target).ifPresent(holder -> {
- poiManager.take(acquirablePois, (holder1, blockPos) -> blockPos.equals(target), target, 1);
- memoryAccessor.set(GlobalPos.of(level.dimension(), target));
- entityEventId.ifPresent(id -> level.broadcastEntityEvent(mob, id));
- map.clear();
- level.debugSynchronizers().updatePoi(target);
+ // DivineMC start - Async path processing
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.asyncPathfinding) {
+ Path possiblePath = findPathToPois(mob, set);
+
+ org.bxteam.divinemc.async.pathfinding.AsyncPathProcessor.awaitProcessing(possiblePath, path -> {
+ processPath(acquirablePois, entityEventId, (Long2ObjectMap<JitteredLinearRetry>) map, memoryAccessor, level, mob, time, poiManager, set, path);
});
} else {
- for (Pair<Holder<PoiType>, BlockPos> pair : set) {
- map.computeIfAbsent(pair.getSecond().asLong(), l -> new AcquirePoi.JitteredLinearRetry(level.random, time));
- }
+ Path path = findPathToPois(mob, set);
+ processPath(acquirablePois, entityEventId, (Long2ObjectMap<JitteredLinearRetry>) map, memoryAccessor, level, mob, time, poiManager, set, path);
}
+ // DivineMC end - Async path processing
return true;
}
@@ -119,6 +116,34 @@ public class AcquirePoi {
: BehaviorBuilder.create(instance -> instance.group(instance.absent(existingAbsentMemory)).apply(instance, memoryAccessor -> oneShot));
}
+ // DivineMC start - Async path processing
+ private static void processPath(Predicate<Holder<PoiType>> acquirablePois,
+ Optional<Byte> entityEventId,
+ Long2ObjectMap<JitteredLinearRetry> map,
+ net.minecraft.world.entity.ai.behavior.declarative.MemoryAccessor<com.mojang.datafixers.kinds.Const.Mu<com.mojang.datafixers.util.Unit>, GlobalPos> memoryAccessor,
+ ServerLevel level,
+ PathfinderMob mob,
+ long time,
+ PoiManager poiManager,
+ Set<Pair<Holder<PoiType>, BlockPos>> set,
+ Path path) {
+ if (path != null && path.canReach()) {
+ BlockPos target = path.getTarget();
+ poiManager.getType(target).ifPresent(holder -> {
+ poiManager.take(acquirablePois, (holder1, blockPos) -> blockPos.equals(target), target, 1);
+ memoryAccessor.set(GlobalPos.of(level.dimension(), target));
+ entityEventId.ifPresent(id -> level.broadcastEntityEvent(mob, id));
+ map.clear();
+ level.debugSynchronizers().updatePoi(target);
+ });
+ } else {
+ for (Pair<Holder<PoiType>, BlockPos> pair : set) {
+ map.computeIfAbsent(pair.getSecond().asLong(), l -> new JitteredLinearRetry(level.random, time));
+ }
+ }
+ }
+ // DivineMC end - Async path processing
+
@Nullable
public static Path findPathToPois(Mob mob, Set<Pair<Holder<PoiType>, BlockPos>> poiPositions) {
if (poiPositions.isEmpty()) {
diff --git a/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java b/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java
index 621ba76784f2b92790eca62be4d0688834335ab6..92d8899ff7d42ecc987a7bf2035cc72484ea9e82 100644
--- a/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java
+++ b/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java
@@ -21,6 +21,7 @@ public class MoveToTargetSink extends Behavior<Mob> {
private int remainingCooldown;
@Nullable
private Path path;
+ private boolean finishedProcessing; // DivineMC - async path processing
@Nullable
private BlockPos lastTargetPos;
private float speedModifier;
@@ -53,9 +54,11 @@ public class MoveToTargetSink extends Behavior<Mob> {
Brain<?> brain = owner.getBrain();
WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get();
boolean flag = this.reachedTarget(owner, walkTarget);
- if (!flag && this.tryComputePath(owner, walkTarget, level.getGameTime())) {
+ if (!org.bxteam.divinemc.config.DivineConfig.AsyncCategory.asyncPathfinding && !flag && this.tryComputePath(owner, walkTarget, level.getGameTime())) { // DivineMC - async path processing
this.lastTargetPos = walkTarget.getTarget().currentBlockPosition();
return true;
+ } else if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.asyncPathfinding && !flag) { // DivineMC - async pathfinding
+ return true;
} else {
brain.eraseMemory(MemoryModuleType.WALK_TARGET);
if (flag) {
@@ -69,6 +72,7 @@ public class MoveToTargetSink extends Behavior<Mob> {
@Override
protected boolean canStillUse(ServerLevel level, Mob entity, long gameTime) {
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.asyncPathfinding && !this.finishedProcessing) return true; // DivineMC - wait for processing
if (this.path != null && this.lastTargetPos != null) {
Optional<WalkTarget> memory = entity.getBrain().getMemory(MemoryModuleType.WALK_TARGET);
boolean flag = memory.map(MoveToTargetSink::isWalkTargetSpectator).orElse(false);
@@ -95,27 +99,98 @@ public class MoveToTargetSink extends Behavior<Mob> {
@Override
protected void start(ServerLevel level, Mob entity, long gameTime) {
+ // DivineMC start - start processing
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.asyncPathfinding) {
+ Brain<?> brain = entity.getBrain();
+ WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get();
+
+ this.finishedProcessing = false;
+ this.lastTargetPos = walkTarget.getTarget().currentBlockPosition();
+ this.path = this.computePath(entity, walkTarget);
+ return;
+ }
+ // DivineMC end - start processing
entity.getBrain().setMemory(MemoryModuleType.PATH, this.path);
entity.getNavigation().moveTo(this.path, (double)this.speedModifier);
}
@Override
protected void tick(ServerLevel level, Mob owner, long gameTime) {
- Path path = owner.getNavigation().getPath();
- Brain<?> brain = owner.getBrain();
- if (this.path != path) {
- this.path = path;
- brain.setMemory(MemoryModuleType.PATH, path);
- }
+ // DivineMC start - Async path processing
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.asyncPathfinding) {
+ if (this.path != null && !this.path.isProcessed()) return; // wait for processing
- if (path != null && this.lastTargetPos != null) {
- WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get();
- if (walkTarget.getTarget().currentBlockPosition().distSqr(this.lastTargetPos) > 4.0 && this.tryComputePath(owner, walkTarget, level.getGameTime())) {
- this.lastTargetPos = walkTarget.getTarget().currentBlockPosition();
- this.start(level, owner, gameTime);
+ if (!this.finishedProcessing) {
+ this.finishedProcessing = true;
+
+ Brain<?> brain = owner.getBrain();
+ boolean canReach = this.path != null && this.path.canReach();
+ if (canReach) {
+ brain.eraseMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE);
+ } else if (!brain.hasMemoryValue(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE)) {
+ brain.setMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE, gameTime);
+ }
+
+ if (!canReach) {
+ Optional<WalkTarget> walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET);
+
+ if (!walkTarget.isPresent()) return;
+
+ BlockPos blockPos = walkTarget.get().getTarget().currentBlockPosition();
+ Vec3 vec3 = DefaultRandomPos.getPosTowards((PathfinderMob) owner, 10, 7, Vec3.atBottomCenterOf(blockPos), (float) Math.PI / 2F);
+ if (vec3 != null) {
+ // try recalculating the path using a random position
+ this.path = owner.getNavigation().createPath(vec3.x, vec3.y, vec3.z, 0);
+ this.finishedProcessing = false;
+ return;
+ }
+ }
+
+ owner.getBrain().setMemory(MemoryModuleType.PATH, this.path);
+ owner.getNavigation().moveTo(this.path, this.speedModifier);
}
+
+ Path path = owner.getNavigation().getPath();
+ Brain<?> brain = owner.getBrain();
+
+ if (path != null && this.lastTargetPos != null && brain.hasMemoryValue(MemoryModuleType.WALK_TARGET)) {
+ WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get(); // we know isPresent = true
+ if (walkTarget.getTarget().currentBlockPosition().distSqr(this.lastTargetPos) > 4.0D) {
+ this.start(level, owner, gameTime);
+ }
+ }
+ } else {
+ Path path = owner.getNavigation().getPath();
+ Brain<?> brain = owner.getBrain();
+ if (this.path != path) {
+ this.path = path;
+ brain.setMemory(MemoryModuleType.PATH, path);
+ }
+
+ if (path != null && this.lastTargetPos != null) {
+ WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get();
+ if (walkTarget.getTarget().currentBlockPosition().distSqr(this.lastTargetPos) > 4.0
+ && this.tryComputePath(owner, walkTarget, level.getGameTime())) {
+ this.lastTargetPos = walkTarget.getTarget().currentBlockPosition();
+ this.start(level, owner, gameTime);
+ }
+ }
+ }
+ // DivineMC end - Async path processing
+ }
+
+ // DivineMC start - Async path processing
+ @Nullable
+ private Path computePath(Mob entity, WalkTarget walkTarget) {
+ BlockPos blockPos = walkTarget.getTarget().currentBlockPosition();
+ this.speedModifier = walkTarget.getSpeedModifier();
+ Brain<?> brain = entity.getBrain();
+ if (this.reachedTarget(entity, walkTarget)) {
+ brain.eraseMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE);
}
+ return entity.getNavigation().createPath(blockPos, 0);
}
+ // DivineMC end - Async path processing
private boolean tryComputePath(Mob mob, WalkTarget target, long time) {
BlockPos blockPos = target.getTarget().currentBlockPosition();
diff --git a/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java b/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java
index 348ff9ef8595fa9324d41ec1328f8d7a503d1d13..55b57381008deeb4965ecaa29932ae168a201dbb 100644
--- a/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java
+++ b/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java
@@ -59,17 +59,18 @@ public class SetClosestHomeAsWalkTarget {
poi -> poi.is(PoiTypes.HOME), predicate, mob.blockPosition(), 48, PoiManager.Occupancy.ANY
)
.collect(Collectors.toSet());
- Path path = AcquirePoi.findPathToPois(mob, set);
- if (path != null && path.canReach()) {
- BlockPos target = path.getTarget();
- Optional<Holder<PoiType>> type = poiManager.getType(target);
- if (type.isPresent()) {
- walkTarget.set(new WalkTarget(target, speedModifier, 1));
- level.debugSynchronizers().updatePoi(target);
- }
- } else if (mutableInt.getValue() < 5) {
- map.long2LongEntrySet().removeIf(entry -> entry.getLongValue() < mutableLong.getValue());
+ // DivineMC start - async path processing
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.asyncPathfinding) {
+ Path possiblePath = AcquirePoi.findPathToPois(mob, set);
+
+ org.bxteam.divinemc.async.pathfinding.AsyncPathProcessor.awaitProcessing(possiblePath, path -> {
+ processPath(speedModifier, map, mutableLong, walkTarget, level, poiManager, mutableInt, path);
+ });
+ } else {
+ Path path = AcquirePoi.findPathToPois(mob, set);
+ processPath(speedModifier, map, mutableLong, walkTarget, level, poiManager, mutableInt, path);
}
+ // DivineMC end - async path processing
return true;
} else {
@@ -80,4 +81,26 @@ public class SetClosestHomeAsWalkTarget {
)
);
}
+
+ // DivineMC start - async path processing
+ private static void processPath(float speedModifier,
+ Long2LongMap map,
+ MutableLong mutableLong,
+ net.minecraft.world.entity.ai.behavior.declarative.MemoryAccessor<com.mojang.datafixers.kinds.Const.Mu<com.mojang.datafixers.util.Unit>, WalkTarget> walkTarget,
+ net.minecraft.server.level.ServerLevel level,
+ PoiManager poiManager,
+ MutableInt mutableInt,
+ @org.jetbrains.annotations.Nullable Path path) {
+ if (path != null && path.canReach()) {
+ BlockPos target = path.getTarget();
+ Optional<Holder<PoiType>> type = poiManager.getType(target);
+ if (type.isPresent()) {
+ walkTarget.set(new WalkTarget(target, speedModifier, 1));
+ level.debugSynchronizers().updatePoi(target);
+ }
+ } else if (mutableInt.getValue() < 5) {
+ map.long2LongEntrySet().removeIf(entry -> entry.getLongValue() < mutableLong.getValue());
+ }
+ }
+ // DivineMC end - async path processing
}
diff --git a/net/minecraft/world/entity/ai/goal/DoorInteractGoal.java b/net/minecraft/world/entity/ai/goal/DoorInteractGoal.java
index 73bba480f3f017a8aed14562bd82ba33db04391c..b31976b68eec3cd0ab0620a487e99ecd49f78186 100644
--- a/net/minecraft/world/entity/ai/goal/DoorInteractGoal.java
+++ b/net/minecraft/world/entity/ai/goal/DoorInteractGoal.java
@@ -54,7 +54,7 @@ public abstract class DoorInteractGoal extends Goal {
return false;
} else {
Path path = this.mob.getNavigation().getPath();
- if (path != null && !path.isDone()) {
+ if (path != null && path.isProcessed() && !path.isDone()) { // DivineMC - Async Pathfinding
for (int i = 0; i < Math.min(path.getNextNodeIndex() + 2, path.getNodeCount()); i++) {
Node node = path.getNode(i);
this.doorPos = new BlockPos(node.x, node.y + 1, node.z);
diff --git a/net/minecraft/world/entity/ai/navigation/AmphibiousPathNavigation.java b/net/minecraft/world/entity/ai/navigation/AmphibiousPathNavigation.java
index 458ceec68ca138b0aa9b70d6c934473c01d468f4..ff06ba3ede2f2e40aae8f9a0b997150cfaaeecb7 100644
--- a/net/minecraft/world/entity/ai/navigation/AmphibiousPathNavigation.java
+++ b/net/minecraft/world/entity/ai/navigation/AmphibiousPathNavigation.java
@@ -12,9 +12,25 @@ public class AmphibiousPathNavigation extends PathNavigation {
super(mob, level);
}
+ // DivineMC start - async path processing
+ private static final org.bxteam.divinemc.async.pathfinding.NodeEvaluatorGenerator nodeEvaluatorGenerator = (org.bxteam.divinemc.async.pathfinding.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> {
+ AmphibiousNodeEvaluator nodeEvaluator = new AmphibiousNodeEvaluator(false);
+ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors());
+ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat());
+ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences());
+ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors());
+ return nodeEvaluator;
+ };
+ // DivineMC end - async path processing
+
@Override
protected PathFinder createPathFinder(int maxVisitedNodes) {
this.nodeEvaluator = new AmphibiousNodeEvaluator(false);
+ // DivineMC start - async path processing
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.asyncPathfinding) {
+ return new PathFinder(this.nodeEvaluator, maxVisitedNodes, nodeEvaluatorGenerator);
+ }
+ // DivineMC end - async path processing
return new PathFinder(this.nodeEvaluator, maxVisitedNodes);
}
diff --git a/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java b/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java
index e21a79f77b6775764816ec45fc9023f52a00af84..2e82b6b884ff6296b2affa29e7b3ce0551372265 100644
--- a/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java
+++ b/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java
@@ -15,9 +15,25 @@ public class FlyingPathNavigation extends PathNavigation {
super(mob, level);
}
+ // DivineMC start - async path processing
+ private static final org.bxteam.divinemc.async.pathfinding.NodeEvaluatorGenerator nodeEvaluatorGenerator = (org.bxteam.divinemc.async.pathfinding.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> {
+ FlyNodeEvaluator nodeEvaluator = new FlyNodeEvaluator();
+ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors());
+ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat());
+ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences());
+ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors());
+ return nodeEvaluator;
+ };
+ // DivineMC end - async path processing
+
@Override
protected PathFinder createPathFinder(int maxVisitedNodes) {
this.nodeEvaluator = new FlyNodeEvaluator();
+ // DivineMC start - async path processing
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.asyncPathfinding) {
+ return new PathFinder(this.nodeEvaluator, maxVisitedNodes, nodeEvaluatorGenerator);
+ }
+ // DivineMC end - async path processing
return new PathFinder(this.nodeEvaluator, maxVisitedNodes);
}
@@ -47,6 +63,7 @@ public class FlyingPathNavigation extends PathNavigation {
if (this.hasDelayedRecomputation) {
this.recomputePath();
}
+ if (this.path != null && !this.path.isProcessed()) return; // DivineMC - async path processing
if (!this.isDone()) {
if (this.canUpdatePath()) {
diff --git a/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java b/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java
index f2f07146a3638fe07f4814abd22a9bf815507fd2..2ee4c8f02bd62c67bacb1d817b3aed24a79dc050 100644
--- a/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java
+++ b/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java
@@ -25,9 +25,25 @@ public class GroundPathNavigation extends PathNavigation {
super(mob, level);
}
+ // DivineMC start - async path processing
+ protected static final org.bxteam.divinemc.async.pathfinding.NodeEvaluatorGenerator nodeEvaluatorGenerator = (org.bxteam.divinemc.async.pathfinding.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> {
+ WalkNodeEvaluator nodeEvaluator = new WalkNodeEvaluator();
+ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors());
+ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat());
+ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences());
+ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors());
+ return nodeEvaluator;
+ };
+ // DivineMC end - async path processing
+
@Override
protected PathFinder createPathFinder(int maxVisitedNodes) {
this.nodeEvaluator = new WalkNodeEvaluator();
+ // DivineMC start - async path processing
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.asyncPathfinding) {
+ return new PathFinder(this.nodeEvaluator, maxVisitedNodes, nodeEvaluatorGenerator);
+ }
+ // DivineMC end - async path processing
return new PathFinder(this.nodeEvaluator, maxVisitedNodes);
}
diff --git a/net/minecraft/world/entity/ai/navigation/PathNavigation.java b/net/minecraft/world/entity/ai/navigation/PathNavigation.java
index 1f45b389553cd5782972193537ce7adcd9c7c600..777b3ccce23b0ffd84176b12207a1bbc4beda379 100644
--- a/net/minecraft/world/entity/ai/navigation/PathNavigation.java
+++ b/net/minecraft/world/entity/ai/navigation/PathNavigation.java
@@ -173,6 +173,10 @@ public abstract class PathNavigation {
return null;
} else if (!this.canUpdatePath()) {
return null;
+ // DivineMC start - catch early if it's still processing these positions let it keep processing
+ } else if (this.path instanceof org.bxteam.divinemc.async.pathfinding.AsyncPath asyncPath && !asyncPath.isProcessed() && asyncPath.hasSameProcessingPositions(targets)) {
+ return this.path;
+ // DivineMC end - catch early if it's still processing these positions let it keep processing
} else if (this.path != null && !this.path.isDone() && targets.contains(this.targetPos)) {
return this.path;
} else {
@@ -197,11 +201,29 @@ public abstract class PathNavigation {
int i = (int)(followRange + regionOffset);
PathNavigationRegion pathNavigationRegion = new PathNavigationRegion(this.level, blockPos.offset(-i, -i, -i), blockPos.offset(i, i, i));
Path path = this.pathFinder.findPath(pathNavigationRegion, this.mob, targets, followRange, accuracy, this.maxVisitedNodesMultiplier);
- if (path != null && path.getTarget() != null) {
- this.targetPos = path.getTarget();
- this.reachRange = accuracy;
- this.resetStuckTimeout();
+ // DivineMC start - async path processing
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.asyncPathfinding) {
+ // assign early a target position. most calls will only have 1 position
+ if (!targets.isEmpty()) this.targetPos = targets.iterator().next();
+
+ org.bxteam.divinemc.async.pathfinding.AsyncPathProcessor.awaitProcessing(path, processedPath -> {
+ // check that processing didn't take so long that we calculated a new path
+ if (processedPath != this.path) return;
+
+ if (processedPath != null && processedPath.getTarget() != null) {
+ this.targetPos = processedPath.getTarget();
+ this.reachRange = accuracy;
+ this.resetStuckTimeout();
+ }
+ });
+ } else {
+ if (path != null && path.getTarget() != null) {
+ this.targetPos = path.getTarget();
+ this.reachRange = accuracy;
+ this.resetStuckTimeout();
+ }
}
+ // DivineMC end - async path processing
return path;
}
@@ -252,8 +274,8 @@ public abstract class PathNavigation {
if (this.isDone()) {
return false;
} else {
- this.trimPath();
- if (this.path.getNodeCount() <= 0) {
+ if (path.isProcessed()) this.trimPath(); // DivineMC - only trim if processed
+ if (path.isProcessed() && this.path.getNodeCount() <= 0) { // DivineMC - only check node count if processed
return false;
} else {
this.speedModifier = speed;
@@ -276,6 +298,7 @@ public abstract class PathNavigation {
if (this.hasDelayedRecomputation) {
this.recomputePath();
}
+ if (this.path != null && !this.path.isProcessed()) return; // DivineMC - skip pathfinding if we're still processing
if (!this.isDone()) {
if (this.canUpdatePath()) {
@@ -304,6 +327,7 @@ public abstract class PathNavigation {
}
protected void followThePath() {
+ if (!this.path.isProcessed()) return; // DivineMC - skip if not processed
Vec3 tempMobPos = this.getTempMobPos();
this.maxDistanceToWaypoint = this.mob.getBbWidth() > 0.75F ? this.mob.getBbWidth() / 2.0F : 0.75F - this.mob.getBbWidth() / 2.0F;
Vec3i nextNodePos = this.path.getNextNodePos();
@@ -460,7 +484,7 @@ public abstract class PathNavigation {
public boolean shouldRecomputePath(BlockPos pos) {
if (this.hasDelayedRecomputation) {
return false;
- } else if (this.path != null && !this.path.isDone() && this.path.getNodeCount() != 0) {
+ } else if (this.path != null && this.path.isProcessed() && !this.path.isDone() && this.path.getNodeCount() != 0) { // DivineMC - Skip if not processed
Node endNode = this.path.getEndNode();
Vec3 vec3 = new Vec3((endNode.x + this.mob.getX()) / 2.0, (endNode.y + this.mob.getY()) / 2.0, (endNode.z + this.mob.getZ()) / 2.0);
return pos.closerToCenterThan(vec3, this.path.getNodeCount() - this.path.getNextNodeIndex());
diff --git a/net/minecraft/world/entity/ai/navigation/WaterBoundPathNavigation.java b/net/minecraft/world/entity/ai/navigation/WaterBoundPathNavigation.java
index ea0f6a19e4a79538e68917ba86cbc98be4dbca8d..030d90f93dbbc07e94d4776198c368650539bf91 100644
--- a/net/minecraft/world/entity/ai/navigation/WaterBoundPathNavigation.java
+++ b/net/minecraft/world/entity/ai/navigation/WaterBoundPathNavigation.java
@@ -15,11 +15,27 @@ public class WaterBoundPathNavigation extends PathNavigation {
super(mob, level);
}
+ // DivineMC start - async path processing
+ private static final org.bxteam.divinemc.async.pathfinding.NodeEvaluatorGenerator nodeEvaluatorGenerator = (org.bxteam.divinemc.async.pathfinding.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> {
+ SwimNodeEvaluator nodeEvaluator = new SwimNodeEvaluator(nodeEvaluatorFeatures.allowBreaching());
+ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors());
+ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat());
+ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences());
+ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors());
+ return nodeEvaluator;
+ };
+ // DivineMC end - async path processing
+
@Override
protected PathFinder createPathFinder(int maxVisitedNodes) {
this.allowBreaching = this.mob.getType() == EntityType.DOLPHIN;
this.nodeEvaluator = new SwimNodeEvaluator(this.allowBreaching);
this.nodeEvaluator.setCanPassDoors(false);
+ // DivineMC start - async path processing
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.asyncPathfinding) {
+ return new PathFinder(this.nodeEvaluator, maxVisitedNodes, nodeEvaluatorGenerator);
+ }
+ // DivineMC end - async path processing
return new PathFinder(this.nodeEvaluator, maxVisitedNodes);
}
diff --git a/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java b/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java
index 1f96fd5085bacb4c584576c7cb9f51e7898e9b03..d975b89c7bb57562852596751a4ff881d3ecf193 100644
--- a/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java
+++ b/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java
@@ -57,17 +57,32 @@ public class NearestBedSensor extends Sensor<Mob> {
java.util.List<Pair<Holder<PoiType>, BlockPos>> poiposes = new java.util.ArrayList<>();
// don't ask me why it's unbounded. ask mojang.
io.papermc.paper.util.PoiAccess.findAnyPoiPositions(poiManager, type -> type.is(PoiTypes.HOME), predicate, entity.blockPosition(), level.purpurConfig.villagerNearestBedSensorSearchRadius, PoiManager.Occupancy.ANY, false, Integer.MAX_VALUE, poiposes); // Purpur - Configurable villager search radius
- Path path = AcquirePoi.findPathToPois(entity, new java.util.HashSet<>(poiposes));
- // Paper end - optimise POI access
- if (path != null && path.canReach()) {
- BlockPos target = path.getTarget();
- Optional<Holder<PoiType>> type = poiManager.getType(target);
- if (type.isPresent()) {
- entity.getBrain().setMemory(MemoryModuleType.NEAREST_BED, target);
- }
- } else if (this.triedCount < 5) {
- this.batchCache.long2LongEntrySet().removeIf(entry -> entry.getLongValue() < this.lastUpdate);
+ // DivineMC start - async pathfinding
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.asyncPathfinding) {
+ Path possiblePath = AcquirePoi.findPathToPois(entity, new java.util.HashSet<>(poiposes));
+ org.bxteam.divinemc.async.pathfinding.AsyncPathProcessor.awaitProcessing(possiblePath, path -> {
+ processPath(entity, poiManager, path);
+ });
+ } else {
+ Path path = AcquirePoi.findPathToPois(entity, new java.util.HashSet<>(poiposes));
+ // Paper end - optimise POI access
+ processPath(entity, poiManager, path);
+ }
+ // DivineMC end - async pathfinding
+ }
+ }
+
+ // DivineMC start - async pathfinding
+ private void processPath(Mob entity, PoiManager poiManager, @org.jetbrains.annotations.Nullable Path path) {
+ if (path != null && path.canReach()) {
+ BlockPos target = path.getTarget();
+ Optional<Holder<PoiType>> type = poiManager.getType(target);
+ if (type.isPresent()) {
+ entity.getBrain().setMemory(MemoryModuleType.NEAREST_BED, target);
}
+ } else if (this.triedCount < 5) {
+ this.batchCache.long2LongEntrySet().removeIf(entry -> entry.getLongValue() < this.lastUpdate);
}
}
+ // DivineMC end - async pathfinding
}
diff --git a/net/minecraft/world/entity/animal/Bee.java b/net/minecraft/world/entity/animal/Bee.java
index a520052f0feae97c5ed8eb4af4fb48cdf56d6550..127840b75b39ec6a68e504396948c3c523535fd9 100644
--- a/net/minecraft/world/entity/animal/Bee.java
+++ b/net/minecraft/world/entity/animal/Bee.java
@@ -944,7 +944,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
} else {
Bee.this.pathfindRandomlyTowards(Bee.this.hivePos);
}
- } else {
+ } else if (navigation.getPath() != null && navigation.getPath().isProcessed()) { // DivineMC - check processing
boolean flag = this.pathfindDirectlyTowards(Bee.this.hivePos);
if (!flag) {
this.dropAndBlacklistHive();
@@ -998,7 +998,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
return true;
} else {
Path path = Bee.this.navigation.getPath();
- return path != null && path.getTarget().equals(pos) && path.canReach() && path.isDone();
+ return path != null && path.isProcessed() && path.getTarget().equals(pos) && path.canReach() && path.isDone(); // DivineMC - ensure path is processed
}
}
}
diff --git a/net/minecraft/world/entity/animal/frog/Frog.java b/net/minecraft/world/entity/animal/frog/Frog.java
index 9656c1bf22b1b6c945a8ba5603742261db650fd5..be75453e5426d2d2819983b17014e0e8675961bd 100644
--- a/net/minecraft/world/entity/animal/frog/Frog.java
+++ b/net/minecraft/world/entity/animal/frog/Frog.java
@@ -480,6 +480,17 @@ public class Frog extends Animal {
super(mob, level);
}
+ // DivineMC start - async path processing
+ private static final org.bxteam.divinemc.async.pathfinding.NodeEvaluatorGenerator nodeEvaluatorGenerator = (org.bxteam.divinemc.async.pathfinding.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> {
+ Frog.FrogNodeEvaluator nodeEvaluator = new Frog.FrogNodeEvaluator(true);
+ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors());
+ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat());
+ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences());
+ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors());
+ return nodeEvaluator;
+ };
+ // DivineMC end - async path processing
+
@Override
public boolean canCutCorner(PathType pathType) {
return pathType != PathType.WATER_BORDER && super.canCutCorner(pathType);
@@ -488,6 +499,11 @@ public class Frog extends Animal {
@Override
protected PathFinder createPathFinder(int maxVisitedNodes) {
this.nodeEvaluator = new Frog.FrogNodeEvaluator(true);
+ // DivineMC start - async path processing
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.asyncPathfinding) {
+ return new PathFinder(this.nodeEvaluator, maxVisitedNodes, nodeEvaluatorGenerator);
+ }
+ // DivineMC end - async path processing
return new PathFinder(this.nodeEvaluator, maxVisitedNodes);
}
}
diff --git a/net/minecraft/world/entity/monster/Drowned.java b/net/minecraft/world/entity/monster/Drowned.java
index 12650e3981fe48ab0cb4398e021e768eea03bd60..63e6fbcdead96fb98a527b8c6ba76d49c9d3eb66 100644
--- a/net/minecraft/world/entity/monster/Drowned.java
+++ b/net/minecraft/world/entity/monster/Drowned.java
@@ -304,7 +304,7 @@ public class Drowned extends Zombie implements RangedAttackMob {
protected boolean closeToNextPos() {
Path path = this.getNavigation().getPath();
- if (path != null) {
+ if (path != null && path.isProcessed()) { // DivineMC - ensure path is processed
BlockPos target = path.getTarget();
if (target != null) {
double d = this.distanceToSqr(target.getX(), target.getY(), target.getZ());
diff --git a/net/minecraft/world/entity/monster/Strider.java b/net/minecraft/world/entity/monster/Strider.java
index 592095f4c78866c53745786a615f1681dcaf6bf6..06a6bc22e408ab4366715fb57edfede37a9e5117 100644
--- a/net/minecraft/world/entity/monster/Strider.java
+++ b/net/minecraft/world/entity/monster/Strider.java
@@ -560,9 +560,25 @@ public class Strider extends Animal implements ItemSteerable {
super(strider, level);
}
+ // DivineMC start - async path processing
+ private static final org.bxteam.divinemc.async.pathfinding.NodeEvaluatorGenerator nodeEvaluatorGenerator = (org.bxteam.divinemc.async.pathfinding.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> {
+ WalkNodeEvaluator nodeEvaluator = new WalkNodeEvaluator();
+ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors());
+ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat());
+ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences());
+ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors());
+ return nodeEvaluator;
+ };
+ // DivineMC end - async path processing
+
@Override
protected PathFinder createPathFinder(int maxVisitedNodes) {
this.nodeEvaluator = new WalkNodeEvaluator();
+ // DivineMC start - async path processing
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.asyncPathfinding) {
+ return new PathFinder(this.nodeEvaluator, maxVisitedNodes, nodeEvaluatorGenerator);
+ }
+ // DivineMC end
return new PathFinder(this.nodeEvaluator, maxVisitedNodes);
}
diff --git a/net/minecraft/world/entity/monster/warden/Warden.java b/net/minecraft/world/entity/monster/warden/Warden.java
index e3478244e430faa614f23c288019303bd6bb0e04..862a7d464e1045e543049523d771bf79e8da7541 100644
--- a/net/minecraft/world/entity/monster/warden/Warden.java
+++ b/net/minecraft/world/entity/monster/warden/Warden.java
@@ -572,6 +572,16 @@ public class Warden extends Monster implements VibrationSystem {
@Override
protected PathFinder createPathFinder(int maxVisitedNodes) {
this.nodeEvaluator = new WalkNodeEvaluator();
+ // DivineMC start - async path processing
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.asyncPathfinding) {
+ return new PathFinder(this.nodeEvaluator, maxVisitedNodes, GroundPathNavigation.nodeEvaluatorGenerator) {
+ @Override
+ protected float distance(Node first, Node second) {
+ return first.distanceToXZ(second);
+ }
+ };
+ }
+ // DivineMC end - async path processing
return new PathFinder(this.nodeEvaluator, maxVisitedNodes) {
@Override
protected float distance(Node first, Node second) {
diff --git a/net/minecraft/world/level/pathfinder/Path.java b/net/minecraft/world/level/pathfinder/Path.java
index d8d086b54f07a855cf312b6f742802e267dfd034..42bf9d4d4a66c1de99ac1ad5b24210f17ec6c0ad 100644
--- a/net/minecraft/world/level/pathfinder/Path.java
+++ b/net/minecraft/world/level/pathfinder/Path.java
@@ -28,6 +28,17 @@ public final class Path {
this.reached = reached;
}
+ // DivineMC start - async path processing
+ /**
+ * checks if the path is completely processed in the case of it being computed async
+ *
+ * @return true if the path is processed
+ */
+ public boolean isProcessed() {
+ return true;
+ }
+ // DivineMC end - async path processing
+
public void advance() {
this.nextNodeIndex++;
}
@@ -101,6 +112,7 @@ public final class Path {
}
public boolean sameAs(@Nullable Path pathEntity) {
+ if (pathEntity == this) return true; // DivineMC - async path processing
return pathEntity != null && this.nodes.equals(pathEntity.nodes);
}
diff --git a/net/minecraft/world/level/pathfinder/PathFinder.java b/net/minecraft/world/level/pathfinder/PathFinder.java
index 98abda72d88fb38a5427a15cc59094f3a7db30dc..d829ed8c15779d2c8f2df51d2136f206c79fd2bf 100644
--- a/net/minecraft/world/level/pathfinder/PathFinder.java
+++ b/net/minecraft/world/level/pathfinder/PathFinder.java
@@ -23,11 +23,19 @@ public class PathFinder {
public final NodeEvaluator nodeEvaluator;
private final BinaryHeap openSet = new BinaryHeap();
private BooleanSupplier captureDebug = () -> false;
+ private final @Nullable org.bxteam.divinemc.async.pathfinding.NodeEvaluatorGenerator nodeEvaluatorGenerator; // DivineMC - we use this later to generate an evaluator
- public PathFinder(NodeEvaluator nodeEvaluator, int maxVisitedNodes) {
+ // DivineMC start - support nodeEvaluatorgenerators
+ public PathFinder(NodeEvaluator nodeEvaluator, int maxVisitedNodes, @Nullable org.bxteam.divinemc.async.pathfinding.NodeEvaluatorGenerator nodeEvaluatorGenerator) { // DivineMC - add nodeEvaluatorGenerator
this.nodeEvaluator = nodeEvaluator;
this.maxVisitedNodes = maxVisitedNodes;
+ this.nodeEvaluatorGenerator = nodeEvaluatorGenerator;
+ }
+
+ public PathFinder(NodeEvaluator nodeEvaluator, int maxVisitedNodes) {
+ this(nodeEvaluator, maxVisitedNodes, null);
}
+ // DivineMC end - support nodeEvaluatorgenerators
public void setCaptureDebug(BooleanSupplier captureDebug) {
this.captureDebug = captureDebug;
@@ -39,26 +47,63 @@ public class PathFinder {
@Nullable
public Path findPath(PathNavigationRegion region, Mob mob, Set<BlockPos> targetPositions, float maxRange, int accuracy, float searchDepthMultiplier) {
- this.openSet.clear();
- this.nodeEvaluator.prepare(region, mob);
- Node start = this.nodeEvaluator.getStart();
+ // DivineMC start - use a generated evaluator if we have one otherwise run sync
+ if (!org.bxteam.divinemc.config.DivineConfig.AsyncCategory.asyncPathfinding)
+ this.openSet.clear(); // it's always cleared in processPath
+ NodeEvaluator nodeEvaluator = this.nodeEvaluatorGenerator == null
+ ? this.nodeEvaluator
+ : org.bxteam.divinemc.async.pathfinding.NodeEvaluatorCache.takeNodeEvaluator(this.nodeEvaluatorGenerator, this.nodeEvaluator);
+ nodeEvaluator.prepare(region, mob);
+ Node start = nodeEvaluator.getStart();
+ // DivineMC end - use a generated evaluator if we have one otherwise run sync
if (start == null) {
+ org.bxteam.divinemc.async.pathfinding.NodeEvaluatorCache.removeNodeEvaluator(nodeEvaluator); // DivineMC - handle nodeEvaluatorGenerator
return null;
} else {
// Paper start - Perf: remove streams and optimize collection
List<Map.Entry<Target, BlockPos>> map = Lists.newArrayList();
for (BlockPos pos : targetPositions) {
- map.add(new java.util.AbstractMap.SimpleEntry<>(this.nodeEvaluator.getTarget(pos.getX(), pos.getY(), pos.getZ()), pos));
+ map.add(new java.util.AbstractMap.SimpleEntry<>(nodeEvaluator.getTarget(pos.getX(), pos.getY(), pos.getZ()), pos)); // DivineMC - handle nodeEvaluatorGenerator
}
// Paper end - Perf: remove streams and optimize collection
- Path path = this.findPath(start, map, maxRange, accuracy, searchDepthMultiplier);
- this.nodeEvaluator.done();
- return path;
+ // DivineMC start - async path processing
+ if (this.nodeEvaluatorGenerator == null) {
+ // run sync :(
+ org.bxteam.divinemc.async.pathfinding.NodeEvaluatorCache.removeNodeEvaluator(nodeEvaluator);
+ return this.findPath(start, map, maxRange, accuracy, searchDepthMultiplier);
+ }
+
+ return new org.bxteam.divinemc.async.pathfinding.AsyncPath(Lists.newArrayList(), targetPositions, () -> {
+ try {
+ return this.processPath(nodeEvaluator, start, map, maxRange, accuracy, searchDepthMultiplier);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ } finally {
+ nodeEvaluator.done();
+ org.bxteam.divinemc.async.pathfinding.NodeEvaluatorCache.returnNodeEvaluator(nodeEvaluator);
+ }
+ });
+ // DivineMC end - async path processing
}
}
@Nullable
private Path findPath(Node node, List<Map.Entry<Target, BlockPos>> positions, float maxRange, int accuracy, float searchDepthMultiplier) { // Paper - optimize collection
+ // DivineMC start - split pathfinding into the original sync method for compat and processing for delaying
+ try {
+ return this.processPath(this.nodeEvaluator, node, positions, maxRange, accuracy, searchDepthMultiplier);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ } finally {
+ this.nodeEvaluator.done();
+ }
+ }
+
+ private synchronized @org.jetbrains.annotations.NotNull Path processPath(NodeEvaluator nodeEvaluator, Node node, List<Map.Entry<Target, BlockPos>> positions, float maxRange, int accuracy, float searchDepthMultiplier) { // sync to only use the caching functions in this class on a single thread
+ org.apache.commons.lang3.Validate.isTrue(!positions.isEmpty()); // ensure that we have at least one position, which means we'll always return a path
+ // DivineMC end - split pathfinding into the original sync method for compat and processing for delaying
// Set<Target> set = targetPositions.keySet(); // Paper - unused
node.g = 0.0F;
node.h = this.getBestH(node, positions); // Paper - optimize collection
@@ -96,7 +141,7 @@ public class PathFinder {
if (!(node1.distanceTo(node) >= maxRange)) {
- int neighbors = this.nodeEvaluator.getNeighbors(this.neighbors, node1);
+ int neighbors = nodeEvaluator.getNeighbors(this.neighbors, node1); // DivineMC - use provided nodeEvaluator
for (int i2 = 0; i2 < neighbors; i2++) {
Node node2 = this.neighbors[i2];

View File

@@ -0,0 +1,178 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Wed, 19 Mar 2025 23:24:32 +0300
Subject: [PATCH] Pufferfish: Optimize mob spawning
Original license: GPL v3
Original project: https://github.com/pufferfish-gg/Pufferfish
This patch reduces the main-thread impact of mob spawning by moving spawning work to other threads
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index a876c7c47788218a86cef53330b2c07f7008b0b0..b65e35cd5bf7f3af8826cb9e9e2696921a951a62 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -287,6 +287,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
public boolean lagging = false; // Purpur - Lagging threshold
protected boolean upnp = false; // Purpur - UPnP Port Forwarding
public final org.bxteam.divinemc.util.tps.TPSCalculator tpsCalculator = new org.bxteam.divinemc.util.tps.TPSCalculator(); // DivineMC - Lag compensation
+ public gg.pufferfish.pufferfish.util.AsyncExecutor mobSpawnExecutor = new gg.pufferfish.pufferfish.util.AsyncExecutor("Mob Spawning"); // DivineMC - Pufferfish: Optimize mob spawning
// Paper start - improve tick loop
public final ca.spottedleaf.moonrise.common.time.TickData tickTimes1s = new ca.spottedleaf.moonrise.common.time.TickData(java.util.concurrent.TimeUnit.SECONDS.toNanos(1L));
public final ca.spottedleaf.moonrise.common.time.TickData tickTimes5s = new ca.spottedleaf.moonrise.common.time.TickData(java.util.concurrent.TimeUnit.SECONDS.toNanos(5L));
diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java
index f9ec63ed7feb67e558abed99746726c10e9e10f4..78f831bf369906396860a73b9aaff5dc67bcfa09 100644
--- a/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/net/minecraft/server/dedicated/DedicatedServer.java
@@ -452,7 +452,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
org.purpurmc.purpur.task.BossBarTask.startAll(); // Purpur - Implement TPSBar
if (org.purpurmc.purpur.PurpurConfig.beeCountPayload) org.purpurmc.purpur.task.BeehiveTask.instance().register(); // Purpur - Give bee counts in beehives to Purpur clients
-
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableAsyncSpawning) mobSpawnExecutor.start(); // DivineMC - Pufferfish: Optimize mob spawning
this.notificationManager().serverStarted();
return true;
}
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
index 65607de63f6ea900599660485861860b71e1aef3..0a32c1106d3eebb8b4aa75b27b489169052897db 100644
--- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -183,6 +183,10 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
}
// Paper end - chunk tick iteration optimisations
+ // DivineMC start - Pufferfish: Optimize mob spawning
+ public boolean firstRunSpawnCounts = true;
+ public final java.util.concurrent.atomic.AtomicBoolean spawnCountsReady = new java.util.concurrent.atomic.AtomicBoolean(false);
+ // DivineMC end - Pufferfish: Optimize mob spawning
public ServerChunkCache(
ServerLevel level,
@@ -501,6 +505,47 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
this.broadcastChangedChunks();
}
+
+ // DivineMC start - Pufferfish: Optimize mob spawning
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableAsyncSpawning) {
+ for (ServerPlayer player : this.level.players) {
+ for (int ii = 0; ii < ServerPlayer.MOBCATEGORY_TOTAL_ENUMS; ii++) {
+ player.mobCounts[ii] = 0;
+
+ int newBackoff = Math.max(0, player.mobBackoffCounts[ii] - 1);
+ player.mobBackoffCounts[ii] = newBackoff;
+ }
+ }
+
+ if (firstRunSpawnCounts) {
+ firstRunSpawnCounts = false;
+ spawnCountsReady.set(true);
+ }
+
+ if (spawnCountsReady.getAndSet(false)) {
+ net.minecraft.server.MinecraftServer.getServer().mobSpawnExecutor.submit(() -> {
+ int mapped = distanceManager.getNaturalSpawnChunkCount();
+ ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet.Iterator<Entity> objectiterator = level.entityTickList.entities.iterator(ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS);
+
+ try {
+ gg.pufferfish.pufferfish.util.IterableWrapper<Entity> wrappedIterator = new gg.pufferfish.pufferfish.util.IterableWrapper<>(objectiterator);
+ LocalMobCapCalculator mobCapCalculator = !level.paperConfig().entities.spawning.perPlayerMobSpawns ? new LocalMobCapCalculator(chunkMap) : null;
+
+ lastSpawnState = NaturalSpawner.createState(
+ mapped,
+ wrappedIterator,
+ ServerChunkCache.this::getFullChunk,
+ mobCapCalculator,
+ level.paperConfig().entities.spawning.perPlayerMobSpawns
+ );
+ } finally {
+ objectiterator.finishedIterating();
+ }
+ spawnCountsReady.set(true);
+ });
+ }
+ }
+ // DivineMC end - Pufferfish: Optimize mob spawning
}
private void broadcastChangedChunks() {
@@ -518,27 +563,31 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
int naturalSpawnChunkCount = this.distanceManager.getNaturalSpawnChunkCount();
// Paper start - Optional per player mob spawns
NaturalSpawner.SpawnState spawnState;
+ // DivineMC start - Pufferfish: Optimize mob spawning
if ((this.spawnFriendlies || this.spawnEnemies) && this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { // don't count mobs when animals and monsters are disabled
- // re-set mob counts
- for (ServerPlayer player : this.level.players) {
- // Paper start - per player mob spawning backoff
- for (int ii = 0; ii < ServerPlayer.MOBCATEGORY_TOTAL_ENUMS; ii++) {
- player.mobCounts[ii] = 0;
-
- int newBackoff = player.mobBackoffCounts[ii] - 1; // TODO make configurable bleed // TODO use nonlinear algorithm?
- if (newBackoff < 0) {
- newBackoff = 0;
+ if (!org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableAsyncSpawning) {
+ // re-set mob counts
+ for (ServerPlayer player : this.level.players) {
+ // Paper start - per player mob spawning backoff
+ for (int ii = 0; ii < ServerPlayer.MOBCATEGORY_TOTAL_ENUMS; ii++) {
+ player.mobCounts[ii] = 0;
+
+ int newBackoff = player.mobBackoffCounts[ii] - 1; // TODO make configurable bleed // TODO use nonlinear algorithm?
+ if (newBackoff < 0) {
+ newBackoff = 0;
+ }
+ player.mobBackoffCounts[ii] = newBackoff;
}
- player.mobBackoffCounts[ii] = newBackoff;
+ // Paper end - per player mob spawning backoff
}
- // Paper end - per player mob spawning backoff
+ lastSpawnState = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, new LocalMobCapCalculator(this.chunkMap), true);
}
- spawnState = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, null, true);
} else {
- spawnState = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, !this.level.paperConfig().entities.spawning.perPlayerMobSpawns ? new LocalMobCapCalculator(this.chunkMap) : null, false);
+ lastSpawnState = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, new LocalMobCapCalculator(this.chunkMap), false);
+ spawnCountsReady.set(true);
}
+ // DivineMC end - Pufferfish: Optimize mob spawning
// Paper end - Optional per player mob spawns
- this.lastSpawnState = spawnState;
boolean _boolean = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit
int _int = this.level.getGameRules().getInt(GameRules.RULE_RANDOMTICKING);
List<MobCategory> filteredSpawningCategories;
@@ -552,7 +601,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
}
// Paper end - PlayerNaturallySpawnCreaturesEvent
boolean flag = this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) != 0L && this.level.getLevelData().getGameTime() % this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) == 0L; // CraftBukkit
- filteredSpawningCategories = NaturalSpawner.getFilteredSpawningCategories(spawnState, this.spawnFriendlies, this.spawnEnemies, flag, this.level); // CraftBukkit
+ filteredSpawningCategories = NaturalSpawner.getFilteredSpawningCategories(lastSpawnState, this.spawnFriendlies, this.spawnEnemies, flag, this.level); // CraftBukkit // DivineMC - Pufferfish: Optimize mob spawning
} else {
filteredSpawningCategories = List.of();
}
@@ -567,7 +616,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
// Paper end - chunk tick iteration optimisation
for (LevelChunk levelChunk : list) {
- this.tickSpawningChunk(levelChunk, timeInhabited, filteredSpawningCategories, spawnState);
+ this.tickSpawningChunk(levelChunk, timeInhabited, filteredSpawningCategories, lastSpawnState); // DivineMC - Pufferfish: Optimize mob spawning
}
} finally {
list.clear();
@@ -586,11 +635,11 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
this.level.tickThunder(chunk);
}
- if (!spawnCategories.isEmpty()) {
- if (this.level.getWorldBorder().isWithinBounds(pos)) { // Paper - rewrite chunk system
- NaturalSpawner.spawnForChunk(this.level, chunk, spawnState, spawnCategories);
- }
+ // DivineMC start - Pufferfish: Optimize mob spawning
+ if (!spawnCategories.isEmpty() && this.level.getWorldBorder().isWithinBounds(pos) && (!org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableAsyncSpawning || spawnCountsReady.get())) { // Paper - rewrite chunk system
+ NaturalSpawner.spawnForChunk(this.level, chunk, spawnState, spawnCategories);
}
+ // DivineMC end - Pufferfish: Optimize mob spawning
}
private void getFullChunk(long chunkPos, Consumer<LevelChunk> fullChunkGetter) {

View File

@@ -0,0 +1,49 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Thu, 10 Jul 2025 04:30:21 +0300
Subject: [PATCH] Copper Bulb 1gt delay
diff --git a/net/minecraft/world/level/block/CopperBulbBlock.java b/net/minecraft/world/level/block/CopperBulbBlock.java
index 54511c27d8d85f1a9702d899f1f7c7dda201cdfa..1a9b785b1a0b06dcc9f4b9520303eab33e092db0 100644
--- a/net/minecraft/world/level/block/CopperBulbBlock.java
+++ b/net/minecraft/world/level/block/CopperBulbBlock.java
@@ -33,16 +33,36 @@ public class CopperBulbBlock extends Block {
@Override
protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) {
if (oldState.getBlock() != state.getBlock() && level instanceof ServerLevel serverLevel) {
- this.checkAndFlip(state, serverLevel, pos);
+ // DivineMC start - Copper Bulb 1gt delay
+ if (!org.bxteam.divinemc.config.DivineConfig.MiscCategory.copperBulb1gt) {
+ this.checkAndFlip(state, serverLevel, pos);
+ } else {
+ level.scheduleTick(pos, this, 1);
+ }
+ // DivineMC end - Copper Bulb 1gt delay
}
}
@Override
protected void neighborChanged(BlockState state, Level level, BlockPos pos, Block neighborBlock, @Nullable Orientation orientation, boolean movedByPiston) {
if (level instanceof ServerLevel serverLevel) {
- this.checkAndFlip(state, serverLevel, pos);
+ // DivineMC start - Copper Bulb 1gt delay
+ if (!org.bxteam.divinemc.config.DivineConfig.MiscCategory.copperBulb1gt) {
+ this.checkAndFlip(state, serverLevel, pos);
+ } else {
+ level.scheduleTick(pos, this, 1);
+ }
+ }
+ }
+
+ // DivineMC start - Copper Bulb 1gt delay
+ @Override
+ public void tick(BlockState state, ServerLevel level, BlockPos pos, net.minecraft.util.RandomSource random) {
+ if (org.bxteam.divinemc.config.DivineConfig.MiscCategory.copperBulb1gt) {
+ checkAndFlip(state, level, pos);
}
}
+ // DivineMC end - Copper Bulb 1gt delay
public void checkAndFlip(BlockState state, ServerLevel level, BlockPos pos) {
boolean hasNeighborSignal = level.hasNeighborSignal(pos);

View File

@@ -0,0 +1,674 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Tue, 28 Jan 2025 01:18:49 +0300
Subject: [PATCH] Petal: Multithreaded Tracker
Original project: https://github.com/Bloom-host/Petal
Original license: GPL v3
Patch description:
We made much of tracking logic asynchronously, and fixed visible issue
for the case of some NPC plugins which using real entity type, e.g. Citizens.
But it is still recommending to use those packet based, virtual entity
based NPC plugins, e.g. ZNPC Plus, Adyeshach, Fancy NPC, etc.
diff --git a/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java b/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java
index 1b8193587814225c2ef2c5d9e667436eb50ff6c5..93272808d94e81d31af728ebe85df9a2bc7aedab 100644
--- a/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java
+++ b/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java
@@ -60,7 +60,16 @@ public final class NearbyPlayers {
private final ServerLevel world;
private final Reference2ReferenceOpenHashMap<ServerPlayer, TrackedPlayer[]> players = new Reference2ReferenceOpenHashMap<>();
- private final Long2ReferenceOpenHashMap<TrackedChunk> byChunk = new Long2ReferenceOpenHashMap<>();
+ // DivineMC start - Multithreaded Tracker
+ private final it.unimi.dsi.fastutil.longs.Long2ReferenceMap<TrackedChunk> byChunk;
+ {
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedEnabled) {
+ byChunk = it.unimi.dsi.fastutil.longs.Long2ReferenceMaps.synchronize(new Long2ReferenceOpenHashMap<>());
+ } else {
+ byChunk = new Long2ReferenceOpenHashMap<>();
+ }
+ }
+ // DivineMC end - Multithreaded Tracker
private final Long2ReferenceOpenHashMap<ReferenceList<ServerPlayer>>[] directByChunk = new Long2ReferenceOpenHashMap[TOTAL_MAP_TYPES];
{
for (int i = 0; i < this.directByChunk.length; ++i) {
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
index ea3d63856ed487c4d23b0448c97169c230932832..646bd06468bdd7a7ebc5ecc7e876617ad0fc6c05 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
@@ -345,7 +345,11 @@ public final class RegionizedPlayerChunkLoader {
private boolean canGenerateChunks = true;
private final ArrayDeque<ChunkHolderManager.TicketOperation<?, ?>> delayedTicketOps = new ArrayDeque<>();
- private final LongOpenHashSet sentChunks = new LongOpenHashSet();
+ // DivineMC start - Multithreaded tracker
+ private final LongOpenHashSet sentChunks = org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedEnabled && !org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedCompatModeEnabled
+ ? new org.bxteam.divinemc.util.map.ConcurrentLongHashSet()
+ : new LongOpenHashSet();
+ // DivineMC end - Multithreaded tracker
private static final byte CHUNK_TICKET_STAGE_NONE = 0;
private static final byte CHUNK_TICKET_STAGE_LOADING = 1;
diff --git a/net/minecraft/network/protocol/game/ClientboundUpdateAttributesPacket.java b/net/minecraft/network/protocol/game/ClientboundUpdateAttributesPacket.java
index 9c0c99b936b4a82ebfe924866e53ec71f7bbe9ad..01ed1e3572e9c2ccfd19df117cda0d5cf65b9bcb 100644
--- a/net/minecraft/network/protocol/game/ClientboundUpdateAttributesPacket.java
+++ b/net/minecraft/network/protocol/game/ClientboundUpdateAttributesPacket.java
@@ -32,6 +32,7 @@ public class ClientboundUpdateAttributesPacket implements Packet<ClientGamePacke
this.attributes = Lists.newArrayList();
for (AttributeInstance attributeInstance : attributes) {
+ if (attributeInstance == null) continue; // DivineMC - Multithreaded Tracker
this.attributes
.add(
new ClientboundUpdateAttributesPacket.AttributeSnapshot(
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
index 6251e7dc5bbe7eee18b0c5a8979d9218fee728d0..5b16dad04bf4540df750ce06c18af4b1d80d8648 100644
--- a/net/minecraft/server/level/ChunkMap.java
+++ b/net/minecraft/server/level/ChunkMap.java
@@ -253,9 +253,19 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
final ServerPlayer[] backingSet = inRange.getRawDataUnchecked();
- for (int i = 0, len = inRange.size(); i < len; i++) {
- ++(backingSet[i].mobCounts[index]);
+ // DivineMC start - Multithreaded Tracker
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedEnabled) {
+ for (int i = 0, len = inRange.size(); i < len; i++) {
+ final ServerPlayer player = backingSet[i];
+ if (player == null) continue;
+ ++(player.mobCounts[index]);
+ }
+ } else {
+ for (int i = 0, len = inRange.size(); i < len; i++) {
+ ++(backingSet[i].mobCounts[index]);
+ }
}
+ // DivineMC end - Multithreaded Tracker
}
// Paper start - per player mob count backoff
@@ -1021,6 +1031,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// Paper end - optimise entity tracker
protected void tick() {
+ // DivineMC start - Multithreaded tracker
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedEnabled) {
+ final ServerLevel level = this.level;
+ org.bxteam.divinemc.async.tracking.MultithreadedTracker.tick(level);
+ return;
+ }
+ // DivineMC end - Multithreaded tracker
// Paper start - optimise entity tracker
if (true) {
this.newTrackerTick();
@@ -1172,7 +1189,44 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
final Entity entity;
private final int range;
SectionPos lastSectionPos;
- public final Set<ServerPlayerConnection> seenBy = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl
+ // DivineMC start - Multithreaded tracker
+ public static final ServerPlayerConnection[] EMPTY_OBJECT_ARRAY = new ServerPlayerConnection[0];
+ private final it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<ServerPlayerConnection> nonSyncSeenBy = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>() {
+ @Override
+ public boolean add(ServerPlayerConnection serverPlayerConnection) {
+ seenByUpdated = true;
+ return super.add(serverPlayerConnection);
+ }
+
+ @Override
+ public boolean remove(Object k) {
+ seenByUpdated = true;
+ return super.remove(k);
+ }
+
+ @Override
+ public void clear() {
+ seenByUpdated = true;
+ super.clear();
+ }
+ };
+ public final Set<ServerPlayerConnection> seenBy = org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedEnabled ? it.unimi.dsi.fastutil.objects.ReferenceSets.synchronize(nonSyncSeenBy) : nonSyncSeenBy; // Paper - Perf: optimise map impl
+ private volatile boolean seenByUpdated = true;
+ private volatile ServerPlayerConnection[] seenByArray = EMPTY_OBJECT_ARRAY;
+
+ public ServerPlayerConnection[] seenBy() {
+ if (!seenByUpdated) {
+ return seenByArray;
+ } else {
+ return seenBy.toArray(EMPTY_OBJECT_ARRAY);
+ }
+ }
+
+ public void seenByUpdated() {
+ this.seenByArray = this.seenBy.toArray(EMPTY_OBJECT_ARRAY);
+ seenByUpdated = false;
+ }
+ // DivineMC end - Multithreaded tracker
// Paper start - optimise entity tracker
private long lastChunkUpdate = -1L;
@@ -1199,23 +1253,93 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.lastTrackedChunk = chunk;
final ServerPlayer[] playersRaw = players.getRawDataUnchecked();
+ final int playersLength = Math.min(playersRaw.length, players.size()); // DivineMC - Multithreaded tracker
- for (int i = 0, len = players.size(); i < len; ++i) {
+ for (int i = 0; i < playersLength; ++i) { // DivineMC - Multithreaded tracker
final ServerPlayer player = playersRaw[i];
this.updatePlayer(player);
}
if (lastChunkUpdate != currChunkUpdate || lastTrackedChunk != chunk) {
// need to purge any players possible not in the chunk list
- for (final ServerPlayerConnection conn : new java.util.ArrayList<>(this.seenBy)) {
+ // DivineMC start - Multithreaded tracker
+ boolean removed = false;
+ for (final ServerPlayerConnection conn : this.seenBy()) {
final ServerPlayer player = conn.getPlayer();
if (!players.contains(player)) {
- this.removePlayer(player);
+ removed |= this.removePlayerMulti(player);
}
}
+
+ if (removed) {
+ this.seenByUpdated();
+ }
+ // DivineMC end - Multithreaded tracker
}
}
+ // DivineMC start - Multithreaded tracker
+ public final @Nullable Runnable tickCompact(final ca.spottedleaf.moonrise.common.misc.NearbyPlayers.TrackedChunk chunk) {
+ if (chunk == null) {
+ this.moonrise$clearPlayers();
+ return null;
+ }
+
+ final ca.spottedleaf.moonrise.common.list.ReferenceList<ServerPlayer> players = chunk.getPlayers(ca.spottedleaf.moonrise.common.misc.NearbyPlayers.NearbyMapType.VIEW_DISTANCE);
+
+ if (players == null) {
+ this.moonrise$clearPlayers();
+ return null;
+ }
+
+ final long lastChunkUpdate = this.lastChunkUpdate;
+ final long currChunkUpdate = chunk.getUpdateCount();
+ final ca.spottedleaf.moonrise.common.misc.NearbyPlayers.TrackedChunk lastTrackedChunk = this.lastTrackedChunk;
+ this.lastChunkUpdate = currChunkUpdate;
+ this.lastTrackedChunk = chunk;
+
+ final ServerPlayer[] playersRaw = players.getRawDataUnchecked();
+ final int playersLen = players.size(); // Ensure length won't change in the future tasks
+
+ if (!org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedEnabled || !org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedCompatModeEnabled) {
+ throw new IllegalStateException();
+ }
+ final boolean isServerPlayer = this.entity instanceof ServerPlayer;
+ final boolean isRealPlayer = isServerPlayer && ((ca.spottedleaf.moonrise.patches.chunk_system.player.ChunkSystemServerPlayer) this.entity).moonrise$isRealPlayer();
+ Runnable updatePlayerTasks = () -> {
+ for (int i = 0; i < playersLen; ++i) {
+ final ServerPlayer player = playersRaw[i];
+ this.updatePlayer(player);
+ }
+
+ if (lastChunkUpdate != currChunkUpdate || lastTrackedChunk != chunk) {
+ // need to purge any players possible not in the chunk list
+ boolean removed = false;
+ for (final ServerPlayerConnection conn : this.seenBy()) {
+ final ServerPlayer player = conn.getPlayer();
+ if (!players.contains(player)) {
+ removed |= this.removePlayerMulti(player);
+ }
+ }
+ if (removed) {
+ this.seenByUpdated();
+ }
+ }
+ };
+
+ // Only update asynchronously for real player, and sync update for fake players
+ // This can fix compatibility issue with NPC plugins using real entity type, like Citizens
+ // To prevent visible issue with player type NPCs
+ // btw, still recommend to use packet based NPC plugins, like ZNPC Plus, Adyeshach, Fancy NPC, etc.
+ if (isRealPlayer || !isServerPlayer) {
+ return updatePlayerTasks;
+ } else {
+ updatePlayerTasks.run();
+ return null;
+ }
+ }
+ // DivineMC end - Multithreaded tracker
+
@Override
public final void moonrise$removeNonTickThreadPlayers() {
boolean foundToRemove = false;
@@ -1230,12 +1354,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
return;
}
- for (final ServerPlayerConnection conn : new java.util.ArrayList<>(this.seenBy)) {
+ for (final ServerPlayerConnection conn : this.seenBy()) { // DivineMC - Multithreaded tracker
ServerPlayer player = conn.getPlayer();
if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(player)) {
- this.removePlayer(player);
+ this.removePlayerMulti(player); // DivineMC - Multithreaded tracker
}
}
+ this.seenByUpdated(); // DivineMC - Multithreaded tracker
}
@Override
@@ -1245,10 +1370,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
if (this.seenBy.isEmpty()) {
return;
}
- for (final ServerPlayerConnection conn : new java.util.ArrayList<>(this.seenBy)) {
+ for (final ServerPlayerConnection conn : this.seenBy()) { // DivineMC - Multithreaded tracker
ServerPlayer player = conn.getPlayer();
- this.removePlayer(player);
+ this.removePlayerMulti(player); // DivineMC - Multithreaded tracker
}
+ this.seenByUpdated(); // DivineMC - Multithreaded tracker
}
@Override
@@ -1276,7 +1402,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@Override
public void sendToTrackingPlayers(Packet<? super ClientGamePacketListener> packet) {
- for (ServerPlayerConnection serverPlayerConnection : this.seenBy) {
+ for (ServerPlayerConnection serverPlayerConnection : this.seenBy()) { // DivineMC - Multithreaded tracker // TODO: verify
serverPlayerConnection.send(packet);
}
}
@@ -1291,7 +1417,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@Override
public void sendToTrackingPlayersFiltered(Packet<? super ClientGamePacketListener> packet, Predicate<ServerPlayer> filter) {
- for (ServerPlayerConnection serverPlayerConnection : this.seenBy) {
+ for (ServerPlayerConnection serverPlayerConnection : this.seenBy()) { // DivineMC - Multithreaded tracker // TODO: verify
if (filter.test(serverPlayerConnection.getPlayer())) {
serverPlayerConnection.send(packet);
}
@@ -1299,24 +1425,35 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
public void broadcastRemoved() {
- for (ServerPlayerConnection serverPlayerConnection : this.seenBy) {
+ for (ServerPlayerConnection serverPlayerConnection : this.seenBy()) { // DivineMC - Multithreaded tracker
this.serverEntity.removePairing(serverPlayerConnection.getPlayer());
}
}
+ // DivineMC start - Multithreaded tracker
+ public boolean removePlayerMulti(ServerPlayer player) {
+ if (this.seenBy.remove(player.connection)) {
+ this.serverEntity.removePairing(player);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ // DivineMC end - Multithreaded tracker
+
public void removePlayer(ServerPlayer player) {
- org.spigotmc.AsyncCatcher.catchOp("player tracker clear"); // Spigot
if (this.seenBy.remove(player.connection)) {
this.serverEntity.removePairing(player);
if (this.seenBy.isEmpty()) {
ChunkMap.this.level.debugSynchronizers().dropEntity(this.entity);
}
}
+ this.seenByUpdated(); // DivineMC - Multithreaded tracker
}
public void updatePlayer(ServerPlayer player) {
- org.spigotmc.AsyncCatcher.catchOp("player tracker update"); // Spigot
if (player != this.entity) {
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedEnabled && player == null) return; // DivineMC - Multithreaded tracker
// Paper start - remove allocation of Vec3D here
// Vec3 vec3 = player.position().subtract(this.entity.position());
double vec3_dx = player.getX() - this.entity.getX();
@@ -1344,6 +1481,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// CraftBukkit end
if (flag) {
if (this.seenBy.add(player.connection)) {
+ this.seenByUpdated(); // DivineMC - Multithreaded tracker
// Paper start - entity tracking events
if (io.papermc.paper.event.player.PlayerTrackEntityEvent.getHandlerList().getRegisteredListeners().length == 0 || new io.papermc.paper.event.player.PlayerTrackEntityEvent(player.getBukkitEntity(), this.entity.getBukkitEntity()).callEvent()) {
this.serverEntity.addPairing(player);
@@ -1357,6 +1495,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.serverEntity.onPlayerAdd(); // Paper - fix desync when a player is added to the tracker
}
} else {
+ this.seenByUpdated(); // DivineMC - Multithreaded tracker // TODO: check this
this.removePlayer(player);
}
}
diff --git a/net/minecraft/server/level/ServerBossEvent.java b/net/minecraft/server/level/ServerBossEvent.java
index f106373ef3ac4a8685c2939c9e8361688a285913..b844b6dd89bc53b74c0d1bdbf4657c115a892dc7 100644
--- a/net/minecraft/server/level/ServerBossEvent.java
+++ b/net/minecraft/server/level/ServerBossEvent.java
@@ -13,7 +13,11 @@ import net.minecraft.util.Mth;
import net.minecraft.world.BossEvent;
public class ServerBossEvent extends BossEvent {
- private final Set<ServerPlayer> players = Sets.newHashSet();
+ // DivineMC start - Multithreaded tracker - players can be removed in async tracking
+ private final Set<ServerPlayer> players = org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedEnabled
+ ? Sets.newConcurrentHashSet()
+ : Sets.newHashSet();
+ // DivineMC end - Multithreaded tracker
private final Set<ServerPlayer> unmodifiablePlayers = Collections.unmodifiableSet(this.players);
public boolean visible = true;
diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java
index 737a6ff0bfec9b555fa425619d97b80ef95cb3e6..cfa2c3aa357a0dbb7edf7f0c8cebea5ed2f31cbc 100644
--- a/net/minecraft/server/level/ServerEntity.java
+++ b/net/minecraft/server/level/ServerEntity.java
@@ -122,7 +122,7 @@ public class ServerEntity {
MapId mapId = itemFrame.cachedMapId; // Paper - Perf: Cache map ids on item frames
MapItemSavedData savedData = MapItem.getSavedData(mapId, this.level);
if (savedData != null) {
- for (final net.minecraft.server.network.ServerPlayerConnection connection : this.trackedPlayers) { // Paper
+ for (final net.minecraft.server.network.ServerPlayerConnection connection : this.trackedPlayers.toArray(ChunkMap.TrackedEntity.EMPTY_OBJECT_ARRAY)) { // Paper // DivineMC - Multithreaded tracker
final ServerPlayer serverPlayer = connection.getPlayer(); // Paper
savedData.tickCarriedBy(serverPlayer, item);
Packet<?> updatePacket = savedData.getUpdatePacket(mapId, serverPlayer);
@@ -409,8 +409,6 @@ public class ServerEntity {
// CraftBukkit end
this.synchronizer.sendToTrackingPlayersAndSelf(new ClientboundUpdateAttributesPacket(this.entity.getId(), attributesToSync));
}
-
- attributesToSync.clear();
}
}
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index b240c7f2579e25d520fbc0ab08e801028bc15192..01ad6566c236bac2141f75fa9cf37844e3d97637 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -2579,7 +2579,6 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@Override
public LevelEntityGetter<Entity> getEntities() {
- org.spigotmc.AsyncCatcher.catchOp("Chunk getEntities call"); // Spigot
return this.moonrise$getEntityLookup(); // Paper - rewrite chunk system
}
@@ -2858,7 +2857,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
}
map.carriedByPlayers.remove(player);
- if (map.carriedBy.removeIf(holdingPlayer -> holdingPlayer.player == player)) {
+ if (map.carriedBy.removeIf(holdingPlayer -> holdingPlayer != null && holdingPlayer.player == player)) { // DivineMC - Multithreaded tracker
map.decorations.remove(player.getName().getString());
}
}
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 2638343fcccbaf81d19dcd4fd09534b2e7bee796..e5ece51effaddeb49b689f897da5172958360d5f 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -1942,7 +1942,6 @@ public class ServerGamePacketListenerImpl
}
public void internalTeleport(PositionMoveRotation posMoveRotation, Set<Relative> relatives) {
- org.spigotmc.AsyncCatcher.catchOp("teleport"); // Paper
// Paper start - Prevent teleporting dead entities
if (this.player.isRemoved()) {
LOGGER.info("Attempt to teleport removed player {} restricted", player.getScoreboardName());
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
index 49f83d3dedc16d977f7904971af13cc17ed32882..86370c9f6e83e5815922080c10336d394075b4e9 100644
--- a/net/minecraft/world/entity/LivingEntity.java
+++ b/net/minecraft/world/entity/LivingEntity.java
@@ -1359,13 +1359,13 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
}
private void refreshDirtyAttributes() {
- Set<AttributeInstance> attributesToUpdate = this.getAttributes().getAttributesToUpdate();
+ // DivineMC start - Multithreaded tracker
+ int[] attributesToUpdate = this.getAttributes().getAttributesToUpdateIds();
- for (AttributeInstance attributeInstance : attributesToUpdate) {
- this.onAttributeUpdated(attributeInstance.getAttribute());
+ for (int attribute : attributesToUpdate) {
+ this.onAttributeUpdated(net.minecraft.core.registries.BuiltInRegistries.ATTRIBUTE.get(attribute).orElseThrow());
}
-
- attributesToUpdate.clear();
+ // DivineMC end - Multithreaded tracker
}
protected void onAttributeUpdated(Holder<Attribute> attribute) {
diff --git a/net/minecraft/world/entity/ai/attributes/Attribute.java b/net/minecraft/world/entity/ai/attributes/Attribute.java
index 18563961e9dba1a11265c6ea708881d4e46846ff..355e552f77e7639ddf1c8b4da9868775017563c3 100644
--- a/net/minecraft/world/entity/ai/attributes/Attribute.java
+++ b/net/minecraft/world/entity/ai/attributes/Attribute.java
@@ -16,10 +16,15 @@ public class Attribute {
private boolean syncable;
private final String descriptionId;
public Attribute.Sentiment sentiment = Attribute.Sentiment.POSITIVE;
+ // DivineMC start - Multithreaded Tracker
+ public final int uid;
+ private static final java.util.concurrent.atomic.AtomicInteger SIZE = new java.util.concurrent.atomic.AtomicInteger();
+ // DivineMC end - Multithreaded Tracker
protected Attribute(String descriptionId, double defaultValue) {
this.defaultValue = defaultValue;
this.descriptionId = descriptionId;
+ this.uid = SIZE.getAndAdd(1); // DivineMC - Multithreaded Tracker
}
public double getDefaultValue() {
diff --git a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
index 42ad600c6a5cb20e1d820f169f6a1a17ef3a5195..c93b2c684d773551b14cc2ce024923536780ee17 100644
--- a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
+++ b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
@@ -22,8 +22,24 @@ public class AttributeInstance {
private final Map<AttributeModifier.Operation, Map<ResourceLocation, AttributeModifier>> modifiersByOperation = Maps.newEnumMap(
AttributeModifier.Operation.class
);
- private final Map<ResourceLocation, AttributeModifier> modifierById = new Object2ObjectArrayMap<>();
- private final Map<ResourceLocation, AttributeModifier> permanentModifiers = new Object2ObjectArrayMap<>();
+ // DivineMC start - Multithreaded tracker
+ private final Map<ResourceLocation, AttributeModifier> modifierById;
+ {
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedEnabled) {
+ modifierById = it.unimi.dsi.fastutil.objects.Object2ObjectMaps.synchronize(new Object2ObjectArrayMap<>(), this);
+ } else {
+ modifierById = new Object2ObjectArrayMap<>();
+ }
+ }
+ private final Map<ResourceLocation, AttributeModifier> permanentModifiers;
+ {
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedEnabled) {
+ permanentModifiers = it.unimi.dsi.fastutil.objects.Object2ObjectMaps.synchronize(new Object2ObjectArrayMap<>(), this);
+ } else {
+ permanentModifiers = new Object2ObjectArrayMap<>();
+ }
+ }
+ // DivineMC end - Multithreaded tracker
private double baseValue;
private boolean dirty = true;
private double cachedValue;
@@ -52,7 +68,13 @@ public class AttributeInstance {
@VisibleForTesting
Map<ResourceLocation, AttributeModifier> getModifiers(AttributeModifier.Operation operation) {
- return this.modifiersByOperation.computeIfAbsent(operation, operation1 -> new Object2ObjectOpenHashMap<>());
+ // DivineMC start - Multithreaded tracker
+ return this.modifiersByOperation.computeIfAbsent(operation, operation1 -> {
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedEnabled)
+ return it.unimi.dsi.fastutil.objects.Object2ObjectMaps.synchronize(new Object2ObjectArrayMap<>(), this);
+ else return new Object2ObjectArrayMap<>();
+ });
+ // DivineMC end - Multithreaded tracker
}
public Set<AttributeModifier> getModifiers() {
@@ -140,8 +162,12 @@ public class AttributeInstance {
public double getValue() {
if (this.dirty) {
- this.cachedValue = this.calculateValue();
+ // DivineMC start - Multithreaded tracker
+ double value = this.calculateValue();
+ this.cachedValue = value;
this.dirty = false;
+ return value;
+ // DivineMC end - Multithreaded tracker
}
return this.cachedValue;
@@ -184,7 +210,15 @@ public class AttributeInstance {
}
public AttributeInstance.Packed pack() {
- return new AttributeInstance.Packed(this.attribute, this.baseValue, List.copyOf(this.permanentModifiers.values()));
+ // DivineMC start - Multithreaded tracker
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedEnabled) {
+ synchronized (this) {
+ return new AttributeInstance.Packed(this.attribute, this.baseValue, List.copyOf(this.permanentModifiers.values()));
+ }
+ } else {
+ return new AttributeInstance.Packed(this.attribute, this.baseValue, List.copyOf(this.permanentModifiers.values()));
+ }
+ // DivineMC end - Multithreaded tracker
}
public void apply(AttributeInstance.Packed instance) {
diff --git a/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/net/minecraft/world/entity/ai/attributes/AttributeMap.java
index 7dd8c1c8e27410854ce1ee90defc607c2710b5a2..290a7fa565f695c7afe3cf0791f6cf1da6a39663 100644
--- a/net/minecraft/world/entity/ai/attributes/AttributeMap.java
+++ b/net/minecraft/world/entity/ai/attributes/AttributeMap.java
@@ -14,9 +14,11 @@ import net.minecraft.core.Holder;
import net.minecraft.resources.ResourceLocation;
public class AttributeMap {
- private final Map<Holder<Attribute>, AttributeInstance> attributes = new Object2ObjectOpenHashMap<>();
- private final Set<AttributeInstance> attributesToSync = new ObjectOpenHashSet<>();
- private final Set<AttributeInstance> attributesToUpdate = new ObjectOpenHashSet<>();
+ // DivineMC start - Multithreaded tracker
+ private final Map<Holder<Attribute>, AttributeInstance> attributes = new org.bxteam.divinemc.util.map.AttributeInstanceArrayMap();
+ private final org.bxteam.divinemc.util.map.AttributeInstanceSet attributesToSync = new org.bxteam.divinemc.util.map.AttributeInstanceSet((org.bxteam.divinemc.util.map.AttributeInstanceArrayMap) attributes);
+ private final org.bxteam.divinemc.util.map.AttributeInstanceSet attributesToUpdate = new org.bxteam.divinemc.util.map.AttributeInstanceSet((org.bxteam.divinemc.util.map.AttributeInstanceArrayMap) attributes);
+ // DivineMC end - Multithreaded tracker
private final AttributeSupplier supplier;
private final net.minecraft.world.entity.LivingEntity entity; // Purpur - Ridables
@@ -30,28 +32,52 @@ public class AttributeMap {
this.supplier = defaultAttributes;
}
- private void onAttributeModified(AttributeInstance instance) {
+ private synchronized void onAttributeModified(AttributeInstance instance) { // DivineMC - Multithreaded Tracker
this.attributesToUpdate.add(instance);
if (instance.getAttribute().value().isClientSyncable() && (entity == null || entity.shouldSendAttribute(instance.getAttribute().value()))) { // Purpur - Ridables
this.attributesToSync.add(instance);
}
}
- public Set<AttributeInstance> getAttributesToSync() {
- return this.attributesToSync;
+ // DivineMC start - Multithreaded Tracker
+ private static final AttributeInstance[] EMPTY_ATTRIBUTE_INSTANCE = new AttributeInstance[0];
+ public synchronized Set<AttributeInstance> getAttributesToSync() {
+ var clone = it.unimi.dsi.fastutil.objects.ReferenceArraySet.ofUnchecked(attributesToSync.toArray(EMPTY_ATTRIBUTE_INSTANCE));
+ this.attributesToSync.clear();
+ return clone;
}
- public Set<AttributeInstance> getAttributesToUpdate() {
- return this.attributesToUpdate;
+ public synchronized Set<AttributeInstance> getAttributesToUpdate() {
+ var clone = it.unimi.dsi.fastutil.objects.ReferenceArraySet.ofUnchecked(attributesToUpdate.toArray(EMPTY_ATTRIBUTE_INSTANCE));
+ this.attributesToUpdate.clear();
+ return clone;
}
+ public synchronized int[] getAttributesToUpdateIds() {
+ int[] clone = attributesToUpdate.inner.toIntArray();
+ this.attributesToUpdate.clear();
+ return clone;
+ }
+ // DivineMC end - Multithreaded Tracker
+
public Collection<AttributeInstance> getSyncableAttributes() {
return this.attributes.values().stream().filter(instance -> instance.getAttribute().value().isClientSyncable() && (entity == null || entity.shouldSendAttribute(instance.getAttribute().value()))).collect(Collectors.toList()); // Purpur - Ridables
}
@Nullable
public AttributeInstance getInstance(Holder<Attribute> attribute) {
- return this.attributes.computeIfAbsent(attribute, holder -> this.supplier.createInstance(this::onAttributeModified, (Holder<Attribute>)holder));
+ // DivineMC start - Multithreaded Tracker
+ AttributeInstance v;
+ if ((v = this.attributes.get(attribute)) == null) {
+ AttributeInstance newValue;
+ if ((newValue = this.supplier.createInstance(this::onAttributeModified, attribute)) != null) {
+ attributes.put(attribute, newValue);
+ return newValue;
+ }
+ }
+
+ return v;
+ // DivineMC end - Multithreaded Tracker
}
public boolean hasAttribute(Holder<Attribute> attribute) {
diff --git a/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java b/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java
index 24710041ccbc70e5506d8d89ae34f0141977f209..dbcff8bdd6911843bc42f64d5dcf1bb854128075 100644
--- a/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java
+++ b/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java
@@ -11,7 +11,7 @@ public class AttributeSupplier {
private final Map<Holder<Attribute>, AttributeInstance> instances;
AttributeSupplier(Map<Holder<Attribute>, AttributeInstance> instances) {
- this.instances = instances;
+ this.instances = new org.bxteam.divinemc.util.map.AttributeInstanceArrayMap(instances); // DivineMC - Multithreaded Tracker
}
public AttributeInstance getAttributeInstance(Holder<Attribute> attribute) {
diff --git a/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java b/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java
index 325ec57df2885f5e81b8a6b61e3a9fed9484b30f..1796f0a6f647c94b0943a6003a1307795294805e 100644
--- a/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java
+++ b/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java
@@ -35,13 +35,20 @@ public class NewMinecartBehavior extends MinecartBehavior {
private int cachedLerpDelay;
private float cachedPartialTick;
private int lerpDelay = 0;
- public final List<NewMinecartBehavior.MinecartStep> lerpSteps = new LinkedList<>();
+ public final List<NewMinecartBehavior.MinecartStep> lerpSteps; // DivineMC - Multithreaded Tracker
public final List<NewMinecartBehavior.MinecartStep> currentLerpSteps = new LinkedList<>();
public double currentLerpStepsTotalWeight = 0.0;
public NewMinecartBehavior.MinecartStep oldLerp = NewMinecartBehavior.MinecartStep.ZERO;
public NewMinecartBehavior(AbstractMinecart minecart) {
super(minecart);
+ // DivineMC start - Multithreaded Tracker
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.multithreadedEnabled) {
+ this.lerpSteps = it.unimi.dsi.fastutil.objects.ObjectLists.synchronize(new it.unimi.dsi.fastutil.objects.ObjectArrayList<>());
+ } else {
+ this.lerpSteps = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>();
+ }
+ // DivineMC end - Multithreaded Tracker
}
@Override
diff --git a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java b/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
index 00c7fe0419fa44736b971b684adfe1b963e24bd5..581486ad49945d1658cb070d9f418f7a2fc9196d 100644
--- a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
+++ b/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
@@ -212,6 +212,7 @@ public class MapItemSavedData extends SavedData {
for (int i = 0; i < this.carriedBy.size(); i++) {
MapItemSavedData.HoldingPlayer holdingPlayer1 = this.carriedBy.get(i);
+ if (holdingPlayer1 == null) continue; // DivineMC - Multithreaded tracker
Player player1 = holdingPlayer1.player;
String plainTextName = player1.getPlainTextName();
if (!player1.isRemoved() && (player1.getInventory().contains(predicate) || mapStack.isFramed())) {

View File

@@ -0,0 +1,28 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Thu, 10 Jul 2025 04:31:46 +0300
Subject: [PATCH] Crafter 1gt delay
diff --git a/net/minecraft/world/level/block/CrafterBlock.java b/net/minecraft/world/level/block/CrafterBlock.java
index 6dd3c0f60b04bc690ebd4a33ec57ef806961ee32..dad8035aff94854eadeb454ef60e6c90dbb31d1e 100644
--- a/net/minecraft/world/level/block/CrafterBlock.java
+++ b/net/minecraft/world/level/block/CrafterBlock.java
@@ -75,7 +75,7 @@ public class CrafterBlock extends BaseEntityBlock {
boolean triggeredValue = state.getValue(TRIGGERED);
BlockEntity blockEntity = level.getBlockEntity(pos);
if (hasNeighborSignal && !triggeredValue) {
- level.scheduleTick(pos, this, 4);
+ level.scheduleTick(pos, this, !org.bxteam.divinemc.config.DivineConfig.MiscCategory.copperBulb1gt ? 4 : 1); // DivineMC - Crafter 1gt delay
level.setBlock(pos, state.setValue(TRIGGERED, true), 2);
this.setBlockEntityTriggered(blockEntity, true);
} else if (!hasNeighborSignal && triggeredValue) {
@@ -125,7 +125,7 @@ public class CrafterBlock extends BaseEntityBlock {
@Override
public void setPlacedBy(Level level, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) {
if (state.getValue(TRIGGERED)) {
- level.scheduleTick(pos, this, 4);
+ level.scheduleTick(pos, this, !org.bxteam.divinemc.config.DivineConfig.MiscCategory.copperBulb1gt ? 4 : 1); // DivineMC - Crafter 1gt delay
}
}

View File

@@ -0,0 +1,105 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sun, 13 Jul 2025 20:03:51 +0300
Subject: [PATCH] Cleanup dead code from Paper
diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java
index 3f85f9e9551b2eed6e66ab8036dbb1f40fb8bbac..78650957bacc0e26d3299a8de7f8bfc57c86627c 100644
--- a/net/minecraft/network/Connection.java
+++ b/net/minecraft/network/Connection.java
@@ -602,13 +602,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.PacketProcessor.packetProcessing.push(this.packetListener);
- try {
- tickablePacketListener.tick();
- } finally {
- net.minecraft.network.PacketProcessor.packetProcessing.pop();
- } // Paper end - detailed watchdog information
+ tickablePacketListener.tick();
} // Paper end - Buffer joins to world
}
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 01ad6566c236bac2141f75fa9cf37844e3d97637..a4a2231f5850269a6003afca8db78fa486cf3a71 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -1370,13 +1370,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
@@ -1389,13 +1383,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 6724ef32e4d0de6ca0965b8b96430b68179b4bd7..72628fab95b76b67630594611f894a5ecdad5fc3 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -1117,29 +1117,10 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name
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) {
if (!this.boundingBoxChanged && movement.equals(Vec3.ZERO)) return; // DivineMC - 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);
this.horizontalCollision = false;
@@ -1260,13 +1241,6 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name
this.setDeltaMovement(this.getDeltaMovement().multiply(blockSpeedFactor, 1.0, blockSpeedFactor));
}
}
- // 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) {

View File

@@ -0,0 +1,68 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Wed, 9 Jul 2025 03:06:02 +0300
Subject: [PATCH] Pufferfish: Simpler ShapelessRecipes comparison for Vanilla
Original license: GPL v3
Original project: https://github.com/pufferfish-gg/Pufferfish
Patch description:
Paper added a fancy sorting comparison due to Bukkit recipes breaking
the vanilla one, however this is far more advanced than what you need
for all the vanilla recipes.
diff --git a/net/minecraft/world/item/crafting/ShapelessRecipe.java b/net/minecraft/world/item/crafting/ShapelessRecipe.java
index d601b54b1de2f2ae44fe2b20c8116c71a6340e45..658c950e18a5a4ff992c8720e60f505a11ab2efd 100644
--- a/net/minecraft/world/item/crafting/ShapelessRecipe.java
+++ b/net/minecraft/world/item/crafting/ShapelessRecipe.java
@@ -23,13 +23,21 @@ public class ShapelessRecipe implements CraftingRecipe {
final List<Ingredient> ingredients;
@Nullable
private PlacementInfo placementInfo;
+ // DivineMC start - Pufferfish: Simpler ShapelessRecipes comparison for Vanilla
+ private final boolean isBukkit;
public ShapelessRecipe(String group, CraftingBookCategory category, ItemStack result, List<Ingredient> ingredients) {
+ this(group, category, result, ingredients, false);
+ }
+
+ public ShapelessRecipe(String group, CraftingBookCategory category, ItemStack result, List<Ingredient> ingredients, boolean isBukkit) {
this.group = group;
this.category = category;
this.result = result;
this.ingredients = ingredients;
+ this.isBukkit = isBukkit;
}
+ // DivineMC end - Pufferfish: Simpler ShapelessRecipes comparison for Vanilla
// CraftBukkit start
@Override
@@ -72,6 +80,27 @@ public class ShapelessRecipe implements CraftingRecipe {
@Override
public boolean matches(CraftingInput input, Level level) {
+ // DivineMC start - Pufferfish: Simpler ShapelessRecipes comparison for Vanilla
+ if (!this.isBukkit) {
+ java.util.List<Ingredient> ingredients = com.google.common.collect.Lists.newArrayList(this.ingredients.toArray(new Ingredient[0]));
+
+ inventory: for (int index = 0; index < input.size(); index++) {
+ ItemStack itemStack = input.getItem(index);
+
+ if (!itemStack.isEmpty()) {
+ for (int i = 0; i < ingredients.size(); i++) {
+ if (ingredients.get(i).test(itemStack)) {
+ ingredients.remove(i);
+ continue inventory;
+ }
+ }
+ return false;
+ }
+ }
+
+ return ingredients.isEmpty();
+ }
+ // DivineMC end - Pufferfish: Simpler ShapelessRecipes comparison for Vanilla
// Paper start - Improve exact choice recipe ingredients & unwrap ternary
if (input.ingredientCount() != this.ingredients.size()) {
return false;

View File

@@ -0,0 +1,54 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Thu, 17 Jul 2025 20:53:13 +0300
Subject: [PATCH] C2ME: The End Biome Cache
This patch is based on the following mixins:
* "com/ishland/c2me/opts/worldgen/vanilla/mixin/the_end_biome_cache/MixinTheEndBiomeSource.java"
By: ishland <ishlandmc@yeah.net>
As part of: C2ME (https://github.com/RelativityMC/C2ME-fabric)
Licensed under: MIT (https://opensource.org/licenses/MIT)
diff --git a/net/minecraft/world/level/biome/TheEndBiomeSource.java b/net/minecraft/world/level/biome/TheEndBiomeSource.java
index cf3172be76fa4c7987ed569138439ff42f92fa7f..0545a0dd25917d75b511d507dc19a5ca7d45b9d9 100644
--- a/net/minecraft/world/level/biome/TheEndBiomeSource.java
+++ b/net/minecraft/world/level/biome/TheEndBiomeSource.java
@@ -55,8 +55,37 @@ public class TheEndBiomeSource extends BiomeSource {
return CODEC;
}
+ // DivineMC start - C2ME: The End Biome Cache
+ private final ThreadLocal<it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap<Holder<Biome>>> cache = ThreadLocal.withInitial(it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap::new);
+ private final int cacheCapacity = org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.endBiomeCacheCapacity;
+
@Override
- public Holder<Biome> getNoiseBiome(int x, int y, int z, Climate.Sampler sampler) {
+ public Holder<Biome> getNoiseBiome(int biomeX, int biomeY, int biomeZ, Climate.Sampler multiNoiseSampler) {
+ if (!org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.endBiomeCacheEnabled) {
+ return getVanillaNoiseBiome(biomeX, biomeY, biomeZ, multiNoiseSampler);
+ }
+
+ final long key = net.minecraft.world.level.ChunkPos.asLong(biomeX, biomeZ);
+ final it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap<Holder<Biome>> cacheThreadLocal = cache.get();
+ final Holder<Biome> biome = cacheThreadLocal.get(key);
+
+ if (biome != null) {
+ return biome;
+ } else {
+ final Holder<Biome> gennedBiome = getVanillaNoiseBiome(biomeX, biomeY, biomeZ, multiNoiseSampler);
+ cacheThreadLocal.put(key, gennedBiome);
+ if (cacheThreadLocal.size() > cacheCapacity) {
+ for (int i = 0; i < cacheCapacity / 16; i ++) {
+ cacheThreadLocal.removeFirst();
+ }
+ }
+
+ return gennedBiome;
+ }
+ }
+ // DivineMC end - C2ME: The End Biome Cache
+
+ private Holder<Biome> getVanillaNoiseBiome(int x, int y, int z, Climate.Sampler sampler) { // DivineMC - C2ME: The End Biome Cache
int blockPosX = QuartPos.toBlock(x);
int blockPosY = QuartPos.toBlock(y);
int blockPosZ = QuartPos.toBlock(z);

View File

@@ -0,0 +1,34 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Fri, 18 Jul 2025 13:54:17 +0300
Subject: [PATCH] Euclidean distance squared option
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
index 646bd06468bdd7a7ebc5ecc7e876617ad0fc6c05..87b4ccc200e090a8c5149b3bc5ae457932b8e15d 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
@@ -387,10 +387,19 @@ public final class RegionizedPlayerChunkLoader {
final int centerX = PlayerChunkLoaderData.this.lastChunkX;
final int centerZ = PlayerChunkLoaderData.this.lastChunkZ;
- return Integer.compare(
- Math.abs(c1x - centerX) + Math.abs(c1z - centerZ),
- Math.abs(c2x - centerX) + Math.abs(c2z - centerZ)
- );
+ // DivineMC start - Euclidean distance squared option
+ if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.useEuclideanDistanceSquared) {
+ return Integer.compare(
+ (c1x - centerX) * (c1x - centerX) + (c1z - centerZ) * (c1z - centerZ),
+ (c2x - centerX) * (c2x - centerX) + (c2z - centerZ) * (c2z - centerZ)
+ );
+ } else {
+ return Integer.compare(
+ Math.abs(c1x - centerX) + Math.abs(c1z - centerZ),
+ Math.abs(c2x - centerX) + Math.abs(c2z - centerZ)
+ );
+ }
+ // DivineMC end - Euclidean distance squared option
};
private final LongHeapPriorityQueue sendQueue = new LongHeapPriorityQueue(CLOSEST_MANHATTAN_DIST);
private final LongHeapPriorityQueue tickingQueue = new LongHeapPriorityQueue(CLOSEST_MANHATTAN_DIST);

View File

@@ -0,0 +1,143 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sun, 20 Jul 2025 00:09:31 +0300
Subject: [PATCH] Do not send spectator change packet
diff --git a/net/minecraft/server/level/ServerPlayerGameMode.java b/net/minecraft/server/level/ServerPlayerGameMode.java
index 07dd9b8088e363110ecab24026a20485484710c4..a51d506c4ffe11ac2ad8510a9b35d854b2c0d42c 100644
--- a/net/minecraft/server/level/ServerPlayerGameMode.java
+++ b/net/minecraft/server/level/ServerPlayerGameMode.java
@@ -86,10 +86,7 @@ public class ServerPlayerGameMode {
}
this.player.onUpdateAbilities();
- this.level
- .getServer()
- .getPlayerList()
- .broadcastAll(new ClientboundPlayerInfoUpdatePacket(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE, this.player), this.player); // CraftBukkit
+ this.sendGameModeUpdatePacket(gameModeForPlayer); // DivineMC - Do not send spectator change packet
this.level.updateSleepingPlayerList();
if (gameModeForPlayer == GameType.CREATIVE) {
this.player.resetCurrentImpulseContext();
@@ -595,4 +592,17 @@ public class ServerPlayerGameMode {
return false;
}
// Purpur end - Shift right click to use exp for mending
+
+ // DivineMC start - Do not send spectator change packet
+ private void sendGameModeUpdatePacket(GameType newGameMode) {
+ ClientboundPlayerInfoUpdatePacket packet = new ClientboundPlayerInfoUpdatePacket(
+ ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE, this.player);
+
+ if (org.bxteam.divinemc.config.DivineConfig.NetworkCategory.sendSpectatorChangePacket || newGameMode != GameType.SPECTATOR) {
+ this.level.getServer().getPlayerList().broadcastAll(packet, this.player);
+ } else {
+ this.player.connection.send(packet);
+ }
+ }
+ // DivineMC end - Do not send spectator change packet
}
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index c8e68bbb210457366822f2c4a01afb49693035ac..1a280122b4ff661c2d2fad359ddfb671a1d8f324 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -255,6 +255,7 @@ public abstract class PlayerList {
// CraftBukkit start - sendAll above replaced with this loop
ClientboundPlayerInfoUpdatePacket packet = ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(List.of(player)); // Paper - Add Listing API for Player
+ ClientboundPlayerInfoUpdatePacket modifiedPacket = this.createSpectatorFilteredPacket(packet); // DivineMC - Do not send spectator change packet
final List<ServerPlayer> onlinePlayers = Lists.newArrayListWithExpectedSize(this.players.size() - 1); // Paper - Use single player info update packet on join
for (int i = 0; i < this.players.size(); ++i) {
@@ -264,7 +265,7 @@ public abstract class PlayerList {
// Paper start - Add Listing API for Player
if (entityplayer1.getBukkitEntity().isListed(bukkitPlayer)) {
// Paper end - Add Listing API for Player
- entityplayer1.connection.send(packet);
+ this.sendPlayerInfoPacket(entityplayer1, player, packet, modifiedPacket); // DivineMC - Do not send spectator change packet
// Paper start - Add Listing API for Player
} else {
entityplayer1.connection.send(ClientboundPlayerInfoUpdatePacket.createSinglePlayerInitializing(player, false));
@@ -280,7 +281,10 @@ public abstract class PlayerList {
}
// Paper start - Use single player info update packet on join
if (!onlinePlayers.isEmpty()) {
- player.connection.send(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(onlinePlayers, player)); // Paper - Add Listing API for Player
+ // DivineMC start - Do not send spectator change packet
+ ClientboundPlayerInfoUpdatePacket updatePacket = this.createFilteredPlayerListPacket(onlinePlayers, player);
+ player.connection.send(updatePacket); // Paper - Add Listing API for Player
+ // DivineMC end - Do not send spectator change packet
}
// Paper end - Use single player info update packet on join
player.sentListPacket = true;
@@ -1389,4 +1393,69 @@ public abstract class PlayerList {
public boolean isAllowCommandsForAllPlayers() {
return this.allowCommandsForAllPlayers;
}
+
+ // DivineMC start - Do not send spectator change packet
+ @Nullable
+ private ClientboundPlayerInfoUpdatePacket createSpectatorFilteredPacket(ClientboundPlayerInfoUpdatePacket originalPacket) {
+ if (org.bxteam.divinemc.config.DivineConfig.NetworkCategory.sendSpectatorChangePacket) {
+ return null;
+ }
+
+ ClientboundPlayerInfoUpdatePacket.Entry entry = originalPacket.entries().getFirst();
+ if (entry.gameMode() != net.minecraft.world.level.GameType.SPECTATOR) {
+ return null;
+ }
+
+ ClientboundPlayerInfoUpdatePacket.Entry filteredEntry = new ClientboundPlayerInfoUpdatePacket.Entry(
+ entry.profileId(),
+ entry.profile(),
+ entry.listed(),
+ entry.latency(),
+ net.minecraft.world.level.GameType.SURVIVAL,
+ entry.displayName(),
+ entry.showHat(),
+ entry.listOrder(),
+ entry.chatSession());
+
+ return new ClientboundPlayerInfoUpdatePacket(originalPacket.actions(), List.of(filteredEntry));
+ }
+
+ private void sendPlayerInfoPacket(ServerPlayer receiver, ServerPlayer joiningPlayer,
+ ClientboundPlayerInfoUpdatePacket originalPacket,
+ @Nullable ClientboundPlayerInfoUpdatePacket filteredPacket) {
+ if (org.bxteam.divinemc.config.DivineConfig.NetworkCategory.sendSpectatorChangePacket ||
+ receiver == joiningPlayer ||
+ filteredPacket == null) {
+ receiver.connection.send(originalPacket);
+ } else {
+ receiver.connection.send(filteredPacket);
+ }
+ }
+
+ private ClientboundPlayerInfoUpdatePacket createFilteredPlayerListPacket(List<ServerPlayer> onlinePlayers, ServerPlayer joiningPlayer) {
+ ClientboundPlayerInfoUpdatePacket updatePacket = ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(onlinePlayers, joiningPlayer);
+
+ if (org.bxteam.divinemc.config.DivineConfig.NetworkCategory.sendSpectatorChangePacket) {
+ return updatePacket;
+ }
+
+ List<ClientboundPlayerInfoUpdatePacket.Entry> newEntries = new java.util.ArrayList<>();
+ for (ClientboundPlayerInfoUpdatePacket.Entry entry : updatePacket.entries()) {
+ ClientboundPlayerInfoUpdatePacket.Entry newEntry = new ClientboundPlayerInfoUpdatePacket.Entry(
+ entry.profileId(),
+ entry.profile(),
+ entry.listed(),
+ entry.latency(),
+ entry.gameMode() == net.minecraft.world.level.GameType.SPECTATOR ?
+ net.minecraft.world.level.GameType.SURVIVAL : entry.gameMode(),
+ entry.displayName(),
+ entry.showHat(),
+ entry.listOrder(),
+ entry.chatSession());
+ newEntries.add(newEntry);
+ }
+
+ return new ClientboundPlayerInfoUpdatePacket(updatePacket.actions(), newEntries);
+ }
+ // DivineMC end - Do not send spectator change packet
}

View File

@@ -0,0 +1,33 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sun, 20 Jul 2025 16:09:38 +0300
Subject: [PATCH] Paper PR: Fire ServerListPingEvent for secondary motd send
Original license: GPLv3
Original project: https://github.com/PaperMC/Paper
Paper pull request: https://github.com/PaperMC/Paper/pull/8074
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index 1a280122b4ff661c2d2fad359ddfb671a1d8f324..c8ce9e591475df1485227d34316fc02c3ec33f3e 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -208,10 +208,15 @@ public abstract class PlayerList {
mutableComponent.withStyle(ChatFormatting.YELLOW);
Component joinMessage = mutableComponent; // Paper - Adventure
serverGamePacketListenerImpl.teleport(player.getX(), player.getY(), player.getZ(), player.getYRot(), player.getXRot());
- ServerStatus status = this.server.getStatus();
- if (status != null && !cookie.transferred()) {
- player.sendServerStatus(status);
- }
+ // DivineMC start - Paper PR: Fire ServerListPingEvent for secondary motd send
+ if (!cookie.transferred()) {
+ io.papermc.paper.util.MCUtil.scheduleAsyncTask(() -> {
+ if (player.hasDisconnected()) return;
+ net.minecraft.network.protocol.status.ServerStatus status = com.destroystokyo.paper.network.StandardPaperServerListPingEventImpl.getEventResponse(this.server, player.connection.connection);
+ if (status != null) player.sendServerStatus(status);
+ });
+ }
+ // DivineMC end - Paper PR: Fire ServerListPingEvent for secondary motd send
// player.connection.send(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(this.players)); // CraftBukkit - replaced with loop below
this.players.add(player);

View File

@@ -0,0 +1,45 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Mon, 4 Aug 2025 02:38:45 +0300
Subject: [PATCH] Configurable player spawn tracking range
diff --git a/ca/spottedleaf/moonrise/patches/chunk_tick_iteration/ChunkTickConstants.java b/ca/spottedleaf/moonrise/patches/chunk_tick_iteration/ChunkTickConstants.java
index 6d1fe8028739145b11fce98ad62b2f8044299548..9f086ded18d1fc8850877c6be113d88074427526 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_tick_iteration/ChunkTickConstants.java
+++ b/ca/spottedleaf/moonrise/patches/chunk_tick_iteration/ChunkTickConstants.java
@@ -2,7 +2,7 @@ package ca.spottedleaf.moonrise.patches.chunk_tick_iteration;
public final class ChunkTickConstants {
- public static final int PLAYER_SPAWN_TRACK_RANGE = 8;
+ public static final int PLAYER_SPAWN_TRACK_RANGE = (int) Math.round(org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.playerNearChunkDetectionRange / 16.0); // DivineMC - Configurable player spawn tracking range
// the smallest distance on x/z is at 45 degrees, we need to subtract 0.5 since this is calculated from chunk center and not chunk perimeter
// note: vanilla does not subtract 0.5 but the result is (luckily!) the same
public static final int NARROW_SPAWN_TRACK_RANGE = (int)Math.floor(((double)PLAYER_SPAWN_TRACK_RANGE / Math.sqrt(2.0)) - 0.5);
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
index 5b16dad04bf4540df750ce06c18af4b1d80d8648..5fb9a4cebf7407b8166ea5716c48a68e658d68d3 100644
--- a/net/minecraft/server/level/ChunkMap.java
+++ b/net/minecraft/server/level/ChunkMap.java
@@ -830,10 +830,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
final ServerPlayer[] raw = players.getRawDataUnchecked();
final int len = players.size();
- Objects.checkFromIndexSize(0, len, raw.length);
- for (int i = 0; i < len; ++i) {
+ for (int i = 0; i < raw.length; ++i) { // DivineMC - Configurable player spawn tracking range
final ServerPlayer player = raw[i];
- if (this.playerIsCloseEnoughForSpawning(player, chunkPos, 16384.0D)) { // Spigot
+ if (player == null) continue; // DivineMC - Configurable player spawn tracking range
+ if (this.playerIsCloseEnoughForSpawning(player, chunkPos, (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.playerNearChunkDetectionRange^2))) { // Spigot // DivineMC - Configurable player spawn tracking range
if (ret == null) {
ret = new ArrayList<>(len - i);
ret.add(player);
@@ -1257,6 +1257,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
for (int i = 0; i < playersLength; ++i) { // DivineMC - Multithreaded tracker
final ServerPlayer player = playersRaw[i];
+ if (player == null) continue; // DivineMC - Configurable player spawn tracking range
this.updatePlayer(player);
}

View File

@@ -0,0 +1,90 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Thu, 24 Jul 2025 14:07:47 +0300
Subject: [PATCH] Optimize collections
diff --git a/net/minecraft/core/NonNullList.java b/net/minecraft/core/NonNullList.java
index feefe51469843707afb2ecd4aaa3c9da2e089eb5..c7e418e5f7ca7f177df4e9432a0963f89b972a61 100644
--- a/net/minecraft/core/NonNullList.java
+++ b/net/minecraft/core/NonNullList.java
@@ -14,23 +14,23 @@ public class NonNullList<E> extends AbstractList<E> {
private final E defaultValue;
public static <E> NonNullList<E> create() {
- return new NonNullList<>(Lists.newArrayList(), null);
+ return new NonNullList<>(new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(), null); // DivineMC - Optimize collections
}
public static <E> NonNullList<E> createWithCapacity(int initialCapacity) {
- return new NonNullList<>(Lists.newArrayListWithCapacity(initialCapacity), null);
+ return new NonNullList<>(new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(initialCapacity), null); // DivineMC - Optimize collections
}
public static <E> NonNullList<E> withSize(int size, E defaultValue) {
Objects.requireNonNull(defaultValue);
Object[] objects = new Object[size];
Arrays.fill(objects, defaultValue);
- return new NonNullList<>(Arrays.asList((E[])objects), defaultValue);
+ return new NonNullList<>(new it.unimi.dsi.fastutil.objects.ObjectArrayList<>((E[])objects), defaultValue); // DivineMC - Optimize collections
}
@SafeVarargs
public static <E> NonNullList<E> of(E defaultValue, E... elements) {
- return new NonNullList<>(Arrays.asList(elements), defaultValue);
+ return new NonNullList<>(new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(elements), defaultValue); // DivineMC - Optimize collections
}
protected NonNullList(List<E> list, @Nullable E defaultValue) {
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
index 5fb9a4cebf7407b8166ea5716c48a68e658d68d3..304ff53e2c21c6153ff8f04436eae66b0406d32a 100644
--- a/net/minecraft/server/level/ChunkMap.java
+++ b/net/minecraft/server/level/ChunkMap.java
@@ -132,8 +132,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
public final ChunkMap.DistanceManager distanceManager;
private final String storageName;
private final PlayerMap playerMap = new PlayerMap();
- public final Int2ObjectMap<ChunkMap.TrackedEntity> entityMap = new Int2ObjectOpenHashMap<>();
- private final Long2ByteMap chunkTypeCache = new Long2ByteOpenHashMap();
+ // DivineMC start - Optimize collections
+ public final Int2ObjectMap<ChunkMap.TrackedEntity> entityMap = new org.bxteam.divinemc.util.map.Int2ObjectConcurrentHashMap<>();
+ private final Long2ByteMap chunkTypeCache = it.unimi.dsi.fastutil.longs.Long2ByteMaps.synchronize(new Long2ByteOpenHashMap());
+ // DivineMC end - Optimize collections
// Paper - rewrite chunk system
public int serverViewDistance;
public final WorldGenContext worldGenContext; // Paper - public
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index b94b946986258fed3c6d68d9972a657e176d08a4..f3110b736764a36abe5778c8a6bbf50f7349016c 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -258,7 +258,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
@Override
public final <T extends Entity> List<T> getEntitiesOfClass(final Class<T> entityClass, final AABB boundingBox, final Predicate<? super T> predicate) {
- final List<T> ret = new java.util.ArrayList<>();
+ final List<T> ret = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(); // DivineMC - Optimize collections
((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel)this).moonrise$getEntityLookup().getEntities(entityClass, null, boundingBox, ret, predicate);
@@ -267,7 +267,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
@Override
public final List<Entity> moonrise$getHardCollidingEntities(final Entity entity, final AABB box, final Predicate<? super Entity> predicate) {
- final List<Entity> ret = new java.util.ArrayList<>();
+ final List<Entity> ret = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(); // DivineMC - Optimize collections
((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel)this).moonrise$getEntityLookup().getHardCollidingEntities(entity, box, ret, predicate);
diff --git a/net/minecraft/world/level/chunk/ProtoChunk.java b/net/minecraft/world/level/chunk/ProtoChunk.java
index 4846ab41592014e45cd6e4f5ff12dbd6eb17e8cc..4155d63aa304d9624613eb81ba9d12457a3385ed 100644
--- a/net/minecraft/world/level/chunk/ProtoChunk.java
+++ b/net/minecraft/world/level/chunk/ProtoChunk.java
@@ -45,7 +45,7 @@ public class ProtoChunk extends ChunkAccess {
@Nullable
private volatile LevelLightEngine lightEngine;
private volatile ChunkStatus status = ChunkStatus.EMPTY;
- private final List<CompoundTag> entities = Lists.newArrayList();
+ private final List<CompoundTag> entities = Collections.synchronizedList(Lists.newArrayList()); // DivineMC - Optimize collections
@Nullable
private CarvingMask carvingMask;
@Nullable

View File

@@ -0,0 +1,221 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Thu, 24 Jul 2025 14:07:47 +0300
Subject: [PATCH] Optimize level ticking
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index a4a2231f5850269a6003afca8db78fa486cf3a71..f3eafbdc06e32788c5ae08279b45feea3b100555 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -929,9 +929,10 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
// Paper start - optimise random ticking
private final ca.spottedleaf.moonrise.common.util.SimpleThreadUnsafeRandom simpleRandom = new ca.spottedleaf.moonrise.common.util.SimpleThreadUnsafeRandom(net.minecraft.world.level.levelgen.RandomSupport.generateUniqueSeed());
+ // DivineMC start - Optimize level ticking
private void optimiseRandomTick(final LevelChunk chunk, final int tickSpeed) {
final LevelChunkSection[] sections = chunk.getSections();
- final int minSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection((ServerLevel)(Object)this);
+ final int minSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection(this); // DivineMC - Optimize level ticking
final ca.spottedleaf.moonrise.common.util.SimpleThreadUnsafeRandom simpleRandom = this.simpleRandom;
final boolean doubleTickFluids = !ca.spottedleaf.moonrise.common.PlatformHooks.get().configFixMC224294();
@@ -940,42 +941,38 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
final int offsetZ = cpos.z << 4;
for (int sectionIndex = 0, sectionsLen = sections.length; sectionIndex < sectionsLen; sectionIndex++) {
- final int offsetY = (sectionIndex + minSection) << 4;
final LevelChunkSection section = sections[sectionIndex];
+ if (!section.isRandomlyTickingBlocks()) continue;
+ final int offsetY = (sectionIndex + minSection) << 4;
final net.minecraft.world.level.chunk.PalettedContainer<net.minecraft.world.level.block.state.BlockState> states = section.states;
- if (!section.isRandomlyTickingBlocks()) {
- continue;
- }
- final ca.spottedleaf.moonrise.common.list.ShortList tickList = ((ca.spottedleaf.moonrise.patches.block_counting.BlockCountingChunkSection)section).moonrise$getTickingBlockList();
+ final ca.spottedleaf.moonrise.common.list.ShortList tickList = section.moonrise$getTickingBlockList();
for (int i = 0; i < tickSpeed; ++i) {
- final int tickingBlocks = tickList.size();
final int index = simpleRandom.nextInt() & ((16 * 16 * 16) - 1);
- if (index >= tickingBlocks) {
+ if (index >= tickList.size()) {
// most of the time we fall here
continue;
}
- final int location = (int)tickList.getRaw(index) & 0xFFFF;
+ final int location = tickList.getRaw(index);
final BlockState state = states.get(location);
// do not use a mutable pos, as some random tick implementations store the input without calling immutable()!
- final BlockPos pos = new BlockPos((location & 15) | offsetX, ((location >>> (4 + 4)) & 15) | offsetY, ((location >>> 4) & 15) | offsetZ);
+ final BlockPos pos = new BlockPos((location & 15) | offsetX, (location >>> (4 + 4)) | offsetY, ((location >>> 4) & 15) | offsetZ);
- state.randomTick((ServerLevel)(Object)this, pos, simpleRandom);
+ state.randomTick(this, pos, simpleRandom);
if (doubleTickFluids) {
final FluidState fluidState = state.getFluidState();
if (fluidState.isRandomlyTicking()) {
- fluidState.randomTick((ServerLevel)(Object)this, pos, simpleRandom);
+ fluidState.randomTick(this, pos, simpleRandom);
}
}
}
}
-
- return;
}
+ // DivineMC end - Optimize level ticking
// Paper end - optimise random ticking
public void tickChunk(LevelChunk chunk, int randomTickSpeed) {
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
index 3a9843c30f685d2e1f0cd54ace5dddfa9e2314fa..ae58d4978d2f8c0f61b5c743282f7241bd29b747 100644
--- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -84,7 +84,7 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
return "<null>";
}
};
- private final Map<BlockPos, LevelChunk.RebindableTickingBlockEntityWrapper> tickersInLevel = Maps.newHashMap();
+ private final Map<BlockPos, LevelChunk.RebindableTickingBlockEntityWrapper> tickersInLevel = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(); // DivineMC - Optimize level ticking
public boolean loaded;
public final ServerLevel level; // CraftBukkit - type
@Nullable
diff --git a/net/minecraft/world/ticks/LevelChunkTicks.java b/net/minecraft/world/ticks/LevelChunkTicks.java
index 66d0a6390febe929ef774b0a7813329015bc8cc2..c17549c4f8a877852c4b86453b1db7b17aab4665 100644
--- a/net/minecraft/world/ticks/LevelChunkTicks.java
+++ b/net/minecraft/world/ticks/LevelChunkTicks.java
@@ -14,10 +14,10 @@ import javax.annotation.Nullable;
import net.minecraft.core.BlockPos;
public class LevelChunkTicks<T> implements SerializableTickContainer<T>, TickContainerAccess<T>, ca.spottedleaf.moonrise.patches.chunk_system.ticks.ChunkSystemLevelChunkTicks { // Paper - rewrite chunk system
- private final Queue<ScheduledTick<T>> tickQueue = new PriorityQueue<>(ScheduledTick.DRAIN_ORDER);
+ private final Queue<ScheduledTick<T>> tickQueue = new java.util.concurrent.PriorityBlockingQueue<>(11, ScheduledTick.DRAIN_ORDER); // DivineMC - Optimize level ticking
@Nullable
private List<SavedTick<T>> pendingTicks;
- private final Set<ScheduledTick<?>> ticksPerPosition = new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH);
+ private final Set<ScheduledTick<?>> ticksPerPosition = it.unimi.dsi.fastutil.objects.ObjectSets.synchronize(new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH)); // DivineMC - Optimize level ticking
@Nullable
private BiConsumer<LevelChunkTicks<T>, ScheduledTick<T>> onTickAdded;
@@ -67,10 +67,18 @@ public class LevelChunkTicks<T> implements SerializableTickContainer<T>, TickCon
@Nullable
public ScheduledTick<T> poll() {
- ScheduledTick<T> scheduledTick = this.tickQueue.poll();
- if (scheduledTick != null) {
- this.ticksPerPosition.remove(scheduledTick); this.dirty = true; // Paper - rewrite chunk system
+ // DivineMC start - Optimize collections
+ ScheduledTick<T> scheduledTick = null;
+ try {
+ scheduledTick = this.tickQueue.poll();
+ if (scheduledTick != null) {
+ this.ticksPerPosition.remove(scheduledTick); this.dirty = true; // Paper - rewrite chunk system
+ }
+ } catch (Exception e) {
+ net.minecraft.server.MinecraftServer.LOGGER.error("Encountered caught exception when polling chunk ticks, blocking and returning null.", e);
+ return null;
}
+ // DivineMC end - Optimize collections
return scheduledTick;
}
@@ -83,6 +91,7 @@ public class LevelChunkTicks<T> implements SerializableTickContainer<T>, TickCon
}
private void scheduleUnchecked(ScheduledTick<T> tick) {
+ if (tick == null) return; // DivineMC - Optimize level ticking
this.tickQueue.add(tick);
if (this.onTickAdded != null) {
this.onTickAdded.accept(this, tick);
@@ -124,6 +133,7 @@ public class LevelChunkTicks<T> implements SerializableTickContainer<T>, TickCon
}
for (ScheduledTick<T> scheduledTick : this.tickQueue) {
+ if (scheduledTick == null) continue; // DivineMC - Optimize level ticking
list.add(scheduledTick.toSavedTick(gametime));
}
diff --git a/net/minecraft/world/ticks/LevelTicks.java b/net/minecraft/world/ticks/LevelTicks.java
index c7f9485191dc797de78e6524c5c2c737581ed838..14f2d0088cd9e6d4a2eb084439bab18bd365c41f 100644
--- a/net/minecraft/world/ticks/LevelTicks.java
+++ b/net/minecraft/world/ticks/LevelTicks.java
@@ -30,17 +30,20 @@ public class LevelTicks<T> implements LevelTickAccess<T> {
private static final Comparator<LevelChunkTicks<?>> CONTAINER_DRAIN_ORDER = (levelChunkTicks, levelChunkTicks1) -> ScheduledTick.INTRA_TICK_DRAIN_ORDER
.compare(levelChunkTicks.peek(), levelChunkTicks1.peek());
private final LongPredicate tickCheck;
- private final Long2ObjectMap<LevelChunkTicks<T>> allContainers = new Long2ObjectOpenHashMap<>();
- private final Long2LongMap nextTickForContainer = Util.make(new Long2LongOpenHashMap(), map -> map.defaultReturnValue(Long.MAX_VALUE));
- private final Queue<LevelChunkTicks<T>> containersToTick = new PriorityQueue<>(CONTAINER_DRAIN_ORDER);
- private final Queue<ScheduledTick<T>> toRunThisTick = new ArrayDeque<>();
+ // DivineMC start - Optimize collections
+ private final Long2ObjectMap<LevelChunkTicks<T>> allContainers = it.unimi.dsi.fastutil.longs.Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
+ private final java.util.Map<Long, Long> nextTickForContainer = new java.util.concurrent.ConcurrentHashMap<>();
+ private final Queue<LevelChunkTicks<T>> containersToTick = new java.util.concurrent.PriorityBlockingQueue<>(11, CONTAINER_DRAIN_ORDER);
+ private final Queue<ScheduledTick<T>> toRunThisTick = new java.util.concurrent.ConcurrentLinkedQueue<>();
+ // DivineMC end - Optimize collections
private final List<ScheduledTick<T>> alreadyRunThisTick = new ArrayList<>();
- private final Set<ScheduledTick<?>> toRunThisTickSet = new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH);
+ private final Set<ScheduledTick<?>> toRunThisTickSet = com.google.common.collect.Sets.newConcurrentHashSet(); // DivineMC - Optimize level ticking
private final BiConsumer<LevelChunkTicks<T>, ScheduledTick<T>> chunkScheduleUpdater = (levelChunkTicks, scheduledTick) -> {
if (scheduledTick.equals(levelChunkTicks.peek())) {
this.updateContainerScheduling(scheduledTick);
}
};
+ private final java.util.concurrent.atomic.AtomicInteger toRunThisTickCount = new java.util.concurrent.atomic.AtomicInteger(0); // DivineMC - Optimize level ticking
public LevelTicks(LongPredicate tickCheck) {
this.tickCheck = tickCheck;
@@ -90,12 +93,14 @@ public class LevelTicks<T> implements LevelTickAccess<T> {
}
private void sortContainersToTick(long gameTime) {
- ObjectIterator<Entry> objectIterator = Long2LongMaps.fastIterator(this.nextTickForContainer);
+ java.util.Iterator<java.util.Map.Entry<Long, Long>> objectIterator = this.nextTickForContainer.entrySet().iterator(); // DivineMC - Optimize level ticking
while (objectIterator.hasNext()) {
- Entry entry = objectIterator.next();
- long longKey = entry.getLongKey();
- long longValue = entry.getLongValue();
+ // DivineMC start - Optimize collections
+ java.util.Map.Entry<Long, Long> entry = objectIterator.next();
+ long longKey = entry.getKey();
+ long longValue = entry.getValue();
+ // DivineMC end - Optimize collections
if (longValue <= gameTime) {
LevelChunkTicks<T> levelChunkTicks = this.allContainers.get(longKey);
if (levelChunkTicks == null) {
@@ -162,16 +167,19 @@ public class LevelTicks<T> implements LevelTickAccess<T> {
}
private void scheduleForThisTick(ScheduledTick<T> tick) {
+ if (tick == null) return; // DivineMC - Optimize level ticking
this.toRunThisTick.add(tick);
+ this.toRunThisTickCount.incrementAndGet(); // DivineMC - Optimize level ticking
}
private boolean canScheduleMoreTicks(int maxAllowedTicks) {
- return this.toRunThisTick.size() < maxAllowedTicks;
+ return this.toRunThisTickCount.get() < maxAllowedTicks; // DivineMC - Optimize level ticking
}
private void runCollectedTicks(BiConsumer<BlockPos, T> ticker) {
while (!this.toRunThisTick.isEmpty()) {
ScheduledTick<T> scheduledTick = this.toRunThisTick.poll();
+ this.toRunThisTickCount.decrementAndGet(); // DivineMC - Optimize level ticking
if (!this.toRunThisTickSet.isEmpty()) {
this.toRunThisTickSet.remove(scheduledTick);
}
@@ -182,7 +190,7 @@ public class LevelTicks<T> implements LevelTickAccess<T> {
}
private void cleanupAfterTick() {
- this.toRunThisTick.clear();
+ this.toRunThisTickCount.set(0); // DivineMC - Optimize level ticking
this.containersToTick.clear();
this.alreadyRunThisTick.clear();
this.toRunThisTickSet.clear();

View File

@@ -0,0 +1,50 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Wed, 27 Aug 2025 22:48:22 +0300
Subject: [PATCH] lithium: combined_heightmap_update
This patch is based on the following mixins:
* "net/caffeinemc/mods/lithium/mixin/world/combined_heightmap_update/LevelChunkMixin"
By: 2No2Name <2No2Name@web.de>
As part of: Lithium (https://github.com/CaffeineMC/lithium)
Licensed under: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html)
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
index ae58d4978d2f8c0f61b5c743282f7241bd29b747..ef98a02b982fd9e0992e0a40879d8cf498417cbf 100644
--- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -388,10 +388,13 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
return null;
} else {
Block block = state.getBlock();
- this.heightmaps.get(Heightmap.Types.MOTION_BLOCKING).update(i, y, i2, state);
- this.heightmaps.get(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES).update(i, y, i2, state);
- this.heightmaps.get(Heightmap.Types.OCEAN_FLOOR).update(i, y, i2, state);
- this.heightmaps.get(Heightmap.Types.WORLD_SURFACE).update(i, y, i2, state);
+ // DivineMC start - lithium: combined_heightmap_update
+ Heightmap heightmap0 = this.heightmaps.get(Heightmap.Types.MOTION_BLOCKING);
+ Heightmap heightmap1 = this.heightmaps.get(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES);
+ Heightmap heightmap2 = this.heightmaps.get(Heightmap.Types.OCEAN_FLOOR);
+ Heightmap heightmap3 = this.heightmaps.get(Heightmap.Types.WORLD_SURFACE);
+ net.caffeinemc.mods.lithium.common.world.chunk.heightmap.CombinedHeightmapUpdate.updateHeightmaps(heightmap0, heightmap1, heightmap2, heightmap3, this, i, y, i2, state);
+ // DivineMC end - lithium: combined_heightmap_update
boolean hasOnlyAir1 = section.hasOnlyAir();
if (hasOnlyAir != hasOnlyAir1) {
this.level.getChunkSource().getLightEngine().updateSectionStatus(pos, hasOnlyAir1);
diff --git a/net/minecraft/world/level/levelgen/Heightmap.java b/net/minecraft/world/level/levelgen/Heightmap.java
index b53853c2b8ec79a4dcdf66f7995540e59ddee356..e30f24788adc8160739a714e3519acb0ae7ee3b5 100644
--- a/net/minecraft/world/level/levelgen/Heightmap.java
+++ b/net/minecraft/world/level/levelgen/Heightmap.java
@@ -123,6 +123,12 @@ public class Heightmap {
this.data.set(getIndex(x, z), value - this.chunk.getMinY());
}
+ // DivineMC start - lithium: combined_heightmap_update
+ public final Predicate<BlockState> isOpaque() {
+ return this.isOpaque;
+ }
+ // DivineMC end - lithium: combined_heightmap_update
+
public void setRawData(ChunkAccess chunk, Heightmap.Types type, long[] data) {
long[] raw = this.data.getRaw();
if (raw.length == data.length) {

View File

@@ -0,0 +1,47 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Mon, 27 Jan 2025 18:42:29 +0300
Subject: [PATCH] Delete timings
diff --git a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
index e989053b703fddfbffc3e4ed9381594aaaa0df41..d7398b1ecf2660c29fb7d106b48fe02d3736603e 100644
--- a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
+++ b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
@@ -1,6 +1,5 @@
package io.papermc.paper.plugin.manager;
-import co.aikar.timings.TimedEventExecutor;
import com.destroystokyo.paper.event.server.ServerExceptionEvent;
import com.destroystokyo.paper.exception.ServerEventException;
import com.google.common.collect.Sets;
@@ -96,7 +95,6 @@ class PaperEventManager {
throw new IllegalPluginAccessException("Plugin attempted to register " + event + " while not enabled");
}
- executor = new TimedEventExecutor(executor, plugin, null, event);
this.getEventListeners(event).register(new RegisteredListener(listener, executor, priority, plugin, ignoreCancelled));
}
@@ -183,7 +181,7 @@ class PaperEventManager {
}
}
- EventExecutor executor = new TimedEventExecutor(EventExecutor.create(method, eventClass), plugin, method, eventClass);
+ EventExecutor executor = EventExecutor.create(method, eventClass); // DivineMC - Delete timings
eventSet.add(new RegisteredListener(listener, executor, eh.priority(), plugin, eh.ignoreCancelled()));
}
return ret;
diff --git a/src/main/java/io/papermc/paper/plugin/manager/PaperPluginManagerImpl.java b/src/main/java/io/papermc/paper/plugin/manager/PaperPluginManagerImpl.java
index 097500a59336db1bbfffcd1aa4cff7a8586e46ec..69341cb3b11409e41b9ff756b11d9bd1b9e6da10 100644
--- a/src/main/java/io/papermc/paper/plugin/manager/PaperPluginManagerImpl.java
+++ b/src/main/java/io/papermc/paper/plugin/manager/PaperPluginManagerImpl.java
@@ -232,7 +232,7 @@ public class PaperPluginManagerImpl implements PluginManager, DependencyContext
@Override
public boolean useTimings() {
- return co.aikar.timings.Timings.isTimingsEnabled();
+ return false; // DivineMC - Delete timings
}
@Override

View File

@@ -0,0 +1,22 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Wed, 2 Jul 2025 19:14:51 +0300
Subject: [PATCH] Delete ReloadCommand
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 916cca90459831c12b988ca10a0f53cb1befc058..ac2e91d00fb0320f0c9a049ab980f6e2f5124dd9 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -970,11 +970,6 @@ public final class CraftServer implements Server {
@Override
public void reload() {
- // Paper start - lifecycle events
- if (io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner.INSTANCE.blocksPluginReloading()) {
- throw new IllegalStateException(org.bukkit.command.defaults.ReloadCommand.RELOADING_DISABLED_MESSAGE);
- }
- // Paper end - lifecycle events
org.spigotmc.WatchdogThread.hasStarted = false; // Paper - Disable watchdog early timeout on reload
this.reloadCount++;
this.configuration = YamlConfiguration.loadConfiguration(this.getConfigFile());

View File

@@ -0,0 +1,29 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sun, 23 Mar 2025 16:39:45 +0300
Subject: [PATCH] Paper PR: Add FillBottleEvents for player and dispenser
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
index 60350a65c8073bef3acb8432e41334430eab01cd..782cfca296cd0f5cced549d29ce6eb94ec74319c 100644
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
@@ -2133,4 +2133,18 @@ public class CraftEventFactory {
event.callEvent();
return event.isAllowed();
}
+
+ // DivineMC start - Paper PR: Add FillBottleEvents for player and dispenser
+ public static io.papermc.paper.event.player.PlayerFillBottleEvent callPlayerFillBottleEvent(net.minecraft.world.entity.player.Player player, InteractionHand hand, ItemStack glassBottle, ItemStack resultItem) {
+ final io.papermc.paper.event.player.PlayerFillBottleEvent event = new io.papermc.paper.event.player.PlayerFillBottleEvent(((org.bukkit.entity.Player) player.getBukkitEntity()), org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand), CraftItemStack.asBukkitCopy(glassBottle), CraftItemStack.asCraftMirror(resultItem));
+ event.callEvent();
+ return event;
+ }
+
+ public static io.papermc.paper.event.block.BlockFillBottleEvent callBlockFillBottleEvent(LevelAccessor level, BlockPos blockPos, ItemStack glassBottle, ItemStack resultItem) {
+ final io.papermc.paper.event.block.BlockFillBottleEvent event = new io.papermc.paper.event.block.BlockFillBottleEvent(CraftBlock.at(level, blockPos), CraftItemStack.asBukkitCopy(glassBottle), CraftItemStack.asCraftMirror(resultItem));
+ event.callEvent();
+ return event;
+ }
+ // DivineMC end - Paper PR: Add FillBottleEvents for player and dispenser
}

View File

@@ -0,0 +1,31 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sun, 23 Mar 2025 16:53:16 +0300
Subject: [PATCH] Paper PR: Player standing on position API
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
index 11c1a0abad4efb65547c5b42869ec06bb74b4d59..939f0a74f0956e73488dea68c3e0b5846fcac68b 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
@@ -1356,6 +1356,20 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
return this.entity.get(io.papermc.paper.datacomponent.PaperDataComponentType.bukkitToMinecraft(type)) != null;
}
+ // Paper start - Player standing on position API
+ @Override
+ public org.bukkit.block.Block getMovementAffectingBlock() {
+ return CraftBlock.at(this.getHandle().level(), this.getHandle().getBlockPosBelowThatAffectsMyMovement());
+ }
+
+ @Override
+ public org.bukkit.block.Block getSupportingBlock() {
+ return this.getHandle().mainSupportingBlockPos
+ .map((pos) -> CraftBlock.at(this.getHandle().level(), pos))
+ .orElse(null);
+ }
+ // Paper end - Player standing on position API
+
// Purpur start - Ridables
@Override
public org.bukkit.entity.Player getRider() {

View File

@@ -0,0 +1,45 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Thu, 27 Mar 2025 00:04:19 +0300
Subject: [PATCH] Paper PR: Throttle failed spawn attempts
Original license: GPLv3
Original project: https://github.com/PaperMC/Paper
Paper pull request: https://github.com/PaperMC/Paper/pull/11099
Example config in paper-world-defaults.yml:
```
spawning-throttle:
failed-attempts-threshold: 1200
throttled-ticks-per-spawn:
ambient: 10 # default value in bukkit.yml tickers-per * 10
axolotls: 10
creature: 4000
monster: 10
underground_water_creature: 10
water_ambient: 10
water_creature: 10
```
diff --git a/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java b/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java
index 6877056ddfe58910c0fa859105f3c7223a1b00c9..e65e3068ca11836bde554a3be2540760aeb25aff 100644
--- a/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java
+++ b/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java
@@ -184,6 +184,17 @@ public class WorldConfiguration extends ConfigurationPart {
@MergeMap
public Reference2IntMap<MobCategory> ticksPerSpawn = Util.make(new Reference2IntOpenHashMap<>(NaturalSpawner.SPAWNING_CATEGORIES.length), map -> Arrays.stream(NaturalSpawner.SPAWNING_CATEGORIES).forEach(mobCategory -> map.put(mobCategory, -1)));
+ // Paper start - throttle failed spawn attempts
+ public SpawningThrottle spawningThrottle;
+
+ public class SpawningThrottle extends ConfigurationPart {
+ public IntOr.Disabled failedAttemptsThreshold = IntOr.Disabled.DISABLED;
+
+ @MergeMap
+ public Reference2IntMap<MobCategory> throttledTicksPerSpawn = Util.make(new Reference2IntOpenHashMap<>(NaturalSpawner.SPAWNING_CATEGORIES.length), map -> Arrays.stream(NaturalSpawner.SPAWNING_CATEGORIES).forEach(mobCategory -> map.put(mobCategory, -1)));
+ }
+ // Paper end - throttle failed spawn attempts
+
@ConfigSerializable
public record DespawnRangePair(@Required DespawnRange hard, @Required DespawnRange soft) {
public static DespawnRangePair createDefault() {

View File

@@ -0,0 +1,307 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sat, 26 Apr 2025 23:43:59 +0300
Subject: [PATCH] Optimize default values for configs
diff --git a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java
index 81553c3f08bfac855b914997fb6a4e7cf2241272..6c23b24477dd24ed43932d0c5ae5d97f80d968f6 100644
--- a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java
+++ b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java
@@ -332,8 +332,8 @@ public class GlobalConfiguration extends ConfigurationPart {
@Constraints.Min(4)
public int regionFileCacheSize = 256;
@Comment("See https://luckformula.emc.gs")
- public boolean useAlternativeLuckFormula = false;
- public boolean useDimensionTypeForCustomSpawners = false;
+ public boolean useAlternativeLuckFormula = true; // DivineMC - Optimize default values for configs
+ public boolean useDimensionTypeForCustomSpawners = true; // DivineMC - Optimize default values for configs
public boolean strictAdvancementDimensionCheck = false;
public IntOr.Default compressionLevel = IntOr.Default.USE_DEFAULT;
@Comment("Defines the leniency distance added on the server to the interaction range of a player when validating interact packets.")
diff --git a/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java b/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java
index e65e3068ca11836bde554a3be2540760aeb25aff..3e4381a8a76385ba9dc9bef0d8d16fce36f3647b 100644
--- a/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java
+++ b/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java
@@ -149,8 +149,10 @@ public class WorldConfiguration extends ConfigurationPart {
public ArmorStands armorStands;
public class ArmorStands extends ConfigurationPart {
- public boolean doCollisionEntityLookups = true;
- public boolean tick = true;
+ // DivineMC start - Optimize default values for configs
+ public boolean doCollisionEntityLookups = false;
+ public boolean tick = false;
+ // DivineMC end - Optimize default values for configs
}
public Markers markers;
@@ -275,8 +277,38 @@ public class WorldConfiguration extends ConfigurationPart {
public AltItemDespawnRate altItemDespawnRate;
public class AltItemDespawnRate extends ConfigurationPart {
- public boolean enabled = false;
- public Reference2IntMap<Item> items = new Reference2IntOpenHashMap<>(Map.of(Items.COBBLESTONE, 300));
+ // DivineMC start - Optimize default values for configs
+ public boolean enabled = true;
+ public Reference2IntMap<Item> items = new Reference2IntOpenHashMap<>(Map.ofEntries(
+ Map.entry(Items.COBBLESTONE, 300),
+ Map.entry(Items.NETHERRACK, 300),
+ Map.entry(Items.SAND, 300),
+ Map.entry(Items.RED_SAND, 300),
+ Map.entry(Items.GRAVEL, 300),
+ Map.entry(Items.DIRT, 300),
+ Map.entry(Items.SHORT_GRASS, 300),
+ Map.entry(Items.PUMPKIN, 300),
+ Map.entry(Items.MELON_SLICE, 300),
+ Map.entry(Items.KELP, 300),
+ Map.entry(Items.BAMBOO, 300),
+ Map.entry(Items.SUGAR_CANE, 300),
+ Map.entry(Items.TWISTING_VINES, 300),
+ Map.entry(Items.WEEPING_VINES, 300),
+ Map.entry(Items.OAK_LEAVES, 300),
+ Map.entry(Items.SPRUCE_LEAVES, 300),
+ Map.entry(Items.BIRCH_LEAVES, 300),
+ Map.entry(Items.JUNGLE_LEAVES, 300),
+ Map.entry(Items.ACACIA_LEAVES, 300),
+ Map.entry(Items.DARK_OAK_LEAVES, 300),
+ Map.entry(Items.MANGROVE_LEAVES, 300),
+ Map.entry(Items.CHERRY_LEAVES, 300),
+ Map.entry(Items.CACTUS, 300),
+ Map.entry(Items.DIORITE, 300),
+ Map.entry(Items.GRANITE, 300),
+ Map.entry(Items.ANDESITE, 300),
+ Map.entry(Items.SCAFFOLDING, 600)
+ ));
+ // DivineMC end - Optimize default values for configs
}
}
@@ -421,7 +453,7 @@ public class WorldConfiguration extends ConfigurationPart {
public class Environment extends ConfigurationPart {
public boolean disableThunder = false;
public boolean disableIceAndSnow = false;
- public boolean optimizeExplosions = false;
+ public boolean optimizeExplosions = true; // DivineMC - Optimize default values for configs
public boolean disableExplosionKnockback = false;
public boolean generateFlatBedrock = false;
public FrostedIce frostedIce;
@@ -474,7 +506,7 @@ public class WorldConfiguration extends ConfigurationPart {
public Fixes fixes;
public class Fixes extends ConfigurationPart {
- public boolean fixItemsMergingThroughWalls = false;
+ public boolean fixItemsMergingThroughWalls = true; // DivineMC - Optimize default values for configs
public boolean disableUnloadedChunkEnderpearlExploit = false;
public boolean preventTntFromMovingInWater = false;
public boolean splitOverstackedLoot = true;
@@ -502,9 +534,9 @@ public class WorldConfiguration extends ConfigurationPart {
public class Collisions extends ConfigurationPart {
public boolean onlyPlayersCollide = false;
public boolean allowVehicleCollisions = true;
- public boolean fixClimbingBypassingCrammingRule = false;
+ public boolean fixClimbingBypassingCrammingRule = true; // DivineMC - Optimize default values for configs
@RequiresSpigotInitialization(MaxEntityCollisionsInitializer.class)
- public int maxEntityCollisions = 8;
+ public int maxEntityCollisions = 2; // DivineMC - Optimize default values for configs
public boolean allowPlayerCrammingDamage = false;
}
@@ -514,16 +546,33 @@ public class WorldConfiguration extends ConfigurationPart {
public AutosavePeriod autoSaveInterval = AutosavePeriod.def();
public int maxAutoSaveChunksPerTick = 24;
public int fixedChunkInhabitedTime = -1;
- public boolean preventMovingIntoUnloadedChunks = false;
+ public boolean preventMovingIntoUnloadedChunks = true; // DivineMC - Optimize default values for configs
public Duration delayChunkUnloadsBy = Duration.of("10s");
public Reference2IntMap<EntityType<?>> entityPerChunkSaveLimit = Util.make(new Reference2IntOpenHashMap<>(BuiltInRegistries.ENTITY_TYPE.size()), map -> {
map.defaultReturnValue(-1);
- map.put(EntityType.EXPERIENCE_ORB, -1);
- map.put(EntityType.SNOWBALL, -1);
- map.put(EntityType.ENDER_PEARL, -1);
- map.put(EntityType.ARROW, -1);
- map.put(EntityType.FIREBALL, -1);
- map.put(EntityType.SMALL_FIREBALL, -1);
+ // DivineMC start - Optimize default values for configs
+ map.put(EntityType.AREA_EFFECT_CLOUD, 8);
+ map.put(EntityType.ARROW, 16);
+ map.put(EntityType.BREEZE_WIND_CHARGE, 8);
+ map.put(EntityType.DRAGON_FIREBALL, 3);
+ map.put(EntityType.EGG, 8);
+ map.put(EntityType.ENDER_PEARL, 8);
+ map.put(EntityType.EXPERIENCE_BOTTLE, 3);
+ map.put(EntityType.EXPERIENCE_ORB, 16);
+ map.put(EntityType.EYE_OF_ENDER, 8);
+ map.put(EntityType.FIREBALL, 8);
+ map.put(EntityType.FIREWORK_ROCKET, 8);
+ map.put(EntityType.LLAMA_SPIT, 3);
+ map.put(EntityType.SPLASH_POTION, 8);
+ map.put(EntityType.LINGERING_POTION, 8);
+ map.put(EntityType.SHULKER_BULLET, 8);
+ map.put(EntityType.SMALL_FIREBALL, 8);
+ map.put(EntityType.SNOWBALL, 8);
+ map.put(EntityType.SPECTRAL_ARROW, 16);
+ map.put(EntityType.TRIDENT, 16);
+ map.put(EntityType.WIND_CHARGE, 8);
+ map.put(EntityType.WITHER_SKULL, 4);
+ // DivineMC end - Optimize default values for configs
});
public boolean flushRegionsOnSave = false;
@@ -543,9 +592,9 @@ public class WorldConfiguration extends ConfigurationPart {
public TickRates tickRates;
public class TickRates extends ConfigurationPart {
- public int grassSpread = 1;
+ public int grassSpread = 4;
public int containerUpdate = 1;
- public int mobSpawner = 1;
+ public int mobSpawner = 2;
public int wetFarmland = 1;
public int dryFarmland = 1;
public Table<EntityType<?>, String, Integer> sensor = Util.make(HashBasedTable.create(), table -> table.put(EntityType.VILLAGER, "secondarypoisensor", 40));
@@ -580,7 +629,7 @@ public class WorldConfiguration extends ConfigurationPart {
public class Misc extends ConfigurationPart {
public boolean updatePathfindingOnBlockUpdate = true;
public boolean showSignClickCommandFailureMsgsToPlayer = false;
- public RedstoneImplementation redstoneImplementation = RedstoneImplementation.VANILLA;
+ public RedstoneImplementation redstoneImplementation = RedstoneImplementation.ALTERNATE_CURRENT; // DivineMC - Optimize default values for configs
public AlternateCurrentUpdateOrder alternateCurrentUpdateOrder = AlternateCurrentUpdateOrder.HORIZONTAL_FIRST_OUTWARD;
public boolean disableEndCredits = false;
public DoubleOr.Default maxLeashDistance = DoubleOr.Default.USE_DEFAULT;
diff --git a/src/main/java/org/spigotmc/SpigotWorldConfig.java b/src/main/java/org/spigotmc/SpigotWorldConfig.java
index 43c6240ec2855c0f668ce04de29d22a223d2612f..74cae42b0c8971e0ad724ef28a7ddb42906e574d 100644
--- a/src/main/java/org/spigotmc/SpigotWorldConfig.java
+++ b/src/main/java/org/spigotmc/SpigotWorldConfig.java
@@ -135,13 +135,13 @@ public class SpigotWorldConfig {
public double itemMerge;
private void itemMerge() {
- this.itemMerge = this.getDouble("merge-radius.item", 0.5);
+ this.itemMerge = this.getDouble("merge-radius.item", 3.5); // DivineMC - Optimize default values for configs
this.log("Item Merge Radius: " + this.itemMerge);
}
public double expMerge;
private void expMerge() {
- this.expMerge = this.getDouble("merge-radius.exp", -1);
+ this.expMerge = this.getDouble("merge-radius.exp", 4.0); // DivineMC - Optimize default values for configs
this.log("Experience Merge Radius: " + this.expMerge);
}
@@ -174,7 +174,7 @@ public class SpigotWorldConfig {
public byte mobSpawnRange;
private void mobSpawnRange() {
- this.mobSpawnRange = (byte) getInt("mob-spawn-range", 8); // Paper - Vanilla
+ this.mobSpawnRange = (byte) getInt("mob-spawn-range", 3); // Paper - Vanilla // DivineMC - Optimize default values for configs
this.log("Mob Spawn Range: " + this.mobSpawnRange);
}
@@ -184,13 +184,15 @@ public class SpigotWorldConfig {
this.log("Item Despawn Rate: " + this.itemDespawnRate);
}
- public int animalActivationRange = 32;
- public int monsterActivationRange = 32;
- public int raiderActivationRange = 64;
- public int miscActivationRange = 16;
- public int flyingMonsterActivationRange = 32;
- public int waterActivationRange = 16;
- public int villagerActivationRange = 32;
+ // DivineMC start - Optimize default values for configs
+ public int animalActivationRange = 16;
+ public int monsterActivationRange = 24;
+ public int raiderActivationRange = 48;
+ public int miscActivationRange = 8;
+ public int flyingMonsterActivationRange = 48;
+ public int waterActivationRange = 8;
+ public int villagerActivationRange = 16;
+ // DivineMC end - Optimize default values for configs
public int wakeUpInactiveAnimals = 4;
public int wakeUpInactiveAnimalsEvery = 60 * 20;
public int wakeUpInactiveAnimalsFor = 5 * 20;
@@ -243,10 +245,10 @@ public class SpigotWorldConfig {
this.log("Entity Activation Range: An " + this.animalActivationRange + " / Mo " + this.monsterActivationRange + " / Ra " + this.raiderActivationRange + " / Mi " + this.miscActivationRange + " / Tiv " + this.tickInactiveVillagers + " / Isa " + this.ignoreSpectatorActivation);
}
- public int playerTrackingRange = 128;
- public int animalTrackingRange = 96;
- public int monsterTrackingRange = 96;
- public int miscTrackingRange = 96;
+ public int playerTrackingRange = 48;
+ public int animalTrackingRange = 48;
+ public int monsterTrackingRange = 48;
+ public int miscTrackingRange = 32;
public int displayTrackingRange = 128;
public int otherTrackingRange = 64;
private void trackingRange() {
@@ -269,7 +271,7 @@ public class SpigotWorldConfig {
if (SpigotConfig.version < 11) {
this.set("ticks-per.hopper-check", 1);
}
- this.hopperCheck = this.getInt("ticks-per.hopper-check", 1);
+ this.hopperCheck = this.getInt("ticks-per.hopper-check", 8); // DivineMC - Optimize default values for configs
this.hopperAmount = this.getInt("hopper-amount", 1);
this.hopperCanLoadChunks = this.getBoolean("hopper-can-load-chunks", false);
this.log("Hopper Transfer: " + this.hopperTransfer + " Hopper Check: " + this.hopperCheck + " Hopper Amount: " + this.hopperAmount + " Hopper Can Load Chunks: " + this.hopperCanLoadChunks);
diff --git a/src/main/resources/configurations/bukkit.yml b/src/main/resources/configurations/bukkit.yml
index 00a5b4dc0d7bc57ae3f9231f84a081617ec4b15b..874f862a67e6c2109bce03d21fb4441a53e71633 100644
--- a/src/main/resources/configurations/bukkit.yml
+++ b/src/main/resources/configurations/bukkit.yml
@@ -16,28 +16,28 @@ settings:
update-folder: update
plugin-profiling: false
connection-throttle: 4000
- query-plugins: true
+ query-plugins: false
deprecated-verbose: default
shutdown-message: Server closed
minimum-api: none
use-map-color-cache: true
spawn-limits:
- monsters: 70
- animals: 10
- water-animals: 5
- water-ambient: 20
- water-underground-creature: 5
- axolotls: 5
- ambient: 15
+ monsters: 20
+ animals: 8
+ water-animals: 3
+ water-ambient: 1
+ water-underground-creature: 3
+ axolotls: 3
+ ambient: 1
chunk-gc:
- period-in-ticks: 600
+ period-in-ticks: 400
ticks-per:
animal-spawns: 400
- monster-spawns: 1
- water-spawns: 1
- water-ambient-spawns: 1
- water-underground-creature-spawns: 1
- axolotl-spawns: 1
- ambient-spawns: 1
+ monster-spawns: 20
+ water-spawns: 400
+ water-ambient-spawns: 600
+ water-underground-creature-spawns: 600
+ axolotl-spawns: 400
+ ambient-spawns: 1800
autosave: 6000
aliases: now-in-commands.yml
diff --git a/src/main/resources/configurations/commands.yml b/src/main/resources/configurations/commands.yml
index ddf2cd4b802cf7d22342eed7f09c989761aed7a9..59f2bed6512f23ca7e28aebe0cef7d50bcfb8f8c 100644
--- a/src/main/resources/configurations/commands.yml
+++ b/src/main/resources/configurations/commands.yml
@@ -10,6 +10,4 @@
command-block-overrides: []
ignore-vanilla-permissions: false
-aliases:
- icanhasbukkit:
- - "version $1-"
+aliases: []

View File

@@ -0,0 +1,114 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: dan28000 <pirkldan28@gmail.com>
Date: Thu, 12 Jun 2025 10:08:25 +0200
Subject: [PATCH] Configurable files locations and plugin loading
diff --git a/src/main/java/com/destroystokyo/paper/console/PaperConsole.java b/src/main/java/com/destroystokyo/paper/console/PaperConsole.java
index 6567ff18cb1c21230565c2d92caf3a7f7f915c17..f5bc0eff886e9e3386467f40d4f329455d0b76e6 100644
--- a/src/main/java/com/destroystokyo/paper/console/PaperConsole.java
+++ b/src/main/java/com/destroystokyo/paper/console/PaperConsole.java
@@ -21,7 +21,7 @@ public final class PaperConsole extends SimpleTerminalConsole {
protected LineReader buildReader(LineReaderBuilder builder) {
builder
.appName("DivineMC") // DivineMC - Rebrand
- .variable(LineReader.HISTORY_FILE, java.nio.file.Paths.get(".console_history"))
+ .variable(LineReader.HISTORY_FILE, ((java.io.File) server.options.valueOf("console-history")).toPath()) // DivineMC - make configurable location of files
.completer(new ConsoleCommandCompleter(this.server))
.option(LineReader.Option.COMPLETE_IN_WORD, true);
if (io.papermc.paper.configuration.GlobalConfiguration.get().console.enableBrigadierHighlighting) {
diff --git a/src/main/java/io/papermc/paper/plugin/PluginInitializerManager.java b/src/main/java/io/papermc/paper/plugin/PluginInitializerManager.java
index 70413fddd23ca1165cb5090cce4fddcb1bbca93f..ae70b84e6473fa2ed94416bf4bef88492de3e5f8 100644
--- a/src/main/java/io/papermc/paper/plugin/PluginInitializerManager.java
+++ b/src/main/java/io/papermc/paper/plugin/PluginInitializerManager.java
@@ -112,6 +112,20 @@ public class PluginInitializerManager {
// Register the default plugin directory
io.papermc.paper.plugin.util.EntrypointUtil.registerProvidersFromSource(io.papermc.paper.plugin.provider.source.DirectoryProviderSource.INSTANCE, pluginSystem.pluginDirectoryPath());
+ // DivineMC start - Register the plugin directory from flags
+ @SuppressWarnings("unchecked")
+ java.util.List<Path> pluginList = ((java.util.List<File>) optionSet.valuesOf("add-plugin-dir")).stream()
+ .filter(java.util.Objects::nonNull)
+ .map(f -> f.listFiles(file -> file.getName().endsWith(".jar")))
+ .filter(java.util.Objects::nonNull)
+ .flatMap(java.util.Arrays::stream)
+ .filter(File::isFile)
+ .map(File::toPath)
+ .toList();
+
+ io.papermc.paper.plugin.util.EntrypointUtil.registerProvidersFromSource(io.papermc.paper.plugin.provider.source.PluginFlagProviderSource.INSTANCE, pluginList);
+ // DivineMC end - Register the plugin directory from flags
+
// Register plugins from the flag
@SuppressWarnings("unchecked")
java.util.List<Path> files = ((java.util.List<File>) optionSet.valuesOf("add-plugin")).stream().map(File::toPath).toList();
diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java
index 4cf0a09594e72193a452215c50ed1cce309d5cc7..efab2b8715988ad87f08e79d77fa46f1fc31aada 100644
--- a/src/main/java/org/bukkit/craftbukkit/Main.java
+++ b/src/main/java/org/bukkit/craftbukkit/Main.java
@@ -180,6 +180,52 @@ public class Main {
.describedAs("Yml file");
// DivineMC end - Configuration
+ // DivineMC start - Implement loading plugins from external folder
+ acceptsAll(asList("add-plugin-dir", "add-extra-plugin-dir"), "Specify paths to directories containing extra plugin jars to be loaded in addition to those in the plugins folder. This argument can be specified multiple times, once for each extra plugin directory path.")
+ .withRequiredArg()
+ .ofType(File.class)
+ .defaultsTo(new File("extra"))
+ .describedAs("Directory");
+ // DivineMC end - Implement loading plugins from external folder
+
+ // DivineMC start - make configurable location of files start
+ accepts("help-location", "Location of the help file")
+ .withRequiredArg()
+ .ofType(File.class)
+ .defaultsTo(new File("help.yml"))
+ .describedAs("Help file location");
+
+ accepts("banned-players", "Location of banned players file")
+ .withRequiredArg()
+ .ofType(File.class)
+ .defaultsTo(new File("banned-players.json"))
+ .describedAs("Banned players file");
+
+ accepts("banned-ips", "Location of banned IPs file")
+ .withRequiredArg()
+ .ofType(File.class)
+ .defaultsTo(new File("banned-ips.json"))
+ .describedAs("Banned IPs file");
+
+ accepts("whitelist", "Location of whitelist file")
+ .withRequiredArg()
+ .ofType(File.class)
+ .defaultsTo(new File("whitelist.json"))
+ .describedAs("Whitelist file");
+
+ accepts("ops", "Location of operators file")
+ .withRequiredArg()
+ .ofType(File.class)
+ .defaultsTo(new File("ops.json"))
+ .describedAs("Operators file");
+
+ accepts("console-history", "Location of console history file")
+ .withRequiredArg()
+ .ofType(File.class)
+ .defaultsTo(new File(".console_history"))
+ .describedAs("Console history file");
+ // DivineMC end - make configurable location of files end
+
this.accepts("server-name", "Name of the server")
.withRequiredArg()
.ofType(String.class)
diff --git a/src/main/java/org/bukkit/craftbukkit/help/HelpYamlReader.java b/src/main/java/org/bukkit/craftbukkit/help/HelpYamlReader.java
index a659e06c4cff1c17333703881c36fed78f61efce..3026662462475ad73465655be0a6eb2141f61f95 100644
--- a/src/main/java/org/bukkit/craftbukkit/help/HelpYamlReader.java
+++ b/src/main/java/org/bukkit/craftbukkit/help/HelpYamlReader.java
@@ -25,7 +25,7 @@ public class HelpYamlReader {
public HelpYamlReader(Server server) {
this.server = server;
- File helpYamlFile = new File("help.yml");
+ File helpYamlFile = (File) net.minecraft.server.dedicated.DedicatedServer.getServer().options.valueOf("help-location"); // DivineMC - make configurable location of files
YamlConfiguration defaultConfig = YamlConfiguration.loadConfiguration(new InputStreamReader(this.getClass().getClassLoader().getResourceAsStream("configurations/help.yml"), StandardCharsets.UTF_8));
try {

View File

@@ -0,0 +1,28 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sun, 29 Jun 2025 15:09:57 +0300
Subject: [PATCH] Smooth teleport API
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index 4eb7d90f38e744b51c11d334a44dacb0a9ac6956..f0065fd83dbe3bd0552251b6e01f2a051a9e4ab9 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -1371,6 +1371,17 @@ public class CraftPlayer extends CraftHumanEntity implements Player, PluginMessa
// Paper end - Teleportation API
}
+ // DivineMC start - Smooth teleport API
+ @Override
+ public boolean teleportWithoutRespawn(Location location) {
+ ServerPlayer serverPlayer = getHandle();
+ serverPlayer.smoothWorldTeleport = true;
+ boolean teleportResult = teleport(location);
+ serverPlayer.smoothWorldTeleport = false;
+ return teleportResult;
+ }
+ // DivineMC end - Smooth teleport API
+
@Override
public boolean teleport(Location location, PlayerTeleportEvent.TeleportCause cause) {
// Paper start - Teleport API

View File

@@ -0,0 +1,45 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sat, 1 Feb 2025 19:52:39 +0300
Subject: [PATCH] SparklyPaper: Optimize "canSee" checks
Original project: https://github.com/SparklyPower/SparklyPaper
Patch description:
The "canSee" checks is in a hot path, invoked by each entity for each player on the server if they are in tracking range, so optimizing it is pretty nice
First, we change the original "HashMap" to fastutil's "Object2ObjectOpenHashMap", because the containsKey throughput is better
Then, we add a "isEmpty()" check before attempting to check if the map contains something
This seems stupid, but it does seem that it improves the performance a bit, and it makes sense, "containsKey(...)" does not attempt to check the map size before attempting to check if the map contains the key
We also create a "canSee" method tailored for "ChunkMap#updatePlayer()", a method without the equals check (the "updatePlayer()" already checks if the entity is the same entity) because the CraftPlayer's `equals()` check is a *bit* expensive compared to only checking the object's identity, and because the identity has already been check, we don't need to check it twice.
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index f0065fd83dbe3bd0552251b6e01f2a051a9e4ab9..6b8841a1a84f6316b89b052328bbb549b4acbe21 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -227,7 +227,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player, PluginMessa
private long lastPlayed = 0;
private boolean hasPlayedBefore = false;
private final ConversationTracker conversationTracker = new ConversationTracker();
- private final Map<UUID, Set<WeakReference<Plugin>>> invertedVisibilityEntities = new HashMap<>();
+ private final Map<UUID, Set<WeakReference<Plugin>>> invertedVisibilityEntities = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(); // DivineMC - SparklyPaper: Optimize "canSee" checks
private final Set<UUID> unlistedEntities = new HashSet<>(); // Paper - Add Listing API for Player
private static final WeakHashMap<Plugin, WeakReference<Plugin>> pluginWeakReferences = new WeakHashMap<>();
private int hash = 0;
@@ -2250,9 +2250,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player, PluginMessa
@Override
public boolean canSee(org.bukkit.entity.Entity entity) {
- return this.equals(entity) || entity.isVisibleByDefault() ^ this.invertedVisibilityEntities.containsKey(entity.getUniqueId()); // SPIGOT-7312: Can always see self
+ return this.equals(entity) || entity.isVisibleByDefault() ^ (!invertedVisibilityEntities.isEmpty() && this.invertedVisibilityEntities.containsKey(entity.getUniqueId())); // SPIGOT-7312: Can always see self // DivineMC - SparklyPaper: Optimize "canSee" checks
}
+ // DivineMC start - SparklyPaper: Optimize "canSee" checks
+ public boolean canSeeChunkMapUpdatePlayer(org.bukkit.entity.Entity entity) {
+ return entity.isVisibleByDefault() ^ (!invertedVisibilityEntities.isEmpty() && this.invertedVisibilityEntities.containsKey(entity.getUniqueId()));
+ }
+ // DivineMC end - SparklyPaper: Optimize "canSee" checks
+
public boolean canSeePlayer(UUID uuid) {
org.bukkit.entity.Entity entity = this.getServer().getPlayer(uuid);

View File

@@ -0,0 +1,59 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Mon, 24 Feb 2025 19:36:33 +0300
Subject: [PATCH] Virtual Threads
diff --git a/src/main/java/io/papermc/paper/util/MCUtil.java b/src/main/java/io/papermc/paper/util/MCUtil.java
index e3138cd673789328514da91bd07484172cd8ae4d..982b9f3b7e546b7e8a0aad883e9a29bb772ddc81 100644
--- a/src/main/java/io/papermc/paper/util/MCUtil.java
+++ b/src/main/java/io/papermc/paper/util/MCUtil.java
@@ -38,7 +38,7 @@ public final class MCUtil {
run.run();
}
};
- public static final ExecutorService ASYNC_EXECUTOR = Executors.newFixedThreadPool(2, new ThreadFactoryBuilder()
+ public static final ExecutorService ASYNC_EXECUTOR = org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.virtualThreadsEnabled && org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.virtualAsyncExecutor ? Executors.newVirtualThreadPerTaskExecutor() : Executors.newFixedThreadPool(2, new ThreadFactoryBuilder() // DivineMC - Virtual Threads
.setNameFormat("Paper Async Task Handler Thread - %1$d")
.setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(MinecraftServer.LOGGER))
.build()
diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncScheduler.java
index 27562fd66ae9d091837cab74057706c8a6b6521c..69fad4fcfa7f3692dedeb8e76402b6de39d82560 100644
--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncScheduler.java
+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncScheduler.java
@@ -31,14 +31,18 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class CraftAsyncScheduler extends CraftScheduler {
-
- private final ThreadPoolExecutor executor = new ThreadPoolExecutor(
+ // DivineMC start - Virtual Threads
+ private final ExecutorService executor = org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.virtualThreadsEnabled && org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.virtualBukkitScheduler
+ ? Executors.newVirtualThreadPerTaskExecutor()
+ : new ThreadPoolExecutor(
+ // DivineMC end - Virtual Threads
4, Integer.MAX_VALUE, 30L, TimeUnit.SECONDS, new SynchronousQueue<>(),
new ThreadFactoryBuilder().setNameFormat("Craft Scheduler Thread - %1$d").build());
private final Executor management = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder()
@@ -47,8 +51,12 @@ public class CraftAsyncScheduler extends CraftScheduler {
CraftAsyncScheduler() {
super(true);
- executor.allowCoreThreadTimeOut(true);
- executor.prestartAllCoreThreads();
+ // DivineMC start - Virtual Threads
+ if (!org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.virtualThreadsEnabled && !org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.virtualBukkitScheduler) {
+ ((ThreadPoolExecutor) executor).allowCoreThreadTimeOut(true);
+ ((ThreadPoolExecutor) executor).prestartAllCoreThreads();
+ }
+ // DivineMC end - Virtual Threads
}
@Override

View File

@@ -0,0 +1,43 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Tue, 28 Jan 2025 00:54:57 +0300
Subject: [PATCH] Implement Secure Seed
Original license: GPLv3
Original project: https://github.com/plasmoapp/matter
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
index 09baa8c13a56e0f503815a436042b7b79b1698e4..eb182ae056d7f17f0a49e3e7d1de3d6d79f40f4b 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
@@ -193,7 +193,12 @@ public class CraftChunk implements Chunk {
@Override
public boolean isSlimeChunk() {
// 987234911L is taken from Slime when seeing if a slime can spawn in a chunk
- return this.level.paperConfig().entities.spawning.allChunksAreSlimeChunks || WorldgenRandom.seedSlimeChunk(this.getX(), this.getZ(), this.getWorld().getSeed(), level.spigotConfig.slimeSeed).nextInt(10) == 0; // Paper
+ // DivineMC start - Implement Secure Seed
+ boolean isSlimeChunk = org.bxteam.divinemc.config.DivineConfig.MiscCategory.enableSecureSeed
+ ? this.level.getChunk(this.getX(), this.getZ()).isSlimeChunk()
+ : WorldgenRandom.seedSlimeChunk(this.getX(), this.getZ(), this.getWorld().getSeed(), this.level.spigotConfig.slimeSeed).nextInt(10) == 0; // Paper
+ return this.level.paperConfig().entities.spawning.allChunksAreSlimeChunks || isSlimeChunk;
+ // DivineMC end - Implement Secure Seed
}
@Override
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index ac2e91d00fb0320f0c9a049ab980f6e2f5124dd9..d66c80582f66ccc7f7eada0eba28c19ef22c8ced 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -1255,7 +1255,11 @@ public final class CraftServer implements Server {
registryAccess = levelDataAndDimensions.dimensions().dimensionsRegistryAccess();
} else {
LevelSettings levelSettings;
- WorldOptions worldOptions = new WorldOptions(creator.seed(), creator.generateStructures(), creator.bonusChest());
+ // DivineMC start - Implement Secure Seed
+ WorldOptions worldOptions = org.bxteam.divinemc.config.DivineConfig.MiscCategory.enableSecureSeed
+ ? new WorldOptions(creator.seed(), su.plo.matter.Globals.createRandomWorldSeed(), creator.generateStructures(), false)
+ : new WorldOptions(creator.seed(), creator.generateStructures(), false);
+ // DivineMC end - Implement Secure Seed
WorldDimensions worldDimensions;
DedicatedServerProperties.WorldDimensionData properties = new DedicatedServerProperties.WorldDimensionData(GsonHelper.parse((creator.generatorSettings().isEmpty()) ? "{}" : creator.generatorSettings()), creator.type().name().toLowerCase(Locale.ROOT));

View File

@@ -0,0 +1,50 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Sun, 20 Jul 2025 16:10:25 +0300
Subject: [PATCH] Paper PR: Fire ServerListPingEvent for secondary motd send
Original license: GPLv3
Original project: https://github.com/PaperMC/Paper
Paper pull request: https://github.com/PaperMC/Paper/pull/8074
diff --git a/src/main/java/com/destroystokyo/paper/network/StandardPaperServerListPingEventImpl.java b/src/main/java/com/destroystokyo/paper/network/StandardPaperServerListPingEventImpl.java
index b40b79beb3115b80150d1aa4455ebb7a2a055574..326510e9a8213937bbfa1e2e75be9f08ce8ac925 100644
--- a/src/main/java/com/destroystokyo/paper/network/StandardPaperServerListPingEventImpl.java
+++ b/src/main/java/com/destroystokyo/paper/network/StandardPaperServerListPingEventImpl.java
@@ -64,13 +64,24 @@ public final class StandardPaperServerListPingEventImpl extends PaperServerListP
}
public static void processRequest(MinecraftServer server, Connection networkManager) {
+ // DivineMC start - Paper PR: Fire ServerListPingEvent for secondary motd send
+ ServerStatus ping = getEventResponse(server, networkManager);
+ if (ping == null) {
+ networkManager.disconnect((Component) null);
+ return;
+ }
+
+ networkManager.send(new ClientboundStatusResponsePacket(ping));
+ }
+
+ public static ServerStatus getEventResponse(MinecraftServer server, Connection networkManager) {
+ // DivineMC end - Paper PR: Fire ServerListPingEvent for secondary motd send
StandardPaperServerListPingEventImpl event = new StandardPaperServerListPingEventImpl(server, networkManager, server.getStatus());
server.server.getPluginManager().callEvent(event);
// Close connection immediately if event is cancelled
if (event.isCancelled()) {
- networkManager.disconnect((Component) null);
- return;
+ return null; // DivineMC - Paper PR: Fire ServerListPingEvent for secondary motd send
}
// Setup response
@@ -98,8 +109,6 @@ public final class StandardPaperServerListPingEventImpl extends PaperServerListP
}
final ServerStatus ping = new ServerStatus(description, players, Optional.of(version), favicon, server.enforceSecureProfile());
- // Send response
- networkManager.send(new ClientboundStatusResponsePacket(ping));
+ return ping; // DivineMC - Paper PR: Fire ServerListPingEvent for secondary motd send
}
-
}

View File

@@ -0,0 +1,26 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Wed, 9 Jul 2025 03:07:19 +0300
Subject: [PATCH] Pufferfish: Simpler ShapelessRecipes comparison for Vanilla
Original license: GPL v3
Original project: https://github.com/pufferfish-gg/Pufferfish
Patch description:
Paper added a fancy sorting comparison due to Bukkit recipes breaking
the vanilla one, however this is far more advanced than what you need
for all the vanilla recipes.
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java
index 7c989318dc7ad89bb0d9143fcaac1e4bba6f5907..233446d9aa149ec5be7a009622d0c89353f3d3df 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java
@@ -44,6 +44,6 @@ public class CraftShapelessRecipe extends ShapelessRecipe implements CraftRecipe
data.add(this.toNMS(i, true));
}
- MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftRecipe.toMinecraft(this.getKey()), new net.minecraft.world.item.crafting.ShapelessRecipe(this.getGroup(), CraftRecipe.getCategory(this.getCategory()), CraftItemStack.asNMSCopy(this.getResult()), data)));
+ MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftRecipe.toMinecraft(this.getKey()), new net.minecraft.world.item.crafting.ShapelessRecipe(this.getGroup(), CraftRecipe.getCategory(this.getCategory()), CraftItemStack.asNMSCopy(this.getResult()), data, true))); // DivineMC - Pufferfish: Simpler ShapelessRecipes comparison for Vanilla
}
}

View File

@@ -21,30 +21,24 @@ import static it.unimi.dsi.fastutil.Hash.FAST_LOAD_FACTOR;
/**
* Generally provides better performance over the vanilla {@link net.minecraft.world.level.chunk.HashMapPalette} when calling
* {@link LithiumHashPalette#idFor(Object)} through using a faster backing map and reducing pointer chasing.
* {@link LithiumHashPalette#idFor(Object, PaletteResize)} through using a faster backing map and reducing pointer chasing.
*/
public class LithiumHashPalette<T> implements Palette<T> {
private static final int ABSENT_VALUE = -1;
private final IdMap<T> idList;
private final PaletteResize<T> resizeHandler;
private final int indexBits;
private final Reference2IntOpenHashMap<T> table;
private T[] entries;
private int size = 0;
private LithiumHashPalette(IdMap<T> idList, PaletteResize<T> resizeHandler, int indexBits, T[] entries, Reference2IntOpenHashMap<T> table, int size) {
this.idList = idList;
this.resizeHandler = resizeHandler;
private LithiumHashPalette(int indexBits, T[] entries, Reference2IntOpenHashMap<T> table, int size) {
this.indexBits = indexBits;
this.entries = entries;
this.table = table;
this.size = size;
}
public LithiumHashPalette(IdMap<T> idList, int bits, PaletteResize<T> resizeHandler, List<T> list) {
this(idList, bits, resizeHandler);
public LithiumHashPalette(int bits, List<T> list) {
this(bits);
for (T t : list) {
this.addEntry(t);
@@ -52,10 +46,8 @@ public class LithiumHashPalette<T> implements Palette<T> {
}
@SuppressWarnings("unchecked")
public LithiumHashPalette(IdMap<T> idList, int bits, PaletteResize<T> resizeHandler) {
this.idList = idList;
public LithiumHashPalette(int bits) {
this.indexBits = bits;
this.resizeHandler = resizeHandler;
int capacity = 1 << bits;
@@ -65,11 +57,11 @@ public class LithiumHashPalette<T> implements Palette<T> {
}
@Override
public int idFor(@NotNull T obj) {
public int idFor(@NotNull T obj, @NotNull PaletteResize<T> resizeHandler) {
int id = this.table.getInt(obj);
if (id == ABSENT_VALUE) {
id = this.computeEntry(obj);
id = this.computeEntry(obj, resizeHandler);
}
return id;
@@ -86,14 +78,14 @@ public class LithiumHashPalette<T> implements Palette<T> {
return false;
}
private int computeEntry(T obj) {
private int computeEntry(T obj, PaletteResize<T> resizeHandler) {
int id = this.addEntry(obj);
if (id >= 1 << this.indexBits) {
if (this.resizeHandler == null) {
if (resizeHandler == null) {
throw new IllegalStateException("Cannot grow");
} else {
id = this.resizeHandler.onResize(this.indexBits + 1, obj);
id = resizeHandler.onResize(this.indexBits + 1, obj);
}
}
@@ -149,32 +141,32 @@ public class LithiumHashPalette<T> implements Palette<T> {
}
@Override
public void read(FriendlyByteBuf buf) {
public void read(FriendlyByteBuf buf, @NotNull IdMap<T> idMap) {
this.clear();
int entryCount = buf.readVarInt();
for (int i = 0; i < entryCount; ++i) {
this.addEntry(this.idList.byIdOrThrow(buf.readVarInt()));
this.addEntry(idMap.byIdOrThrow(buf.readVarInt()));
}
}
@Override
public void write(FriendlyByteBuf buf) {
public void write(FriendlyByteBuf buf, @NotNull IdMap<T> idMap) {
int size = this.size;
buf.writeVarInt(size);
for (int i = 0; i < size; ++i) {
buf.writeVarInt(this.idList.getId(this.valueFor(i)));
buf.writeVarInt(idMap.getId(this.valueFor(i)));
}
}
@Override
public int getSerializedSize() {
public int getSerializedSize(@NotNull IdMap<T> idMap) {
int size = VarInt.getByteSize(this.size);
for (int i = 0; i < this.size; ++i) {
size += VarInt.getByteSize(this.idList.getId(this.valueFor(i)));
size += VarInt.getByteSize(idMap.getId(this.valueFor(i)));
}
return size;
@@ -186,8 +178,8 @@ public class LithiumHashPalette<T> implements Palette<T> {
}
@Override
public @NotNull Palette<T> copy(@NotNull PaletteResize<T> resizeHandler) {
return new LithiumHashPalette<>(this.idList, resizeHandler, this.indexBits, this.entries.clone(), this.table.clone(), this.size);
public @NotNull Palette<T> copy() {
return new LithiumHashPalette<>(this.indexBits, this.entries.clone(), this.table.clone(), this.size);
}
private void clear() {
@@ -201,7 +193,7 @@ public class LithiumHashPalette<T> implements Palette<T> {
return Arrays.asList(copy);
}
public static <A> Palette<A> create(int bits, IdMap<A> idList, PaletteResize<A> listener, List<A> list) {
return new LithiumHashPalette<>(idList, bits, listener, list);
public static <A> Palette<A> create(int bits, List<A> list) {
return new LithiumHashPalette<>(bits, list);
}
}

View File

@@ -216,11 +216,6 @@ public class DivineConfig {
public static int asyncEntityTrackerKeepalive = 60;
public static int asyncEntityTrackerQueueSize = 0;
// Async Join Thread settings
public static boolean asyncJoinEnabled = false;
public static int asyncJoinThreadCount = 1;
public static boolean asyncJoinUseVirtualThreads = false;
// Async chunk sending settings
public static boolean asyncChunkSendingEnabled = true;
public static int asyncChunkSendingMaxThreads = 1;
@@ -233,7 +228,6 @@ public class DivineConfig {
regionizedChunkTicking();
asyncPathfinding();
multithreadedTracker();
asyncJoinSettings();
asyncChunkSending();
asyncMobSpawning();
}
@@ -327,18 +321,6 @@ public class DivineConfig {
if (asyncEntityTrackerQueueSize <= 0) asyncEntityTrackerQueueSize = asyncEntityTrackerMaxThreads * 384;
}
private static void asyncJoinSettings() {
asyncJoinEnabled = getBoolean(ConfigCategory.ASYNC.key("join-thread.enabled"), asyncJoinEnabled,
"Enables async join thread, which offloads player setup and connection tasks to a separate thread",
"This can significantly improve MSPT when multiple players are joining simultaneously");
asyncJoinThreadCount = getInt(ConfigCategory.ASYNC.key("join-thread.thread-count"), asyncJoinThreadCount,
"Number of threads to use for async join operations");
asyncJoinUseVirtualThreads = getBoolean(ConfigCategory.ASYNC.key("join-thread.use-virtual-threads"), asyncJoinUseVirtualThreads,
"Whether to use virtual threads for async join operations (requires Java 21+)");
AsyncJoinHandler.init(asyncJoinEnabled, asyncJoinThreadCount);
}
private static void asyncChunkSending() {
asyncChunkSendingEnabled = getBoolean(ConfigCategory.ASYNC.key("chunk-sending.enable"), asyncChunkSendingEnabled,
"Makes chunk sending asynchronous, which can significantly reduce main thread load when many players are loading chunks.");
@@ -408,7 +390,6 @@ public class DivineConfig {
public static boolean virtualTabCompleteScheduler = false;
public static boolean virtualAsyncExecutor = false;
public static boolean virtualCommandBuilderScheduler = false;
public static boolean virtualProfileLookupPool = false;
public static boolean virtualServerTextFilterPool = false;
public static void load() {
@@ -548,8 +529,6 @@ public class DivineConfig {
"Uses virtual threads for the MCUtil async executor.");
virtualCommandBuilderScheduler = getBoolean(ConfigCategory.PERFORMANCE.key("virtual-threads.command-builder-scheduler"), virtualCommandBuilderScheduler,
"Uses virtual threads for the Async Command Builder Thread Pool.");
virtualProfileLookupPool = getBoolean(ConfigCategory.PERFORMANCE.key("virtual-threads.profile-lookup-pool"), virtualProfileLookupPool,
"Uses virtual threads for the Profile Lookup Pool, that is used for fetching player profiles.");
virtualServerTextFilterPool = getBoolean(ConfigCategory.PERFORMANCE.key("virtual-threads.server-text-filter-pool"), virtualServerTextFilterPool,
"Uses virtual threads for the server text filter pool.");
}