From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: AlphaKR93 Date: Thu, 25 Jan 2024 17:13:09 +0900 Subject: [PATCH] Port SparklyPaper patches SparklyPower Copyright (C) 2024 SparklyPower Based on commit: 29212936a832106c4d68e2a2017acbea2fdd3cc4 diff --git a/src/main/java/io/papermc/paper/command/MSPTCommand.java b/src/main/java/io/papermc/paper/command/MSPTCommand.java index 8b5293b0c696ef21d0101493ffa41b60bf0bc86b..03be23690a94a14d7343526acad67ccf53b85c70 100644 --- a/src/main/java/io/papermc/paper/command/MSPTCommand.java +++ b/src/main/java/io/papermc/paper/command/MSPTCommand.java @@ -78,6 +78,47 @@ public final class MSPTCommand extends Command { ) ) ); + + // Plazma start - Port SparklyPaper patches; Track World specific MSPT + sender.sendMessage(text()); + sender.sendMessage(text().content("World tick times ").color(GOLD) + .append(text().color(YELLOW) + .append( + text("("), + text("avg", GRAY), + text("/"), + text("min", GRAY), + text("/"), + text("max", GRAY), + text(")") + ) + ).append( + text(" from last 5s"), + text(",", GRAY), + text(" 10s"), + text(",", GRAY), + text(" 1m"), + text(":", YELLOW) + ) + ); + for (net.minecraft.server.level.ServerLevel level: server.getAllLevels()) { + List worldTimes = new ArrayList<>(); + worldTimes.addAll(eval(level.tickTimes5s.getTimes())); + worldTimes.addAll(eval(level.tickTimes10s.getTimes())); + worldTimes.addAll(eval(level.tickTimes60s.getTimes())); + + sender.sendMessage(text().content("◴ " + level.getWorld().getName() + ": ").color(GOLD) + .append(text().color(GRAY) + .append( + worldTimes.get(0), SLASH, worldTimes.get(1), SLASH, worldTimes.get(2), text(", ", YELLOW), + worldTimes.get(3), SLASH, worldTimes.get(4), SLASH, worldTimes.get(5), text(", ", YELLOW), + worldTimes.get(6), SLASH, worldTimes.get(7), SLASH, worldTimes.get(8) + ) + ) + ); + } + // Plazma end - Port SparklyPaper patches; Track World specific MSPT + return true; } diff --git a/src/main/java/io/papermc/paper/threadedregions/EntityScheduler.java b/src/main/java/io/papermc/paper/threadedregions/EntityScheduler.java index 62484ebf4550b05182f693a3180bbac5d5fd906d..8b39c463b90db2d4faa33471ddce65f775908f2f 100644 --- a/src/main/java/io/papermc/paper/threadedregions/EntityScheduler.java +++ b/src/main/java/io/papermc/paper/threadedregions/EntityScheduler.java @@ -41,6 +41,7 @@ public final class EntityScheduler { private long tickCount = 0L; private static final long RETIRED_TICK_COUNT = -1L; + private static final net.minecraft.server.MinecraftServer SERVER = net.minecraft.server.MinecraftServer.getServer(); private final Object stateLock = new Object(); private final Long2ObjectOpenHashMap> oneTimeDelayed = new Long2ObjectOpenHashMap<>(); @@ -61,14 +62,15 @@ public final class EntityScheduler { * @throws IllegalStateException If the scheduler is already retired. */ public void retire() { + final Entity thisEntity = this.entity.getHandleRaw(); // Plazma - Port SparklyPaper patches; Skip EntityScheduler's executeTick checks if there isn't any tasks to be run synchronized (this.stateLock) { if (this.tickCount == RETIRED_TICK_COUNT) { throw new IllegalStateException("Already retired"); } this.tickCount = RETIRED_TICK_COUNT; + SERVER.entitiesWithScheduledTasks.remove(thisEntity); // Plazma - Port SparklyPaper patches; Skip EntityScheduler's executeTick checks if there isn't any tasks to be run } - final Entity thisEntity = this.entity.getHandleRaw(); // correctly handle and order retiring while running executeTick for (int i = 0, len = this.currentlyExecuting.size(); i < len; ++i) { @@ -124,9 +126,12 @@ public final class EntityScheduler { if (this.tickCount == RETIRED_TICK_COUNT) { return false; } - this.oneTimeDelayed.computeIfAbsent(this.tickCount + Math.max(1L, delay), (final long keyInMap) -> { - return new ArrayList<>(); - }).add(task); + // Plazma start - Port SparklyPaper patches; Skip EntityScheduler's executeTick checks if there isn't any tasks to be run + SERVER.entitiesWithScheduledTasks.add(this.entity.getHandleRaw()); + this.oneTimeDelayed.computeIfAbsent( + this.tickCount + Math.max(1L, delay), (final long keyInMap) -> new ArrayList<>() + ).add(task); + // Plazma end - Port SparklyPaper patches; Skip EntityScheduler's executeTick checks if there isn't any tasks to be run } return true; @@ -143,6 +148,12 @@ public final class EntityScheduler { TickThread.ensureTickThread(thisEntity, "May not tick entity scheduler asynchronously"); final List toRun; synchronized (this.stateLock) { + // Plazma start - Port SparklyPaper patches; Skip EntityScheduler's executeTick checks if there isn't any tasks to be run + if (this.currentlyExecuting.isEmpty() && this.oneTimeDelayed.isEmpty()) { + SERVER.entitiesWithScheduledTasks.remove(thisEntity); + return; + } + // Plazma end - Port SparklyPaper patches; Skip EntityScheduler's executeTick checks if there isn't any tasks to be run if (this.tickCount == RETIRED_TICK_COUNT) { throw new IllegalStateException("Ticking retired scheduler"); } diff --git a/src/main/java/io/papermc/paper/util/player/NearbyPlayers.java b/src/main/java/io/papermc/paper/util/player/NearbyPlayers.java index f164256d59b761264876ca0c85f812d101bfd5de..deaeb134c47da8710afa747bf980bd00aab846d6 100644 --- a/src/main/java/io/papermc/paper/util/player/NearbyPlayers.java +++ b/src/main/java/io/papermc/paper/util/player/NearbyPlayers.java @@ -106,6 +106,13 @@ public final class NearbyPlayers { return chunk == null ? null : chunk.players[type.ordinal()]; } + // Plazma start - Port SparklyPaper patches; Cache coordinate key used for nearby players when ticking chunks + public ReferenceList getPlayers(final long nearbyCoordKey, final NearbyMapType type) { + final TrackedChunk chunk = this.byChunk.get(nearbyCoordKey); + return chunk == null ? null : chunk.players[type.ordinal()]; + } + // Plazma end - Port SparklyPaper patches; Cache coordinate key used for nearby players when ticking chunks + public ReferenceList getPlayersByChunk(final int chunkX, final int chunkZ, final NearbyMapType type) { final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(chunkX, chunkZ)); diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java index 690423f4159ddff2d90d7ccf931db72903cb822c..c9a5c3b915f09a316f0f2725c51b696da111ccd3 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -324,6 +324,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop entitiesWithScheduledTasks = java.util.concurrent.ConcurrentHashMap.newKeySet(); // Plazma - Port SparklyPaper patches; Skip EntityScheduler's executeTick checks if there isn't any tasks to be run public static S spin(Function serverFactory) { AtomicReference atomicreference = new AtomicReference(); @@ -1745,17 +1746,15 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { - for (final Entity entity : level.getEntityLookup().getAllCopy()) { // Paper - rewrite chunk system - if (entity.isRemoved()) { - continue; - } - final org.bukkit.craftbukkit.entity.CraftEntity bukkit = entity.getBukkitEntityRaw(); - if (bukkit != null) { - bukkit.taskScheduler.executeTick(); - } - } - }); + // Plazma start - Port SparklyPaper patches; Skip EntityScheduler's executeTick checks if there isn't any tasks to be run + for (final Entity entity: entitiesWithScheduledTasks) { + if (entity.isRemoved()) continue; + + final org.bukkit.craftbukkit.entity.CraftEntity bukkit = entity.getBukkitEntityRaw(); + //noinspection ConstantValue + if (bukkit != null) bukkit.taskScheduler.executeTick(); + } + // Plazma end - Port SparklyPaper patches; Skip EntityScheduler's executeTick checks if there isn't any tasks to be run // Paper end - Folia scheduler API io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.CALLBACK_MANAGER.handleQueue(this.tickCount); // Paper //this.profiler.push("commandFunctions"); // Purpur @@ -1822,7 +1821,15 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop playersNearby - = nearbyPlayers.getPlayers(chunkcoordintpair, io.papermc.paper.util.player.NearbyPlayers.NearbyMapType.SPAWN_RANGE); + = nearbyPlayers.getPlayers(chunk1.nearbyPlayersCoordinateKey, io.papermc.paper.util.player.NearbyPlayers.NearbyMapType.SPAWN_RANGE); if (playersNearby == null) { continue; } diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java index 78be6cea03f40edfa90b5d9df014d5cf22b81f61..c376997ea2b8f61c2dbd80b31d51ba731fd5ed4c 100644 --- a/src/main/java/net/minecraft/server/level/ServerEntity.java +++ b/src/main/java/net/minecraft/server/level/ServerEntity.java @@ -78,6 +78,7 @@ public class ServerEntity { private List> trackedDataValues; // CraftBukkit start public final Set trackedPlayers; // Purpur - private -> public + public static boolean skipSqrWhenNoDeltaChanges = false; // Plazma - SparklyPaper port; Skip distanceToSqr if the delta movement hasn't changed public ServerEntity(ServerLevel worldserver, Entity entity, int i, boolean flag, Consumer> consumer, Set trackedPlayers) { this.trackedPlayers = trackedPlayers; @@ -217,12 +218,15 @@ public class ServerEntity { if ((this.trackDelta || this.entity.hasImpulse || this.entity instanceof LivingEntity && ((LivingEntity) this.entity).isFallFlying()) && this.tickCount > 0) { Vec3 vec3d1 = this.entity.getDeltaMovement(); - double d0 = vec3d1.distanceToSqr(this.ap); - - if (d0 > 1.0E-7D || d0 > 0.0D && vec3d1.lengthSqr() == 0.0D) { - this.ap = vec3d1; - this.broadcast.accept(new ClientboundSetEntityMotionPacket(this.entity.getId(), this.ap)); + // Plazma start - SparklyPaper port; Skip distanceToSqr if the delta movement hasn't changed + if (!skipSqrWhenNoDeltaChanges && vec3d1 != this.ap) { + double d0 = vec3d1.distanceToSqr(this.ap); + if (d0 > 1.0E-7D || d0 > 0.0D && vec3d1.lengthSqr() == 0.0D) { + this.ap = vec3d1; + this.broadcast.accept(new ClientboundSetEntityMotionPacket(this.entity.getId(), this.ap)); + } } + // Plazma end - SparklyPaper port; Skip distanceToSqr if the delta movement hasn't changed } if (packet1 != null) { diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java index 5456d2ded6f6e75c48868147e5a2dde5eb23bec8..e2de0c343098d65d830f27b4f80c3f8a9f18a7ae 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java @@ -233,6 +233,12 @@ public class ServerLevel extends Level implements WorldGenLevel { private final alternate.current.wire.WireHandler wireHandler = new alternate.current.wire.WireHandler(this); // Paper - optimize redstone (Alternate Current) public boolean hasRidableMoveEvent = false; // Purpur + // Plazma start - Port SparklyPaper patches; Track World specific MSPT + public final MinecraftServer.TickTimes tickTimes5s = new MinecraftServer.TickTimes(100); + public final MinecraftServer.TickTimes tickTimes10s = new MinecraftServer.TickTimes(200); + public final MinecraftServer.TickTimes tickTimes60s = new MinecraftServer.TickTimes(1200); + // Plazma end - Port SparklyPaper patches; Track World specific MSPT + public LevelChunk getChunkIfLoaded(int x, int z) { return this.chunkSource.getChunkAtIfLoadedImmediately(x, z); // Paper - Use getChunkIfLoadedImmediately } diff --git a/src/main/java/net/minecraft/stats/ServerStatsCounter.java b/src/main/java/net/minecraft/stats/ServerStatsCounter.java index f890738d3bb9fb5e70a9d323c6cec97f9948f9cf..eb3fc1af68768383d524cf0f50c4f4b304b9d5ca 100644 --- a/src/main/java/net/minecraft/stats/ServerStatsCounter.java +++ b/src/main/java/net/minecraft/stats/ServerStatsCounter.java @@ -90,12 +90,14 @@ public class ServerStatsCounter extends StatsCounter { this.dirty.add(stat); } + /* // Plazma - Port SparklyPaper patches; Skip dirty stats copy when requesting player stats private Set> getDirty() { Set> set = Sets.newHashSet(this.dirty); this.dirty.clear(); return set; } + */ // Plazma - Port SparklyPaper patches; Skip dirty stats copy when requesting player stats public void parseLocal(DataFixer dataFixer, String json) { try { @@ -243,7 +245,7 @@ public class ServerStatsCounter extends StatsCounter { public void sendStats(ServerPlayer player) { Object2IntMap> object2intmap = new Object2IntOpenHashMap(); - Iterator iterator = this.getDirty().iterator(); + Iterator> iterator = this.dirty.iterator(); // Plazma - SparklyPaper port; Skip dirty stats copy when requesting player stats while (iterator.hasNext()) { Stat statistic = (Stat) iterator.next(); @@ -251,6 +253,7 @@ public class ServerStatsCounter extends StatsCounter { object2intmap.put(statistic, this.getValue(statistic)); } + this.dirty.clear(); // Plazma - SparklyPaper port; Skip dirty stats copy when requesting player stats player.connection.send(new ClientboundAwardStatsPacket(object2intmap)); } } diff --git a/src/main/java/net/minecraft/world/entity/ambient/Bat.java b/src/main/java/net/minecraft/world/entity/ambient/Bat.java index a70255995f380a9b0f7057cb5cb820f7655b93fc..2bffc790496fd9b09516d0d908b5319c9dbb1c63 100644 --- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java +++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java @@ -306,7 +306,7 @@ public class Bat extends AmbientCreature { int i = world.getMaxLocalRawBrightness(pos); byte b0 = 4; - if (Bat.isHalloweenSeason(world.getMinecraftWorld())) { // Purpur + if (isSpookySeason(world.getMinecraftWorld())) { // Purpur // Plazma - Port SparklyPaper patches; Optimize Spooky Season b0 = 7; } else if (random.nextBoolean()) { return false; @@ -320,7 +320,25 @@ public class Bat extends AmbientCreature { private static boolean isSpookySeason = false; private static final int ONE_HOUR = 20 * 60 * 60; private static int lastSpookyCheck = -ONE_HOUR; - public static boolean isHalloweenSeason(Level level) { return level.purpurConfig.forceHalloweenSeason || isHalloween(); } // Purpur + + // Plazma start - Port SparklyPaper patches; Optimize Spooky Season + private static boolean isSpookySeason(Level level) { + if (level.purpurConfig.forceHalloweenSeason) return true; + if (org.plazmamc.plazma.configurations.GlobalConfiguration.get().entity.spookyOptimize) + return net.sparklypower.sparklypaper.HalloweenManager.isSpookySeason() + || net.sparklypower.sparklypaper.HalloweenManager.isHalloween(); + return isHalloween(); + } + + public static boolean isHalloweenSeason(Level level) { + if (level.purpurConfig.forceHalloweenSeason) return true; + if (org.plazmamc.plazma.configurations.GlobalConfiguration.get().entity.spookyOptimize) + return net.sparklypower.sparklypaper.HalloweenManager.isHalloween(); + return isHalloween(); + } + + @SuppressWarnings("RedundantExplicitChronoField") + // Plazma end - Port SparklyPaper patches; Optimize Spooky Season private static boolean isHalloween() { if (net.minecraft.server.MinecraftServer.currentTick - lastSpookyCheck > ONE_HOUR) { LocalDate localdate = LocalDate.now(); diff --git a/src/main/java/net/minecraft/world/item/MapItem.java b/src/main/java/net/minecraft/world/item/MapItem.java index 608390ed36710a419de1542b80340dd3fcc7299c..043f068345ca3c50209c1c3cc1feb6277a3da61a 100644 --- a/src/main/java/net/minecraft/world/item/MapItem.java +++ b/src/main/java/net/minecraft/world/item/MapItem.java @@ -268,11 +268,13 @@ public class MapItem extends ComplexItem { } } + public static boolean skipTickWhenCraftNotPresent = false; // Plazma - SparklyPaper port; Skip map item ticking if the craft map renderer is not present @Override public void inventoryTick(ItemStack stack, Level world, Entity entity, int slot, boolean selected) { if (!world.isClientSide) { MapItemSavedData mapItemSavedData = getSavedData(stack, world); if (mapItemSavedData != null) { + if (skipTickWhenCraftNotPresent && mapItemSavedData.mapView.getRenderers().stream().noneMatch(mapRenderer -> mapRenderer.getClass() == org.bukkit.craftbukkit.map.CraftMapRenderer.class)) return; // Plazma - SparklyPaper port; Skip map item ticking if the craft map renderer is not present if (entity instanceof Player player) { mapItemSavedData.tickCarriedBy(player, stack); } diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java index 59d6d25ce3d37a7d408b168aec3c2a90a6d1dfab..23f10ed05e2c4aabfa3a565a9117dabe52f175f7 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -1347,6 +1347,8 @@ public abstract class Level implements LevelAccessor, AutoCloseable { boolean flag = this.tickRateManager().runsNormally(); int tilesThisCycle = 0; + int shouldTickBlocksAtLastResult = -1; // Plazma - Port SparklyPaper patches; Optimize tickingBlockEntities + long shouldTickBlocksAtChunkPos = 0; // Plazma - Port SparklyPaper patches; Optimize tickingBlockEntities var toRemove = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet(); // Paper - Fix MC-117075; use removeAll toRemove.add(null); // Paper - Fix MC-117075 for (tileTickPosition = 0; tileTickPosition < this.blockEntityTickers.size(); tileTickPosition++) { // Paper - Disable tick limiters @@ -1359,14 +1361,28 @@ public abstract class Level implements LevelAccessor, AutoCloseable { tilesThisCycle--; toRemove.add(tickingblockentity); // Paper - Fix MC-117075; use removeAll // Spigot end - } else if (flag && this.shouldTickBlocksAt(tickingblockentity.getPos())) { - tickingblockentity.tick(); - // Paper start - execute chunk tasks during tick - if ((this.tileTickPosition & 7) == 0) { - MinecraftServer.getServer().executeMidTickTasks(); + // Plazma start - Port SparklyPaper patches; Optimize tickingBlockEntities + } else if (flag) { + long chunkPos = tickingblockentity.getChunkCoordinateKey(); + boolean shouldTick; + if (shouldTickBlocksAtChunkPos == chunkPos && shouldTickBlocksAtLastResult != -1) + shouldTick = shouldTickBlocksAtLastResult == 1; + else { + shouldTick = this.shouldTickBlocksAt(chunkPos); + shouldTickBlocksAtLastResult = shouldTick ? 1 : 0; + shouldTickBlocksAtChunkPos = chunkPos; + } + + if (shouldTick) { + tickingblockentity.tick(); + // Paper start - execute chunk tasks during tick + if ((this.tileTickPosition & 7) == 0) { + MinecraftServer.getServer().executeMidTickTasks(); + } + // Paper end - execute chunk tasks during tick } - // Paper end - execute chunk tasks during tick } + // Plazma end - Port SparklyPaper patches; Optimize tickingBlockEntities } this.blockEntityTickers.removeAll(toRemove); // Paper - Fix MC-117075 diff --git a/src/main/java/net/minecraft/world/level/block/CropBlock.java b/src/main/java/net/minecraft/world/level/block/CropBlock.java index 5a190834baef60c7b61074393f8856a933902d81..366ae05a060b5b12b85521a4b8aed1907f3f044a 100644 --- a/src/main/java/net/minecraft/world/level/block/CropBlock.java +++ b/src/main/java/net/minecraft/world/level/block/CropBlock.java @@ -77,35 +77,57 @@ public class CropBlock extends BushBlock implements BonemealableBlock { @Override protected void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) { - if (world.getRawBrightness(pos, 0) >= 9) { - int i = this.getAge(state); - - if (i < this.getMaxAge()) { - float f = CropBlock.getGrowthSpeed(this, world, pos); - - // Spigot start - int modifier; - if (this == Blocks.BEETROOTS) { - modifier = world.spigotConfig.beetrootModifier; - } else if (this == Blocks.CARROTS) { - modifier = world.spigotConfig.carrotModifier; - } else if (this == Blocks.POTATOES) { - modifier = world.spigotConfig.potatoModifier; - // Paper start - Fix Spigot growth modifiers - } else if (this == Blocks.TORCHFLOWER_CROP) { - modifier = world.spigotConfig.torchFlowerModifier; - // Paper end - Fix Spigot growth modifiers - } else { - modifier = world.spigotConfig.wheatModifier; - } + // Plazma start - Port SparklyPaper patches; Optimize Farm checks + if (world.getRawBrightness(pos, 0) < 9) return; + + int age = this.getAge(state); + if (age >= this.getMaxAge()) return; + + final int modifier; + if (this == Blocks.BEETROOTS) { + modifier = world.spigotConfig.beetrootModifier; + } else if (this == Blocks.CARROTS) { + modifier = world.spigotConfig.carrotModifier; + } else if (this == Blocks.POTATOES) { + modifier = world.spigotConfig.potatoModifier; + } else if (this == Blocks.TORCHFLOWER_CROP) { + modifier = world.spigotConfig.torchFlowerModifier; + } else { + modifier = world.spigotConfig.wheatModifier; + } - if (random.nextFloat() < (modifier / (100.0f * (Math.floor((25.0F / f) + 1))))) { // Spigot - SPIGOT-7159: Better modifier resolution - // Spigot end - CraftEventFactory.handleBlockGrowEvent(world, pos, this.getStateForAge(i + 1), 2); // CraftBukkit - } + if (world.plazmaConfig().block.optimizeFarmCheck.enabled) { + BlockPos current = pos.below(); + BlockState currentState = world.getBlockState(current); + + boolean moist; + float growthSpeed; + if (currentState.is(Blocks.FARMLAND) && currentState.getValue(FarmBlock.MOISTURE) > 0) { + moist = true; + growthSpeed = world.plazmaConfig().block.optimizeFarmCheck.growthSpeed.moist; + } else { + moist = false; + growthSpeed = world.plazmaConfig().block.optimizeFarmCheck.growthSpeed.normal; } + + if (world.plazmaConfig().block.optimizeFarmCheck.skipMiddleAgingStageForCrops) { + growthSpeed = growthSpeed / getMaxAge(); + age = getMaxAge() - 1; + } + + if (random.nextFloat() >= (modifier / (100.0f * Math.floor((25.0F / growthSpeed) + 1)))) return; + if (!CraftEventFactory.handleBlockGrowEvent(world, pos, this.getStateForAge(age + 1), 2)) return; + if (!moist || age + 1 != this.getMaxAge() || FarmBlock.isNearWater(world, current)) return; + + org.bukkit.craftbukkit.event.CraftEventFactory.handleMoistureChangeEvent(world, current, currentState.setValue(FarmBlock.MOISTURE, 0), 2); + return; } + float growthSpeed = CropBlock.getGrowthSpeed(this, world, pos); + + if (random.nextFloat() < (modifier / (100.0f * Math.floor((25.0F / growthSpeed) + 1)))) + CraftEventFactory.handleBlockGrowEvent(world, pos, this.getStateForAge(age + 1), 2); + // Plazma end - Port SparklyPaper patches; Optimize Farm checks } public void growCrops(Level world, BlockPos pos, BlockState state) { diff --git a/src/main/java/net/minecraft/world/level/block/FarmBlock.java b/src/main/java/net/minecraft/world/level/block/FarmBlock.java index d0ec0722496ed931b48c4e7076fddbb1ed36e111..b91afbc90c138ebb7f8722934f59f953642196c9 100644 --- a/src/main/java/net/minecraft/world/level/block/FarmBlock.java +++ b/src/main/java/net/minecraft/world/level/block/FarmBlock.java @@ -92,7 +92,19 @@ public class FarmBlock extends Block { @Override protected void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) { - int i = (Integer) state.getValue(FarmBlock.MOISTURE); + // Plazma start - Port SparklyPaper patches; Optimize Farm checks + int i = state.getValue(FarmBlock.MOISTURE); + if (world.plazmaConfig().block.optimizeFarmCheck.enabled) { + if (i != 0) return; + + if (isNearWater(world, pos)) + org.bukkit.craftbukkit.event.CraftEventFactory.handleMoistureChangeEvent(world, pos, state.setValue(MOISTURE, 7), 2); + else + turnToDirt(null, state, world, pos); + + return; + } + // Plazma end - Port SparklyPaper patches; Optimize Farm checks if (i > 0 && world.paperConfig().tickRates.wetFarmland != 1 && (world.paperConfig().tickRates.wetFarmland < 1 || (net.minecraft.server.MinecraftServer.currentTick + pos.hashCode()) % world.paperConfig().tickRates.wetFarmland != 0)) { return; } // Paper - Configurable random tick rates for blocks if (i == 0 && world.paperConfig().tickRates.dryFarmland != 1 && (world.paperConfig().tickRates.dryFarmland < 1 || (net.minecraft.server.MinecraftServer.currentTick + pos.hashCode()) % world.paperConfig().tickRates.dryFarmland != 0)) { return; } // Paper - Configurable random tick rates for blocks @@ -167,7 +179,7 @@ public class FarmBlock extends Block { return world.getBlockState(pos.above()).is(BlockTags.MAINTAINS_FARMLAND); } - private static boolean isNearWater(LevelReader world, BlockPos pos) { + static boolean isNearWater(LevelReader world, BlockPos pos) { // Plazma - AT (private -> package) // Paper start - Perf: remove abstract block iteration int xOff = pos.getX(); int yOff = pos.getY(); diff --git a/src/main/java/net/minecraft/world/level/block/StemBlock.java b/src/main/java/net/minecraft/world/level/block/StemBlock.java index 924d80eb41d9a71d1e521c40742557251cf51832..4a30e1e6eac4b0e3dc2147a74e73e05fa76f5db2 100644 --- a/src/main/java/net/minecraft/world/level/block/StemBlock.java +++ b/src/main/java/net/minecraft/world/level/block/StemBlock.java @@ -72,38 +72,82 @@ public class StemBlock extends BushBlock implements BonemealableBlock { @Override protected void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) { - if (world.getRawBrightness(pos, 0) >= 9) { - float f = CropBlock.getGrowthSpeed(this, world, pos); - - if (random.nextFloat() < ((this == Blocks.PUMPKIN_STEM ? world.spigotConfig.pumpkinModifier : world.spigotConfig.melonModifier) / (100.0f * (Math.floor((25.0F / f) + 1))))) { // Spigot - SPIGOT-7159: Better modifier resolution - int i = (Integer) state.getValue(StemBlock.AGE); - - if (i < 7) { - state = (BlockState) state.setValue(StemBlock.AGE, i + 1); - CraftEventFactory.handleBlockGrowEvent(world, pos, state, 2); // CraftBukkit - } else { - Direction enumdirection = Direction.Plane.HORIZONTAL.getRandomDirection(random); - BlockPos blockposition1 = pos.relative(enumdirection); - BlockState iblockdata1 = world.getBlockState(blockposition1.below()); - - if (world.getBlockState(blockposition1).isAir() && (iblockdata1.is(Blocks.FARMLAND) || iblockdata1.is(BlockTags.DIRT))) { - Registry iregistry = world.registryAccess().registryOrThrow(Registries.BLOCK); - Optional optional = iregistry.getOptional(this.fruit); - Optional optional1 = iregistry.getOptional(this.attachedStem); - - if (optional.isPresent() && optional1.isPresent()) { - // CraftBukkit start - if (!CraftEventFactory.handleBlockGrowEvent(world, blockposition1, ((Block) optional.get()).defaultBlockState())) { - return; - } - // CraftBukkit end - world.setBlockAndUpdate(pos, (BlockState) ((Block) optional1.get()).defaultBlockState().setValue(HorizontalDirectionalBlock.FACING, enumdirection)); - } - } - } + // Plazma start - Port SparklyPaper patches; Optimize Farm checks + if (world.getRawBrightness(pos, 0) < 9) return; + + int modifier = this == Blocks.PUMPKIN_STEM ? world.spigotConfig.pumpkinModifier : world.spigotConfig.melonModifier; + + if (world.plazmaConfig().block.optimizeFarmCheck.enabled) { + final BlockPos current = pos.below(); + final BlockState currentState = world.getBlockState(current); + + final boolean moist; + final float growthSpeed; + if (currentState.is(Blocks.FARMLAND) && currentState.getValue(FarmBlock.MOISTURE) > 0) { + moist = true; + growthSpeed = world.plazmaConfig().block.optimizeFarmCheck.growthSpeed.moist; + } else { + moist = false; + growthSpeed = world.plazmaConfig().block.optimizeFarmCheck.growthSpeed.normal; } + if (random.nextFloat() >= (modifier / (100.0f * Math.floor((25.0F / growthSpeed) + 1)))) return; + + int age = state.getValue(AGE); + + if (age < 7) { + CraftEventFactory.handleMoistureChangeEvent(world, pos, state.setValue(AGE, age + 1), 2); + return; + } + + Direction direction = Direction.Plane.HORIZONTAL.getRandomDirection(random); + BlockPos pos1 = pos.relative(direction); + BlockState state1 = world.getBlockState(pos1.below()); + + if (!world.getBlockState(pos1).isAir() || (!state1.is(Blocks.FARMLAND) && !state1.is(BlockTags.DIRT))) + return; + + Registry registry = world.registryAccess().registryOrThrow(Registries.BLOCK); + Optional fruit = registry.getOptional(this.fruit); + Optional stem = registry.getOptional(this.attachedStem); + + if (fruit.isEmpty() || stem.isEmpty()) return; + if (!CraftEventFactory.handleBlockGrowEvent(world, pos1, fruit.get().defaultBlockState())) return; + if (moist && !FarmBlock.isNearWater(world, current)) + org.bukkit.craftbukkit.event.CraftEventFactory.handleMoistureChangeEvent(world, current, currentState.setValue(FarmBlock.MOISTURE, 0), 2); + + world.setBlockAndUpdate(pos, stem.get().defaultBlockState().setValue(HorizontalDirectionalBlock.FACING, direction)); + return; } + + float f = CropBlock.getGrowthSpeed(this, world, pos); + + if (random.nextFloat() >= (modifier / (100.0f * Math.floor((25.0F / f) + 1)))) return; + + int age = state.getValue(StemBlock.AGE); + + if (age < 7) { + CraftEventFactory.handleBlockGrowEvent(world, pos, state.setValue(StemBlock.AGE, age + 1), 2); + return; + } + + Direction direction = Direction.Plane.HORIZONTAL.getRandomDirection(random); + BlockPos pos1 = pos.relative(direction); + BlockState state1 = world.getBlockState(pos1.below()); + + if (!world.getBlockState(pos1).isAir() || (!state1.is(Blocks.FARMLAND) && !state1.is(BlockTags.DIRT))) return; + + Registry registry = world.registryAccess().registryOrThrow(Registries.BLOCK); + Optional fruit = registry.getOptional(this.fruit); + Optional stem = registry.getOptional(this.attachedStem); + + if (fruit.isEmpty() || stem.isEmpty()) return; + + if (!CraftEventFactory.handleBlockGrowEvent(world, pos1, fruit.get().defaultBlockState())) + return; + + world.setBlockAndUpdate(pos, stem.get().defaultBlockState().setValue(HorizontalDirectionalBlock.FACING, direction)); + // Plazma end - Port SparklyPaper patches; Optimize Farm checks } @Override diff --git a/src/main/java/net/minecraft/world/level/block/entity/TickingBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/TickingBlockEntity.java index 28e3b73507b988f7234cbf29c4024c88180d0aef..6239c171ca996f3f5c23060f728a62236bc8b6d5 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/TickingBlockEntity.java +++ b/src/main/java/net/minecraft/world/level/block/entity/TickingBlockEntity.java @@ -10,4 +10,6 @@ public interface TickingBlockEntity { BlockPos getPos(); String getType(); + + long getChunkCoordinateKey(); // Plazma - Port SparklyPaper patches; Optimize tickingBlockEntities } diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java index 1aac95b03a9e2e37c24f2a30bcb259c1424e1c78..e7c0c36fd3455c0536e98259b46dbcc952d90e01 100644 --- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java +++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java @@ -66,6 +66,7 @@ public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiom protected volatile boolean unsaved; private volatile boolean isLightCorrect; protected final ChunkPos chunkPos; public final long coordinateKey; public final int locX; public final int locZ; // Paper - cache coordinate key + public final long nearbyPlayersCoordinateKey; // Plazma - Port SparklyPaper patches; Cache coordinate key used for nearby players when ticking chunks private long inhabitedTime; /** @deprecated */ @Nullable @@ -140,6 +141,7 @@ public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiom // Paper end - rewrite light engine this.locX = pos.x; this.locZ = pos.z; // Paper - reduce need for field lookups this.chunkPos = pos; this.coordinateKey = ChunkPos.asLong(locX, locZ); // Paper - cache long key + this.nearbyPlayersCoordinateKey = io.papermc.paper.util.CoordinateUtils.getChunkKey(locX, locZ); // Plazma - Port SparklyPaper patches; Cache coordinate key used for nearby players when ticking chunks this.upgradeData = upgradeData; this.levelHeightAccessor = heightLimitView; this.sections = new LevelChunkSection[heightLimitView.getSectionsCount()]; diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java index 56dd36a88f11aeac528e7be2998e52324c01fde1..e1853318350f9a10e8db106a7bc015307db0de6c 100644 --- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java @@ -75,6 +75,8 @@ public class LevelChunk extends ChunkAccess { public String getType() { return ""; } + + @Override public long getChunkCoordinateKey() { return 0; } // Plazma - Port SparklyPaper patches; Optimize tickingBlockEntities }; private final Map tickersInLevel; public boolean loaded; @@ -1101,7 +1103,7 @@ public class LevelChunk extends ChunkAccess { } private TickingBlockEntity createTicker(T blockEntity, BlockEntityTicker blockEntityTicker) { - return new LevelChunk.BoundTickingBlockEntity<>(blockEntity, blockEntityTicker); + return new LevelChunk.BoundTickingBlockEntity<>(blockEntity, blockEntityTicker, this.coordinateKey); // Plazma - Port SparklyPaper patches; Optimize TickingBlockEntity } @FunctionalInterface @@ -1152,17 +1154,21 @@ public class LevelChunk extends ChunkAccess { public String toString() { return String.valueOf(this.ticker) + " "; } + + @Override public long getChunkCoordinateKey() { return this.ticker.getChunkCoordinateKey(); } // Plazma - Port SparklyPaper patches; Optimize TickingBlockEntity } private class BoundTickingBlockEntity implements TickingBlockEntity { + private final long chunkCoordinateKey; // Plazma - Port SparklyPaper patches; Optimize TickingBlockEntity private final T blockEntity; private final BlockEntityTicker ticker; private boolean loggedInvalidBlockState; - BoundTickingBlockEntity(final BlockEntity tileentity, final BlockEntityTicker blockentityticker) { + BoundTickingBlockEntity(final BlockEntity tileentity, final BlockEntityTicker blockentityticker, long chunkCoordinateKey) { this.blockEntity = (T) tileentity; // CraftBukkit - decompile error this.ticker = blockentityticker; + this.chunkCoordinateKey = chunkCoordinateKey; } @Override @@ -1230,5 +1236,7 @@ public class LevelChunk extends ChunkAccess { return "Level ticker for " + s + "@" + String.valueOf(this.getPos()); } + + @Override public long getChunkCoordinateKey() { return this.chunkCoordinateKey; } // Plazma - Port SparklyPaper patches; Optimize TickingBlockEntity } } diff --git a/src/main/java/net/sparklypower/sparklypaper/HalloweenManager.java b/src/main/java/net/sparklypower/sparklypaper/HalloweenManager.java new file mode 100644 index 0000000000000000000000000000000000000000..f8c26e26025d7a7b5489ed5b3274ba734db27a1d --- /dev/null +++ b/src/main/java/net/sparklypower/sparklypaper/HalloweenManager.java @@ -0,0 +1,78 @@ +package net.sparklypower.sparklypaper; + +import com.mojang.logging.LogUtils; +import it.unimi.dsi.fastutil.Pair; +import net.minecraft.world.level.Level; +import org.slf4j.Logger; +import java.time.LocalDateTime; +import java.time.Month; +import java.time.ZoneOffset; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.plazmamc.plazma.configurations.GlobalConfiguration.get; + +public class HalloweenManager { + + private static final Logger LOGGER = LogUtils.getClassLogger(); + private static final ScheduledExecutorService EXECUTOR = Executors.newSingleThreadScheduledExecutor(factory -> { + Thread thread = new Thread(factory); + thread.setName("halloween-timer-updater"); + thread.setPriority(1); + return thread; + }); + + private static ScheduledFuture future; + private static Pair spookyEpoch; + private static Pair halloweenEpoch; + + private static long getEpochMillisAtDate(Month month, int day, boolean start) { + LocalDateTime now = LocalDateTime.now(); + LocalDateTime target = LocalDateTime.of( + now.getYear(), month, day, start ? 0 : 23, start ? 0 : 59, start ? 0 : 59, start ? 0 : 999_999_999 + ); + + if (now.isAfter(target)) target = target.plusYears(1); + return target.atZone(ZoneOffset.systemDefault()).toInstant().toEpochMilli(); + } + + private static void syncEpoch() { + LOGGER.info("Updating Spooky Season and Halloween epoch..."); + spookyEpoch = Pair.of( + getEpochMillisAtDate(Month.OCTOBER, 20, true), + getEpochMillisAtDate(Month.NOVEMBER, 3, false) + ); + halloweenEpoch = Pair.of( + getEpochMillisAtDate(Month.OCTOBER, 31, true), + getEpochMillisAtDate(Month.OCTOBER, 31, false) + ); + LOGGER.info("Successfully updated Spooky Season and Halloween epoch"); + } + + public static void syncConfiguration() { + if (get().entity.spookyOptimize && future == null) { + startSyncEpochTask(); + } else if (!get().entity.spookyOptimize && future != null) { + future.cancel(true); + future = null; + } + } + + public static void startSyncEpochTask() { + if (!get().entity.spookyOptimize) return; + future = EXECUTOR.scheduleAtFixedRate(HalloweenManager::syncEpoch, 0, 90, TimeUnit.DAYS); + } + + public static boolean isSpookySeason() { + return spookyEpoch.first() <= System.currentTimeMillis() && System.currentTimeMillis() <= spookyEpoch.second(); + } + + public static boolean isHalloween() { + return halloweenEpoch.first() <= System.currentTimeMillis() && System.currentTimeMillis() <= halloweenEpoch.second(); + } + +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java index 2c9726d0f2dec1136a2f45768e8f8169ce165a1d..4f199eea0c8a05f94e8443917c5a6f2c42f0b25e 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -976,7 +976,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { public void sendMultiBlockChange(final Map blockChanges) { if (this.getHandle().connection == null) return; - Map> sectionMap = new HashMap<>(); + Map> sectionMap = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(); // Plazma - Port SparklyPaper patches; Optimize canSee checks for (Map.Entry entry : blockChanges.entrySet()) { BlockData blockData = entry.getValue(); @@ -2237,9 +2237,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player { @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) || this.chunkMapCanSee(entity); // SPIGOT-7312: Can always see self // Plazma - Port SparklyPaper patches; Optimize canSee check } + // Plazma start - Port SparklyPaper patches; Optimize canSee check (The check in ChunkMap#updatePlayer already rejects if it is the same entity, so we don't need to check it twice, especially because CraftPlayer's equals check is a bit expensive) + public boolean chunkMapCanSee(org.bukkit.entity.Entity entity) { + return entity.isVisibleByDefault() ^ (!invertedVisibilityEntities.isEmpty() && this.invertedVisibilityEntities.containsKey(entity.getUniqueId())); + } + // Plazma end - Port SparklyPaper patches; Optimize canSee check + public boolean canSeePlayer(UUID uuid) { org.bukkit.entity.Entity entity = this.getServer().getPlayer(uuid); diff --git a/src/main/java/org/bukkit/craftbukkit/map/CraftMapColorCache.java b/src/main/java/org/bukkit/craftbukkit/map/CraftMapColorCache.java index 8149b9c51b78eb5c689b7218a2ca3aab60e73bcf..b9a303f6280a2f6ad3616da152922a4f4a504281 100644 --- a/src/main/java/org/bukkit/craftbukkit/map/CraftMapColorCache.java +++ b/src/main/java/org/bukkit/craftbukkit/map/CraftMapColorCache.java @@ -145,7 +145,7 @@ public class CraftMapColorCache implements MapPalette.MapColorCache { } @Override - public boolean isCached() { + public synchronized boolean isCached() { // Plazma - Fix concurrency issues when using "imageToBytes" in multiple threads return this.cached || (!this.running.get() && this.initCache().isDone()); } diff --git a/src/main/java/org/plazmamc/plazma/configurations/GlobalConfiguration.java b/src/main/java/org/plazmamc/plazma/configurations/GlobalConfiguration.java index 57d29f8f3bc89529db9ee8f6dc3fffdbd4a03ceb..73dd69941a004b4a2ec244f28d8683e9bdb0a445 100644 --- a/src/main/java/org/plazmamc/plazma/configurations/GlobalConfiguration.java +++ b/src/main/java/org/plazmamc/plazma/configurations/GlobalConfiguration.java @@ -40,9 +40,12 @@ public class GlobalConfiguration extends ConfigurationPart { public Entity entity; public class Entity extends ConfigurationPart { + boolean skipSqrWhenNoDeltaChanges = OPTIMIZE; + public boolean spookyOptimize = OPTIMIZE; @PostProcess public void post() { + net.minecraft.server.level.ServerEntity.skipSqrWhenNoDeltaChanges = this.skipSqrWhenNoDeltaChanges; } } @@ -50,9 +53,11 @@ public class GlobalConfiguration extends ConfigurationPart { public World world; public class World extends ConfigurationPart { + boolean skipTickWhenCraftNotPresent = OPTIMIZE; @PostProcess public void post() { + net.minecraft.world.item.MapItem.skipTickWhenCraftNotPresent = this.skipTickWhenCraftNotPresent; } } diff --git a/src/main/java/org/plazmamc/plazma/configurations/WorldConfigurations.java b/src/main/java/org/plazmamc/plazma/configurations/WorldConfigurations.java index 8dce68cf7769fcd5ea03be32621ccb6bab174697..069a9f8504c74d939b1df569f082cc7bd33d9cfc 100644 --- a/src/main/java/org/plazmamc/plazma/configurations/WorldConfigurations.java +++ b/src/main/java/org/plazmamc/plazma/configurations/WorldConfigurations.java @@ -49,7 +49,22 @@ public class WorldConfigurations extends ConfigurationPart { public Block block; public class Block extends ConfigurationPart { - + + public OptimizeFarmCheck optimizeFarmCheck; + public class OptimizeFarmCheck extends ConfigurationPart { + + public boolean enabled = OPTIMIZE; + public boolean skipMiddleAgingStageForCrops = true; + + public GrowthSpeed growthSpeed; + public class GrowthSpeed extends ConfigurationPart { + + public int normal = 1; + public int moist = 4; + + } + + } }