9
0
mirror of https://github.com/SparklyPower/SparklyPaper.git synced 2025-12-21 16:09:30 +00:00

Update to Paper 1.21.4 (post hard fork™️ edition)

This commit is contained in:
MrPowerGamerBR
2025-01-14 00:52:12 -03:00
parent 3e8eff82ea
commit 8e7b51f5d7
98 changed files with 3947 additions and 5536 deletions

View File

@@ -0,0 +1,94 @@
--- a/paper-server/build.gradle.kts
+++ b/paper-server/build.gradle.kts
@@ -4,6 +_,8 @@
plugins {
`java-library`
+ kotlin("jvm") version "2.0.0"
+ kotlin("plugin.serialization") version "2.0.0"
`maven-publish`
id("io.papermc.paperweight.core")
}
@@ -21,6 +_,21 @@
// macheOldPath = file("F:\\Projects\\PaperTooling\\mache\\versions\\1.21.4\\src\\main\\java")
// gitFilePatches = true
+ val fork = forks.register("sparklypaper") {
+ upstream.patchDir("paperServer") {
+ upstreamPath = "paper-server"
+ excludes = setOf("src/minecraft", "patches", "build.gradle.kts")
+ patchesDir = rootDirectory.dir("sparklypaper-server/paper-patches")
+ outputDir = rootDirectory.dir("paper-server")
+ }
+ }
+
+ activeFork = fork
+
+ paper {
+ paperServerDir = upstreamsDirectory().map { it.dir("paper/paper-server") }
+ }
+
spigot {
buildDataRef = "3edaf46ec1eed4115ce1b18d2846cded42577e42"
packageVersion = "v1_21_R3" // also needs to be updated in MappingEnvironment
@@ -101,7 +_,20 @@
}
}
-val log4jPlugins = sourceSets.create("log4jPlugins")
+sourceSets {
+ main {
+ java { srcDir("../paper-server/src/main/java") }
+ resources { srcDir("../paper-server/src/main/resources") }
+ }
+ test {
+ java { srcDir("../paper-server/src/test/java") }
+ resources { srcDir("../paper-server/src/test/resources") }
+ }
+}
+
+val log4jPlugins = sourceSets.create("log4jPlugins") {
+ java { srcDir("../paper-server/src/log4jPlugins/java") }
+}
configurations.named(log4jPlugins.compileClasspathConfigurationName) {
extendsFrom(configurations.compileClasspath.get())
}
@@ -119,7 +_,12 @@
}
dependencies {
- implementation(project(":paper-api"))
+ // SparklyPaper start
+ implementation(project(":sparklypaper-api"))
+ implementation(kotlin("reflect"))
+ implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
+ implementation("com.charleskorn.kaml:kaml:0.55.0")
+ // SparklyPaper end
implementation("ca.spottedleaf:concurrentutil:0.0.3")
implementation("org.jline:jline-terminal-ffm:3.27.1") // use ffm on java 22+
implementation("org.jline:jline-terminal-jni:3.27.1") // fall back to jni on java 21
@@ -183,7 +_,12 @@
val build = System.getenv("BUILD_NUMBER") ?: null
val buildTime = if (build != null) Instant.now() else Instant.EPOCH
val gitHash = git.exec(providers, "rev-parse", "--short=7", "HEAD").get().trim()
- val implementationVersion = "$mcVersion-${build ?: "DEV"}-$gitHash"
+ // SparklyPaper start
+ var implementationVersion = "$mcVersion-${build ?: "DEV"}-$gitHash"
+ if (project.hasProperty("sparklypaperImplementationVersionSuffix")) {
+ implementationVersion += "/${project.property("sparklypaperImplementationVersionSuffix")}"
+ }
+ // SparklyPaper end
val date = git.exec(providers, "show", "-s", "--format=%ci", gitHash).get().trim()
val gitBranch = git.exec(providers, "rev-parse", "--abbrev-ref", "HEAD").get().trim()
attributes(
@@ -194,8 +_,8 @@
"Specification-Title" to "Paper",
"Specification-Version" to project.version,
"Specification-Vendor" to "Paper Team",
- "Brand-Id" to "papermc:paper",
- "Brand-Name" to "Paper",
+ "Brand-Id" to "sparklypower:sparklypaper", // SparklyPaper
+ "Brand-Name" to "SparklyPaper", // SparklyPaper
"Build-Number" to (build ?: ""),
"Build-Time" to buildTime.toString(),
"Git-Branch" to gitBranch,

View File

@@ -0,0 +1,46 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Mon, 13 Jan 2025 13:20:31 -0300
Subject: [PATCH] SparklyPaper config files
diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java
index 97a294d2f5c1ddf0af7ffec3e1425eb329c5751b..4c420e17d936132bb4569776911757fd959e93d6 100644
--- a/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/net/minecraft/server/dedicated/DedicatedServer.java
@@ -224,6 +224,15 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
io.papermc.paper.command.PaperCommands.registerCommands(this); // Paper - setup /paper command
this.server.spark.registerCommandBeforePlugins(this.server); // Paper - spark
com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics(); // Paper - start metrics
+ // SparklyPaper start
+ try {
+ net.sparklypower.sparklypaper.configs.SparklyPaperConfigUtils.INSTANCE.init((java.io.File) options.valueOf("sparklypaper-settings"));
+ } catch (Exception e) {
+ DedicatedServer.LOGGER.error("Unable to load server configuration", e);
+ return false;
+ }
+ net.sparklypower.sparklypaper.SparklyPaperCommands.INSTANCE.registerCommands(this);
+ // SparklyPaper end
com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // Paper - load version history now
this.setPvpAllowed(properties.pvp);
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index 2bbebb4335d927f240abcac67a5b423e38dc33d7..27c2ea4a387ee5f7d078a42cb57f146cb748501d 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -170,6 +170,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
// Paper end - add paper world config
public final io.papermc.paper.antixray.ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray
+ public net.sparklypower.sparklypaper.configs.SparklyPaperConfig.SparklyPaperWorldConfig sparklyPaperConfig; // SparklyPaper
public static BlockPos lastPhysicsProblem; // Spigot
private org.spigotmc.TickLimiter entityLimiter;
private org.spigotmc.TickLimiter tileLimiter;
@@ -853,6 +854,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
// Paper end - getblock optimisations - cache world height/sections
this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) levelData).getLevelName()); // Spigot
this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config
+ this.sparklyPaperConfig = net.sparklypower.sparklypaper.configs.SparklyPaperConfigUtils.INSTANCE.getWorldSettings(((net.minecraft.world.level.storage.PrimaryLevelData) levelData).getLevelName()); // SparklyPaper
this.generator = gen;
this.world = new CraftWorld((ServerLevel) this, gen, biomeProvider, env);

View File

@@ -0,0 +1,30 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Mon, 13 Jan 2025 14:19:31 -0300
Subject: [PATCH] Skip "distanceToSqr" call in "ServerEntity#sendChanges" if
the delta movement hasn't changed
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 0fb253aa55a24b56b17f524b3261c5b75c7d7e59..8d0c259efc31de7de0ca5b900376677552e81f81 100644
--- a/net/minecraft/server/level/ServerEntity.java
+++ b/net/minecraft/server/level/ServerEntity.java
@@ -195,6 +195,7 @@ public class ServerEntity {
if (this.entity.hasImpulse || this.trackDelta || this.entity instanceof LivingEntity && ((LivingEntity)this.entity).isFallFlying()) {
Vec3 deltaMovement = this.entity.getDeltaMovement();
+ if (deltaMovement != this.lastSentMovement) { // SparklyPaper start - skip distanceToSqr call in ServerEntity#sendChanges if the delta movement hasn't changed
double d = deltaMovement.distanceToSqr(this.lastSentMovement);
if (d > 1.0E-7 || d > 0.0 && deltaMovement.lengthSqr() == 0.0) {
this.lastSentMovement = deltaMovement;
@@ -212,6 +213,7 @@ public class ServerEntity {
this.broadcast.accept(new ClientboundSetEntityMotionPacket(this.entity.getId(), this.lastSentMovement));
}
}
+ } // SparklyPaper end
}
if (packet != null) {

View File

@@ -0,0 +1,23 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Fri, 17 Nov 2023 14:22:41 -0300
Subject: [PATCH] Skip "MapItem#update()" if the map does not have the
CraftMapRenderer present
Optimizes "image in map" maps, without requiring the map to be locked, which some old map plugins may not do
This has the disadvantage that the vanilla map data will never be updated while the CraftMapRenderer is not present, but that's not a huuuge problem for us
diff --git a/net/minecraft/world/item/MapItem.java b/net/minecraft/world/item/MapItem.java
index 8795d54cff569c911e0a535f38a0ec4130f7b4d5..31960e54bf5ed4aa31cd12b60ab0fa263902ce2c 100644
--- a/net/minecraft/world/item/MapItem.java
+++ b/net/minecraft/world/item/MapItem.java
@@ -277,7 +277,7 @@ public class MapItem extends Item {
savedData.tickCarriedBy(player, stack);
}
- if (!savedData.locked && (isSelected || entity instanceof Player && ((Player)entity).getOffhandItem() == stack)) {
+ if (!savedData.locked && (!level.sparklyPaperConfig.getSkipMapItemDataUpdatesIfMapDoesNotHaveCraftMapRenderer() || savedData.mapView.getRenderers().stream().anyMatch(mapRenderer -> mapRenderer.getClass() == org.bukkit.craftbukkit.map.CraftMapRenderer.class)) && (isSelected || entity instanceof Player && ((Player)entity).getOffhandItem() == stack)) { // SparklyPaper - don't update maps if they don't have the CraftMapRenderer in the render list
this.update(level, entity, savedData);
}
}

View File

@@ -0,0 +1,40 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Mon, 13 Jan 2025 14:32:08 -0300
Subject: [PATCH] Skip dirty stats copy when requesting player stats
diff --git a/net/minecraft/stats/ServerStatsCounter.java b/net/minecraft/stats/ServerStatsCounter.java
index 6c5205fe1dc6387a77b1edbdcab748d39e775d7f..c6e4a7668ed923d93bd7f67d19af82d5d84a0dc0 100644
--- a/net/minecraft/stats/ServerStatsCounter.java
+++ b/net/minecraft/stats/ServerStatsCounter.java
@@ -81,11 +81,15 @@ public class ServerStatsCounter extends StatsCounter {
this.dirty.add(stat);
}
+ // SparklyPaper start - Skip dirty stats copy when requesting player stats
+ /*
private Set<Stat<?>> getDirty() {
Set<Stat<?>> set = Sets.newHashSet(this.dirty);
this.dirty.clear();
return set;
}
+ */
+ // SparklyPaper end
public void parseLocal(DataFixer fixerUpper, String json) {
try {
@@ -190,10 +194,12 @@ public class ServerStatsCounter extends StatsCounter {
public void sendStats(ServerPlayer player) {
Object2IntMap<Stat<?>> map = new Object2IntOpenHashMap<>();
- for (Stat<?> stat : this.getDirty()) {
+ for (Stat<?> stat : this.dirty) { // SparklyPaper - Skip dirty stats copy when requesting player stats
map.put(stat, this.getValue(stat));
}
+ this.dirty.clear(); // SparklyPaper - Skip dirty stats copy when requesting player stats
+
player.connection.send(new ClientboundAwardStatsPacket(map));
}
}

View File

@@ -0,0 +1,52 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Sun, 19 Nov 2023 12:35:16 -0300
Subject: [PATCH] Skip EntityScheduler's executeTick checks if there isn't any
tasks to be run
On each tick, Paper runs EntityScheduler's executeTick of each entity. This is a bit expensive, due to ArrayDeque's size() call because it ain't a simple "get the current queue size" function, due to the thread checks, and because it needs to iterate all entities in all worlds.
To avoid the hefty ArrayDeque's size() call, we check if we *really* need to execute the executeTick, by adding all entities with scheduled tasks to a global set.
Most entities won't have any scheduled tasks, so this is a nice performance bonus. These optimizations, however, wouldn't work in a Folia environment, but because in SparklyPaper executeTick is always executed on the main thread, it ain't an issue for us (yay).
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index ae220a732c78ab076261f20b5a54c71d7fceb407..6ad6bfe60026be304aa2178d5f04a79531cc66a6 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -303,6 +303,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
public boolean isIteratingOverLevels = false; // Paper - Throw exception on world create while being ticked
private final Set<String> pluginsBlockingSleep = new java.util.HashSet<>(); // Paper - API to allow/disallow tick sleeping
public static final long SERVER_INIT = System.nanoTime(); // Paper - Lag compensation
+ public final Set<net.minecraft.world.entity.Entity> entitiesWithScheduledTasks = java.util.concurrent.ConcurrentHashMap.newKeySet(); // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run (concurrent because plugins may schedule tasks async)
public static <S extends MinecraftServer> S spin(Function<Thread, S> threadFunction) {
ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system
@@ -1659,6 +1660,18 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.server.getScheduler().mainThreadHeartbeat(); // CraftBukkit
// Paper start - Folia scheduler API
((io.papermc.paper.threadedregions.scheduler.FoliaGlobalRegionScheduler) org.bukkit.Bukkit.getGlobalRegionScheduler()).tick();
+ // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run
+ for (final net.minecraft.world.entity.Entity entity : entitiesWithScheduledTasks) {
+ if (entity.isRemoved()) {
+ continue;
+ }
+
+ final org.bukkit.craftbukkit.entity.CraftEntity bukkit = entity.getBukkitEntityRaw();
+ if (bukkit != null) {
+ bukkit.taskScheduler.executeTick();
+ }
+ }
+ /*
getAllLevels().forEach(level -> {
for (final net.minecraft.world.entity.Entity entity : level.getEntities().getAll()) {
if (entity.isRemoved()) {
@@ -1670,6 +1683,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
}
});
+ */
+ // SparklyPaper end
// Paper end - Folia scheduler API
io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.CALLBACK_MANAGER.handleQueue(this.tickCount); // Paper
profilerFiller.push("commandFunctions");

View File

@@ -0,0 +1,180 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Mon, 13 Jan 2025 14:50:47 -0300
Subject: [PATCH] Blazingly Simple Farm Checks
diff --git a/net/minecraft/world/level/block/CropBlock.java b/net/minecraft/world/level/block/CropBlock.java
index bc0969f40814094e42a860a72314fccd1a66fabe..c14de811ae7cead820a00a87b9d2ce8cfb241d92 100644
--- a/net/minecraft/world/level/block/CropBlock.java
+++ b/net/minecraft/world/level/block/CropBlock.java
@@ -87,6 +87,57 @@ public class CropBlock extends BushBlock implements BonemealableBlock {
if (level.getRawBrightness(pos, 0) >= 9) {
int age = this.getAge(state);
if (age < this.getMaxAge()) {
+ // SparklyPaper start - Blazingly simple farm checks
+ if (level.sparklyPaperConfig.getBlazinglySimpleFarmChecks().getEnabled()) {
+ // These checks are similar to getGrowthSpeed, but we have "inlined" them because we want to access stuff like the farm block data later on
+ // Is the block below us moisturised?
+ BlockPos farmlandBelowTheCurrentBlock = pos.below();
+ BlockState farmlandBelowTheCurrentBlockData = level.getBlockState(farmlandBelowTheCurrentBlock);
+ float f = level.sparklyPaperConfig.getBlazinglySimpleFarmChecks().getDefaultGrowthSpeed();
+ boolean isCurrentFarmlandStateMoist = false;
+ if (farmlandBelowTheCurrentBlockData.is(Blocks.FARMLAND)) {
+ if ((Integer) farmlandBelowTheCurrentBlockData.getValue(FarmBlock.MOISTURE) > 0) {
+ // If we are currently moist, increase the speed!
+ f = level.sparklyPaperConfig.getBlazinglySimpleFarmChecks().getMoistGrowthSpeed();
+ isCurrentFarmlandStateMoist = true;
+ }
+ }
+ // If we are skipping the middle aging stages, we need to change the growth speed and the next stage accordingly
+ if (level.sparklyPaperConfig.getBlazinglySimpleFarmChecks().getSkipMiddleAgingStagesForCrops()) {
+ f = f / getMaxAge();
+ age = getMaxAge() - 1;
+ }
+
+ // Spigot start
+ int modifier;
+ if (this == Blocks.BEETROOTS) {
+ modifier = level.spigotConfig.beetrootModifier;
+ } else if (this == Blocks.CARROTS) {
+ modifier = level.spigotConfig.carrotModifier;
+ } else if (this == Blocks.POTATOES) {
+ modifier = level.spigotConfig.potatoModifier;
+ // Paper start
+ } else if (this == Blocks.TORCHFLOWER_CROP) {
+ modifier = level.spigotConfig.torchFlowerModifier;
+ // Paper end
+ } else {
+ modifier = level.spigotConfig.wheatModifier;
+ }
+
+ if (random.nextFloat() < (modifier / (100.0f * (Math.floor((25.0F / f) + 1))))) { // Spigot - SPIGOT-7159: Better modifier resolution
+ // Spigot end
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, pos, this.getStateForAge(age + 1), 2)) {
+ return;
+ }
+
+ // Now that we know that the crop will grow... is the next stage the crop's max age? If yes, we are going to check if the farm land is moist!
+ if (age + 1 == getMaxAge() && isCurrentFarmlandStateMoist && !FarmBlock.isNearWater(level, farmlandBelowTheCurrentBlock)) {
+ // Whoops, farm land ain't moist!
+ // From FarmBlock, set the moisture to 0
+ org.bukkit.craftbukkit.event.CraftEventFactory.handleMoistureChangeEvent(level, farmlandBelowTheCurrentBlock, (BlockState) farmlandBelowTheCurrentBlockData.setValue(FarmBlock.MOISTURE, 0), 2); // CraftBukkit
+ }
+ }
+ } else {
float growthSpeed = getGrowthSpeed(this, level, pos);
// Spigot start
int modifier = 100;
@@ -108,6 +159,8 @@ public class CropBlock extends BushBlock implements BonemealableBlock {
// Spigot end
org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, pos, this.getStateForAge(age + 1), 2); // CraftBukkit
}
+ }
+ // SparklyPaper end
}
}
}
diff --git a/net/minecraft/world/level/block/FarmBlock.java b/net/minecraft/world/level/block/FarmBlock.java
index 47c9b32c89e7e6f84a279c2f6098ada77dc58b6b..06a33e6f6118c2f2dce567efc2e23cbf877b7a9c 100644
--- a/net/minecraft/world/level/block/FarmBlock.java
+++ b/net/minecraft/world/level/block/FarmBlock.java
@@ -95,6 +95,19 @@ public class FarmBlock extends Block {
@Override
protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
int moistureValue = state.getValue(MOISTURE);
+ // SparklyPaper start - Blazingly simple farm checks
+ if (level.sparklyPaperConfig.getBlazinglySimpleFarmChecks().getEnabled()) {
+ if (moistureValue == 0) { // We only care about non-moisturised farm blocks
+ if (FarmBlock.isNearWater(level, pos)) {
+ // Make it MOIST!
+ org.bukkit.craftbukkit.event.CraftEventFactory.handleMoistureChangeEvent(level, pos, (BlockState) state.setValue(FarmBlock.MOISTURE, 7), 2); // CraftBukkit
+ } else if (!FarmBlock.shouldMaintainFarmland(level, pos)) {
+ FarmBlock.turnToDirt((Entity) null, state, level, pos);
+ }
+ }
+ return;
+ }
+ // SparklyPaper end
if (moistureValue > 0 && level.paperConfig().tickRates.wetFarmland != 1 && (level.paperConfig().tickRates.wetFarmland < 1 || (net.minecraft.server.MinecraftServer.currentTick + pos.hashCode()) % level.paperConfig().tickRates.wetFarmland != 0)) { return; } // Paper - Configurable random tick rates for blocks
if (moistureValue == 0 && level.paperConfig().tickRates.dryFarmland != 1 && (level.paperConfig().tickRates.dryFarmland < 1 || (net.minecraft.server.MinecraftServer.currentTick + pos.hashCode()) % level.paperConfig().tickRates.dryFarmland != 0)) { return; } // Paper - Configurable random tick rates for blocks
if (!isNearWater(level, pos) && !level.isRainingAt(pos.above())) {
@@ -154,7 +167,7 @@ public class FarmBlock extends Block {
return level.getBlockState(pos.above()).is(BlockTags.MAINTAINS_FARMLAND);
}
- private static boolean isNearWater(LevelReader level, BlockPos pos) {
+ public static boolean isNearWater(LevelReader level, BlockPos pos) { // SparklyPaper - make public for the Blazingly simple farm checks
// Paper start - Perf: remove abstract block iteration
int xOff = pos.getX();
int yOff = pos.getY();
diff --git a/net/minecraft/world/level/block/StemBlock.java b/net/minecraft/world/level/block/StemBlock.java
index 3dca57dd0c00dc64724cbc7f5a71963da9d12fd5..9839821365484bac93023b1aa542619dae4c88cf 100644
--- a/net/minecraft/world/level/block/StemBlock.java
+++ b/net/minecraft/world/level/block/StemBlock.java
@@ -79,6 +79,56 @@ public class StemBlock extends BushBlock implements BonemealableBlock {
@Override
protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
if (level.getRawBrightness(pos, 0) >= 9) {
+ // SparklyPaper start - Blazingly simple farm checks
+ if (level.sparklyPaperConfig.getBlazinglySimpleFarmChecks().getEnabled()) {
+ // These checks are similar to getGrowthSpeed, but we have "inlined" them because we want to access stuff like the farm block data later on
+ // Is the block below us moisturised?
+ BlockPos farmlandBelowTheCurrentBlock = pos.below();
+ BlockState farmlandBelowTheCurrentBlockData = level.getBlockState(farmlandBelowTheCurrentBlock);
+ float f = level.sparklyPaperConfig.getBlazinglySimpleFarmChecks().getDefaultGrowthSpeed();
+ boolean isCurrentFarmlandStateMoist = false;
+ if (farmlandBelowTheCurrentBlockData.is(Blocks.FARMLAND)) {
+ if ((Integer) farmlandBelowTheCurrentBlockData.getValue(FarmBlock.MOISTURE) > 0) {
+ // If we are currently moist, increase the speed!
+ f = level.sparklyPaperConfig.getBlazinglySimpleFarmChecks().getMoistGrowthSpeed();
+ isCurrentFarmlandStateMoist = true;
+ }
+ }
+
+ if (random.nextFloat() < ((this == Blocks.PUMPKIN_STEM ? level.spigotConfig.pumpkinModifier : level.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);
+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, pos, state, 2); // CraftBukkit
+ } else {
+ Direction enumdirection = Direction.Plane.HORIZONTAL.getRandomDirection(random);
+ BlockPos blockposition1 = pos.relative(enumdirection);
+ BlockState iblockdata1 = level.getBlockState(blockposition1.below());
+
+ if (level.getBlockState(blockposition1).isAir() && (iblockdata1.is(Blocks.FARMLAND) || iblockdata1.is(BlockTags.DIRT))) {
+ Registry<Block> iregistry = net.minecraft.core.registries.BuiltInRegistries.BLOCK;
+ Optional<Block> optional = iregistry.getOptional(this.fruit);
+ Optional<Block> optional1 = iregistry.getOptional(this.attachedStem);
+
+ if (optional.isPresent() && optional1.isPresent()) {
+ // CraftBukkit start
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, blockposition1, ((Block) optional.get()).defaultBlockState())) {
+ return;
+ }
+ // CraftBukkit end
+ // Now that we know that the crop will grow... is the next stage the crop's max age? If yes, we are going to check if the farm land is moist!
+ if (isCurrentFarmlandStateMoist && !FarmBlock.isNearWater(level, farmlandBelowTheCurrentBlock)) {
+ // Whoops, farm land ain't moist!
+ // From FarmBlock, set the moisture to 0
+ org.bukkit.craftbukkit.event.CraftEventFactory.handleMoistureChangeEvent(level, farmlandBelowTheCurrentBlock, (BlockState) farmlandBelowTheCurrentBlockData.setValue(FarmBlock.MOISTURE, 0), 2); // CraftBukkit
+ }
+ level.setBlockAndUpdate(pos, (BlockState) ((Block) optional1.get()).defaultBlockState().setValue(HorizontalDirectionalBlock.FACING, enumdirection));
+ }
+ }
+ }
+ }
+ } else {
float growthSpeed = CropBlock.getGrowthSpeed(this, level, pos);
if (random.nextFloat() < ((this == Blocks.PUMPKIN_STEM ? level.spigotConfig.pumpkinModifier : level.spigotConfig.melonModifier) / (100.0f * (Math.floor((25.0F / growthSpeed) + 1))))) { // Spigot - SPIGOT-7159: Better modifier resolution
int ageValue = state.getValue(AGE);
@@ -105,6 +155,8 @@ public class StemBlock extends BushBlock implements BonemealableBlock {
}
}
}
+ }
+ // SparklyPaper end
}
@Override

View File

@@ -0,0 +1,109 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Tue, 21 Nov 2023 16:50:04 -0300
Subject: [PATCH] Spooky month optimizations
The quintessential patch that other performance forks also have for... some reason??? I thought that this optimization was too funny to not do it in SparklyPaper.
Caches when Bat's spooky season starts and ends, and when Skeleton and Zombies halloween starts and ends. The epoch is updated every 90 days. If your server is running for 90+ days straight without restarts, congratulations!
Avoids unnecessary date checks, even tho that this shouldn't really improve performance that much... unless you have a lot of bats/zombies/skeletons spawning.
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 6ad6bfe60026be304aa2178d5f04a79531cc66a6..4dbf2d9aa52029122f4816ca3504fa330b28a29b 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -304,6 +304,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
private final Set<String> pluginsBlockingSleep = new java.util.HashSet<>(); // Paper - API to allow/disallow tick sleeping
public static final long SERVER_INIT = System.nanoTime(); // Paper - Lag compensation
public final Set<net.minecraft.world.entity.Entity> entitiesWithScheduledTasks = java.util.concurrent.ConcurrentHashMap.newKeySet(); // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run (concurrent because plugins may schedule tasks async)
+ public net.sparklypower.sparklypaper.HalloweenManager halloweenManager = new net.sparklypower.sparklypaper.HalloweenManager(); // SparklyPaper - Spooky month optimizations
public static <S extends MinecraftServer> S spin(Function<Thread, S> threadFunction) {
ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system
diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java
index 4c420e17d936132bb4569776911757fd959e93d6..6ac73610f6039290c4ef3dde2206b6863d2b5067 100644
--- a/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/net/minecraft/server/dedicated/DedicatedServer.java
@@ -233,6 +233,10 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
}
net.sparklypower.sparklypaper.SparklyPaperCommands.INSTANCE.registerCommands(this);
// SparklyPaper end
+ // SparklyPaper start - Spooky month optimizations
+ halloweenManager.startHalloweenEpochTask();
+ halloweenManager.waitUntilEpochHasBeenUpdated();
+ // SparklyPaper end
com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // Paper - load version history now
this.setPvpAllowed(properties.pvp);
diff --git a/net/minecraft/world/entity/ambient/Bat.java b/net/minecraft/world/entity/ambient/Bat.java
index 5ebe7b1dce367d5c5e1136b97b2b9f6737595201..b003fa86fab9a6106c9875205d73e49e46904498 100644
--- a/net/minecraft/world/entity/ambient/Bat.java
+++ b/net/minecraft/world/entity/ambient/Bat.java
@@ -231,7 +231,7 @@ public class Bat extends AmbientCreature {
} else {
int maxLocalRawBrightness = level.getMaxLocalRawBrightness(pos);
int i = 4;
- if (isHalloween()) {
+ if (level.getServer().halloweenManager.isSpookySeason()) {
i = 7;
} else if (randomSource.nextBoolean()) {
return false;
@@ -243,12 +243,15 @@ public class Bat extends AmbientCreature {
}
}
+ // SparklyPaper - Spooky month optimizations
+ /*
private static boolean isHalloween() {
LocalDate localDate = LocalDate.now();
int i = localDate.get(ChronoField.DAY_OF_MONTH);
int i1 = localDate.get(ChronoField.MONTH_OF_YEAR);
return i1 == 10 && i >= 20 || i1 == 11 && i <= 3;
}
+ */
private void setupAnimationStates() {
if (this.isResting()) {
diff --git a/net/minecraft/world/entity/monster/AbstractSkeleton.java b/net/minecraft/world/entity/monster/AbstractSkeleton.java
index 37abc7769573e3cdda380166dd086551d5e7bd88..be623fd09ca573922297d3af3cd114160ec05061 100644
--- a/net/minecraft/world/entity/monster/AbstractSkeleton.java
+++ b/net/minecraft/world/entity/monster/AbstractSkeleton.java
@@ -158,10 +158,12 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo
this.reassessWeaponGoal();
this.setCanPickUpLoot(this.level().paperConfig().entities.behavior.mobsCanAlwaysPickUpLoot.skeletons || random.nextFloat() < 0.55F * difficulty.getSpecialMultiplier()); // Paper - Add world settings for mobs picking up loot
if (this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) {
- LocalDate localDate = LocalDate.now();
- int i = localDate.get(ChronoField.DAY_OF_MONTH);
- int i1 = localDate.get(ChronoField.MONTH_OF_YEAR);
- if (i1 == 10 && i == 31 && random.nextFloat() < 0.25F) {
+ // SparklyPaper start - Spooky month optimizations
+ // LocalDate localDate = LocalDate.now();
+ // int i = localDate.get(ChronoField.DAY_OF_MONTH);
+ // int i1 = localDate.get(ChronoField.MONTH_OF_YEAR);
+ if (this.getServer().halloweenManager.isHalloween() /* i1 == 10 && i == 31 */&& random.nextFloat() < 0.25F) {
+ // SparklyPaper end
this.setItemSlot(EquipmentSlot.HEAD, new ItemStack(random.nextFloat() < 0.1F ? Blocks.JACK_O_LANTERN : Blocks.CARVED_PUMPKIN));
this.armorDropChances[EquipmentSlot.HEAD.getIndex()] = 0.0F;
}
diff --git a/net/minecraft/world/entity/monster/Zombie.java b/net/minecraft/world/entity/monster/Zombie.java
index 637790ff833abaa0c52fdee204abba7077d12ccc..600b96d4b2645e26fd690ac70d3901d567b7bc24 100644
--- a/net/minecraft/world/entity/monster/Zombie.java
+++ b/net/minecraft/world/entity/monster/Zombie.java
@@ -550,10 +550,12 @@ public class Zombie extends Monster {
}
if (this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) {
- LocalDate localDate = LocalDate.now();
- int i = localDate.get(ChronoField.DAY_OF_MONTH);
- int i1 = localDate.get(ChronoField.MONTH_OF_YEAR);
- if (i1 == 10 && i == 31 && random.nextFloat() < 0.25F) {
+ // SparklyPaper start - Spooky month optimizations
+ // LocalDate localDate = LocalDate.now();
+ // int i = localDate.get(ChronoField.DAY_OF_MONTH);
+ // int i1 = localDate.get(ChronoField.MONTH_OF_YEAR);
+ if (this.getServer().halloweenManager.isHalloween() /* i1 == 10 && i == 31 */&& random.nextFloat() < 0.25F) {
+ // SparklyPaper end
this.setItemSlot(EquipmentSlot.HEAD, new ItemStack(random.nextFloat() < 0.1F ? Blocks.JACK_O_LANTERN : Blocks.CARVED_PUMPKIN));
this.armorDropChances[EquipmentSlot.HEAD.getIndex()] = 0.0F;
}

View File

@@ -0,0 +1,19 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Mon, 13 Jan 2025 15:03:52 -0300
Subject: [PATCH] Optimize canSee checks
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
index b3f498558614243cf633dcd71e3c49c2c55e6e0f..ad9ab7fe6df3880c730388a50741b7938b037ff2 100644
--- a/net/minecraft/server/level/ChunkMap.java
+++ b/net/minecraft/server/level/ChunkMap.java
@@ -1232,7 +1232,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
flag = flag && this.entity.broadcastToPlayer(player) && ChunkMap.this.isChunkTracked(player, this.entity.chunkPosition().x, this.entity.chunkPosition().z);
// Paper end - Configurable entity tracking range by Y
// CraftBukkit start - respect vanish API
- if (flag && !player.getBukkitEntity().canSee(this.entity.getBukkitEntity())) { // Paper - only consider hits
+ if (flag && !player.getBukkitEntity().canSeeChunkMapUpdatePlayer(this.entity.getBukkitEntity())) { // Paper - only consider hits // SparklyPaper - optimize canSee checks
flag = false;
}
// CraftBukkit end

View File

@@ -0,0 +1,39 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Mon, 13 Jan 2025 16:41:37 -0300
Subject: [PATCH] Fix MC-117075: TE Unload Lag Spike
We replaced the `blockEntityTickers` list with a custom list based on fastutil's `ObjectArrayList` with a small yet huge change for us: A method that allows us to remove a list of indexes from the list.
This is WAY FASTER than using `removeAll` with a list of entries to be removed, because we don't need to calculate the identity of each block entity to be removed, and we can jump directly to where the search should begin, giving a performance boost for small removals (because we don't need to loop thru the entire list to find what element should be removed) and a performance boost for big removals (no need to calculate the identity of each block entity).
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index 27c2ea4a387ee5f7d078a42cb57f146cb748501d..644ab8b2c797f0675530a3efe23a878899689d66 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -115,7 +115,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
public static final int TICKS_PER_DAY = 24000;
public static final int MAX_ENTITY_SPAWN_Y = 20000000;
public static final int MIN_ENTITY_SPAWN_Y = -20000000;
- public final List<TickingBlockEntity> blockEntityTickers = Lists.newArrayList(); // Paper - public
+ public final net.sparklypower.sparklypaper.BlockEntityTickersList blockEntityTickers = new net.sparklypower.sparklypaper.BlockEntityTickersList(); // Paper - public // SparklyPaper - optimize block entity removals
protected final NeighborUpdater neighborUpdater;
private final List<TickingBlockEntity> pendingBlockEntityTickers = Lists.newArrayList();
private boolean tickingBlockEntities;
@@ -1479,7 +1479,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
TickingBlockEntity tickingBlockEntity = this.blockEntityTickers.get(this.tileTickPosition);
// Spigot end
if (tickingBlockEntity.isRemoved()) {
- toRemove.add(tickingBlockEntity); // Paper - Fix MC-117075; use removeAll
+ this.blockEntityTickers.markAsRemoved(this.tileTickPosition); // toRemove.add(tickingBlockEntity); // SparklyPaper - optimize block entity removals // Paper - Fix MC-117075; use removeAll
} else if (runsNormally && this.shouldTickBlocksAt(tickingBlockEntity.getPos())) {
tickingBlockEntity.tick();
// Paper start - rewrite chunk system
@@ -1491,6 +1491,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
}
this.blockEntityTickers.removeAll(toRemove); // Paper - Fix MC-117075
+ this.blockEntityTickers.removeMarkedEntries(); // SparklyPaper - optimize block entity removals
this.tickingBlockEntities = false;
profilerFiller.pop();
this.spigotConfig.currentPrimedTnt = 0; // Spigot

View File

@@ -0,0 +1,44 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Mon, 6 Nov 2023 21:54:33 -0300
Subject: [PATCH] Track how much MSPT each world used
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 4dbf2d9aa52029122f4816ca3504fa330b28a29b..c15c11ab3feaca918ac0185383cd1653200cd186 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1737,7 +1737,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
profilerFiller.push("tick");
try {
+ long i = Util.getNanos(); // SparklyPaper - track world's MSPT
serverLevel.tick(hasTimeLeft);
+ // SparklyPaper start - track world's MSPT
+ long j = Util.getNanos() - i;
+
+ // These are from the "tickServer" function
+ serverLevel.tickTimes5s.add(this.tickCount, j);
+ serverLevel.tickTimes10s.add(this.tickCount, j);
+ serverLevel.tickTimes60s.add(this.tickCount, j);
+ // SparklyPaper end
} catch (Throwable var7) {
CrashReport crashReport = CrashReport.forThrowable(var7, "Exception ticking world");
serverLevel.fillReportDetails(crashReport);
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index ebeeb63c3dca505a3ce8b88feaa5d2ca20ec24a2..59465ab6d7723d27b1597cc7094b5044a3f24250 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -570,6 +570,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
}
// Paper end - chunk tick iteration
+ // SparklyPaper start - track world's 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);
+ // SparklyPaper end
+
public ServerLevel(
MinecraftServer server,
Executor dispatcher,

View File

@@ -0,0 +1,21 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Wed, 5 Jun 2024 15:20:00 -0300
Subject: [PATCH] Reset dirty flag when loading maps from the disk
By default, the server will start rewriting all map datas to the disk after loading it, even if the map didn't have any changes
This also slows down world saving a lot if you have a lot of maps
diff --git a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java b/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
index f5a131e870a4f1ad06ebfb1f360720cf19656fb5..2aed9d55ea1a93b323865141a69c9947759eae29 100644
--- a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
+++ b/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
@@ -197,6 +197,7 @@ public class MapItemSavedData extends SavedData {
}
}
+ mapItemSavedData.setDirty(false); // SparklyPaper - reset dirty flag when loading maps from the disk (context for updates: this modification is at the end of the map "load" function)
return mapItemSavedData;
}

View File

@@ -0,0 +1,144 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Mon, 10 Jun 2024 13:06:30 -0300
Subject: [PATCH] Helpful NMS packet changes
Some nice changes to the packet internals to make packet sending and manipulation easier for us to avoid Reflection and JVM internals (ooo theUnsafe spooky) usage
diff --git a/net/minecraft/network/protocol/game/ClientboundAddEntityPacket.java b/net/minecraft/network/protocol/game/ClientboundAddEntityPacket.java
index 1373977b339405ef59bb3ea03d195285c96dd3fe..20a782d1bb7355bbdc0822a02204e1e05dc44145 100644
--- a/net/minecraft/network/protocol/game/ClientboundAddEntityPacket.java
+++ b/net/minecraft/network/protocol/game/ClientboundAddEntityPacket.java
@@ -22,7 +22,7 @@ public class ClientboundAddEntityPacket implements Packet<ClientGamePacketListen
private static final double LIMIT = 3.9;
private final int id;
private final UUID uuid;
- private final EntityType<?> type;
+ public EntityType<?> type; // SparklyPaper - Helpful NMS packet changes: remove final and make public
private final double x;
private final double y;
private final double z;
@@ -180,6 +180,32 @@ public class ClientboundAddEntityPacket implements Packet<ClientGamePacketListen
return Mth.unpackDegrees(this.yHeadRot);
}
+ // SparklyPaper - Helpful NMS packet changes: expose raw rotational fields
+ public int getXaRaw() {
+ return this.xa;
+ }
+
+ public int getYaRaw() {
+ return this.ya;
+ }
+
+ public int getZaRaw() {
+ return this.za;
+ }
+
+ public byte getXRotRaw() {
+ return this.xRot;
+ }
+
+ public byte getYRotRaw() {
+ return this.yRot;
+ }
+
+ public byte getYHeadRotRaw() {
+ return this.yHeadRot;
+ }
+ // SparklyPaper end
+
public int getData() {
return this.data;
}
diff --git a/net/minecraft/network/protocol/game/ClientboundBlockUpdatePacket.java b/net/minecraft/network/protocol/game/ClientboundBlockUpdatePacket.java
index 8fca836e63731c5cef95bbc07e6e1414c9f02bea..c412dcab882abe11ecae34700df3e62ece464b82 100644
--- a/net/minecraft/network/protocol/game/ClientboundBlockUpdatePacket.java
+++ b/net/minecraft/network/protocol/game/ClientboundBlockUpdatePacket.java
@@ -19,7 +19,7 @@ public class ClientboundBlockUpdatePacket implements Packet<ClientGamePacketList
ClientboundBlockUpdatePacket::new
);
private final BlockPos pos;
- public final BlockState blockState;
+ public BlockState blockState; // SparklyPaper - Helpful NMS packet changes: remove final
public ClientboundBlockUpdatePacket(BlockPos pos, BlockState blockState) {
this.pos = pos;
diff --git a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
index 9e321ef1c3d5803519b243685f4ee598dc0cf640..851931062c4401817aad14721ac7fb0362c3b418 100644
--- a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
+++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
@@ -25,7 +25,7 @@ import net.minecraft.world.level.levelgen.Heightmap;
public class ClientboundLevelChunkPacketData {
private static final int TWO_MEGABYTES = 2097152;
private final CompoundTag heightmaps;
- private final byte[] buffer;
+ public byte[] buffer; // SparklyPaper - Helpful NMS packet changes: remove final and make public
private final List<ClientboundLevelChunkPacketData.BlockEntityInfo> blockEntitiesData;
// Paper start - Handle oversized block entities in chunks
private final java.util.List<net.minecraft.network.protocol.Packet<?>> extraPackets = new java.util.ArrayList<>();
diff --git a/net/minecraft/network/protocol/game/ClientboundRotateHeadPacket.java b/net/minecraft/network/protocol/game/ClientboundRotateHeadPacket.java
index 4bcbf4bff67d4d4958d56764a273cf4a66b9dd0c..fd621a5d02170f6aa297dbd6a08ba1fef60e54c9 100644
--- a/net/minecraft/network/protocol/game/ClientboundRotateHeadPacket.java
+++ b/net/minecraft/network/protocol/game/ClientboundRotateHeadPacket.java
@@ -20,6 +20,13 @@ public class ClientboundRotateHeadPacket implements Packet<ClientGamePacketListe
this.yHeadRot = yHeadRot;
}
+ // SparklyPaper start - Helpful NMS packet changes: add entity ID constructor
+ public ClientboundRotateHeadPacket(int entityId, byte headYaw) {
+ this.entityId = entityId;
+ this.yHeadRot = headYaw;
+ }
+ // SparklyPaper end
+
private ClientboundRotateHeadPacket(FriendlyByteBuf buffer) {
this.entityId = buffer.readVarInt();
this.yHeadRot = buffer.readByte();
diff --git a/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java b/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java
index 10b46c59d83d1274cf491f217df2355d13b594c1..de8767eb8a01e1fed06fbb9d8d5904a83fd19bf5 100644
--- a/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java
+++ b/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java
@@ -17,9 +17,9 @@ public class ClientboundSectionBlocksUpdatePacket implements Packet<ClientGamePa
ClientboundSectionBlocksUpdatePacket::write, ClientboundSectionBlocksUpdatePacket::new
);
private static final int POS_IN_SECTION_BITS = 12;
- private final SectionPos sectionPos;
- private final short[] positions;
- private final BlockState[] states;
+ public SectionPos sectionPos; // SparklyPaper - Helpful NMS packet changes: remove final and make public
+ public short[] positions; // SparklyPaper - Helpful NMS packet changes: remove final and make public
+ public BlockState[] states; // SparklyPaper - Helpful NMS packet changes: remove final and make public
public ClientboundSectionBlocksUpdatePacket(SectionPos sectionPos, ShortSet positions, LevelChunkSection section) {
this.sectionPos = sectionPos;
diff --git a/net/minecraft/network/protocol/game/ClientboundSetCameraPacket.java b/net/minecraft/network/protocol/game/ClientboundSetCameraPacket.java
index 2545057c7f1f355f59f1e7da06ce08fd2d997b43..9920fe2d2f2f74f372731f1be5abaa4953344632 100644
--- a/net/minecraft/network/protocol/game/ClientboundSetCameraPacket.java
+++ b/net/minecraft/network/protocol/game/ClientboundSetCameraPacket.java
@@ -22,6 +22,12 @@ public class ClientboundSetCameraPacket implements Packet<ClientGamePacketListen
this.cameraId = buffer.readVarInt();
}
+ // SparklyPaper - Helpful NMS packet changes: add direct entityId constructor
+ public ClientboundSetCameraPacket(int entityId) {
+ this.cameraId = entityId;
+ }
+ // SparklyPaper end
+
private void write(FriendlyByteBuf buffer) {
buffer.writeVarInt(this.cameraId);
}
diff --git a/net/minecraft/world/entity/Display.java b/net/minecraft/world/entity/Display.java
index ff49fdcddb43ee479a377aa219eb18fcaefeffca..33a47487f79fdc5164ff6a6d639dfe5834deed0d 100644
--- a/net/minecraft/world/entity/Display.java
+++ b/net/minecraft/world/entity/Display.java
@@ -800,7 +800,7 @@ public abstract class Display extends Entity {
public static final byte FLAG_ALIGN_RIGHT = 16;
private static final byte INITIAL_TEXT_OPACITY = -1;
public static final int INITIAL_BACKGROUND = 1073741824;
- private static final EntityDataAccessor<Component> DATA_TEXT_ID = SynchedEntityData.defineId(Display.TextDisplay.class, EntityDataSerializers.COMPONENT);
+ public static final EntityDataAccessor<Component> DATA_TEXT_ID = SynchedEntityData.defineId(Display.TextDisplay.class, EntityDataSerializers.COMPONENT); // SparklyPaper - Helpful NMS packet changes: make public
public static final EntityDataAccessor<Integer> DATA_LINE_WIDTH_ID = SynchedEntityData.defineId(Display.TextDisplay.class, EntityDataSerializers.INT);
public static final EntityDataAccessor<Integer> DATA_BACKGROUND_COLOR_ID = SynchedEntityData.defineId(
Display.TextDisplay.class, EntityDataSerializers.INT

View File

@@ -0,0 +1,51 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Tue, 25 Jun 2024 02:52:32 -0300
Subject: [PATCH] Add CraftItemRecipeEvent
Used when a player OR a crafter block crafts an item, as an alternative to PrepareItemCraftEvent and CraftItemEvent, because both events are not triggered when a item is crafted from a crafter
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 27ef385a85b13ceb58e8d149849983107c539b31..7f9ed94576c94161070fd1be1544aaed9aac0c6b 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -3156,6 +3156,21 @@ public class ServerGamePacketListenerImpl
} else {
event = new CraftItemEvent(recipe, inventory, type, slotNum, click, action);
}
+ // SparklyPaper start - add CraftItemRecipeEvent
+ // We will pigback a bit on the current implementation
+ net.sparklypower.sparklypaper.event.inventory.CraftItemRecipeEvent craftItemRecipeEvent = new net.sparklypower.sparklypaper.event.inventory.CraftItemRecipeEvent(
+ ((CraftingInventory) top).getMatrix(), // We cannot use the top inventory directly because (it seems) that the first slot is the "result" slot
+ recipe,
+ event.getCurrentItem()
+ );
+ if (craftItemRecipeEvent.callEvent()) {
+ event.setCurrentItem(craftItemRecipeEvent.getResult());
+ } else {
+ event.setCancelled(true);
+ cancelled = true;
+ event.setCurrentItem(craftItemRecipeEvent.getResult());
+ }
+ // SparklyPaper end
}
}
diff --git a/net/minecraft/world/level/block/CrafterBlock.java b/net/minecraft/world/level/block/CrafterBlock.java
index 5f5966278faf86ed9b28955c80ba845c0cb75595..751969d28951f59f381aa6bf7ad42d55d3e84d57 100644
--- a/net/minecraft/world/level/block/CrafterBlock.java
+++ b/net/minecraft/world/level/block/CrafterBlock.java
@@ -167,6 +167,13 @@ public class CrafterBlock extends BaseEntityBlock {
}
itemStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getResult());
// CraftBukkit end
+ // SparklyPaper - add CraftItemRecipeEvent
+ net.sparklypower.sparklypaper.event.inventory.CraftItemRecipeEvent sparklyEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callCraftItemRecipeEvent(crafterBlockEntity, recipeHolder.toBukkitRecipe(), itemStack);
+ if (sparklyEvent.isCancelled()) {
+ return;
+ }
+ itemStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(sparklyEvent.getResult());
+ // SparklyPaper end
if (itemStack.isEmpty()) {
level.levelEvent(1050, pos, 0);
} else {

View File

@@ -0,0 +1,19 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Fri, 23 Aug 2024 16:20:45 -0300
Subject: [PATCH] Allow throttling hopper checks if the target container is
full
diff --git a/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/net/minecraft/world/level/block/entity/HopperBlockEntity.java
index 5cd1326ad5d046c88b2b3449d610a78fa880b4cd..d512f44dcfc480b5608555aa319c93342ae5b208 100644
--- a/net/minecraft/world/level/block/entity/HopperBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/HopperBlockEntity.java
@@ -419,6 +419,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
} else {
Direction opposite = blockEntity.facing.getOpposite();
if (isFullContainer(attachedContainer, opposite)) {
+ if (level.sparklyPaperConfig.getTicksPer().getHopperCooldownWhenTargetContainerIsFull() != 0) blockEntity.setCooldown(level.sparklyPaperConfig.getTicksPer().getHopperCooldownWhenTargetContainerIsFull()); // SparklyPaper - Allow throttling hopper checks if the target container is full
return false;
} else {
// Paper start - Perf: Optimize Hoppers

View File

@@ -0,0 +1,42 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Sat, 4 Jan 2025 23:58:55 -0300
Subject: [PATCH] Add PlayerMoveControllableVehicleEvent
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 7f9ed94576c94161070fd1be1544aaed9aac0c6b..da190c2ab2080344dc11903aec51598af18d7556 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -587,6 +587,31 @@ public class ServerGamePacketListenerImpl
LOGGER.warn("{} (vehicle of {}) moved wrongly! {}", rootVehicle.getName().getString(), this.player.getName().getString(), Math.sqrt(d7));
}
+ // SparklyPaper start - Add PlayerMoveControllableVehicleEvent
+ org.bukkit.entity.Player craftPlayer = this.getCraftPlayer();
+ org.bukkit.entity.Entity bukkitVehicle = rootVehicle.getBukkitEntity();
+ if (bukkitVehicle instanceof org.bukkit.entity.Vehicle) {
+ net.sparklypower.sparklypaper.event.player.PlayerMoveControllableVehicleEvent playerMoveControllableVehicleEvent = new net.sparklypower.sparklypaper.event.player.PlayerMoveControllableVehicleEvent(
+ craftPlayer,
+ (org.bukkit.entity.Vehicle) bukkitVehicle,
+ new org.bukkit.Location(craftPlayer.getWorld(), x, y, z, f, f1),
+ new org.bukkit.Location(craftPlayer.getWorld(), d, d1, d2, f, f1)
+ );
+ if (!playerMoveControllableVehicleEvent.callEvent()) {
+ // Cancelled, move back!
+ rootVehicle.absMoveTo(x, y, z, f, f1);
+ this.player.absMoveTo(x, y, z, this.player.getYRot(), this.player.getXRot()); // CraftBukkit
+ this.send(ClientboundMoveVehiclePacket.fromEntity(rootVehicle));
+ return;
+ }
+ d = playerMoveControllableVehicleEvent.getTo().x();
+ d1 = playerMoveControllableVehicleEvent.getTo().y();
+ d2 = playerMoveControllableVehicleEvent.getTo().z();
+ f = playerMoveControllableVehicleEvent.getTo().getYaw();
+ f1 = playerMoveControllableVehicleEvent.getTo().getPitch();
+ }
+ // SparklyPaper end
+
rootVehicle.absMoveTo(d, d1, d2, f, f1);
this.player.absMoveTo(d, d1, d2, this.player.getYRot(), this.player.getXRot()); // CraftBukkit
// Paper start - optimise out extra getCubes

View File

@@ -0,0 +1,86 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Wed, 8 Jan 2025 22:48:04 -0300
Subject: [PATCH] Add PlayerPreMoveEvent
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index da190c2ab2080344dc11903aec51598af18d7556..f637d2be3d67a928ed7f78eaa7aa80cbea85a3e6 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -1422,7 +1422,7 @@ public class ServerGamePacketListenerImpl
d3 = d - this.lastGoodX; // Paper - diff on change, used for checking large move vectors above
d4 = d1 - this.lastGoodY; // Paper - diff on change, used for checking large move vectors above
d5 = d2 - this.lastGoodZ; // Paper - diff on change, used for checking large move vectors above
- boolean flag = d4 > 0.0;
+ boolean flag = d4 > 0.0; // SparklyPaper - diff on change, used to reset the fall distance, checks if d7 is > 0.0D (player is moving upwards)
if (this.player.onGround() && !packet.isOnGround() && flag) {
// Paper start - Add PlayerJumpEvent
org.bukkit.entity.Player player = this.getCraftPlayer();
@@ -1456,7 +1456,37 @@ public class ServerGamePacketListenerImpl
boolean flag1 = this.player.verticalCollisionBelow;
this.player.move(MoverType.PLAYER, new Vec3(d3, d4, d5));
- this.player.onGround = packet.isOnGround(); // CraftBukkit - SPIGOT-5810, SPIGOT-5835, SPIGOT-6828: reset by this.player.move
+ // SparklyPaper start - Add PlayerPreMoveEvent event
+ boolean isOnGround = packet.isOnGround();
+ boolean horizontalCollision = packet.horizontalCollision();
+ if (net.sparklypower.sparklypaper.event.player.PlayerPreMoveEvent.getHandlerList().getRegisteredListeners().length != 0) {
+ org.bukkit.entity.Player player = this.getCraftPlayer();
+ Location from = new Location(player.getWorld(), lastPosX, lastPosY, lastPosZ, lastYaw, lastPitch); // Get the Players previous Event location.
+ Location to = player.getLocation().clone(); // Start off the To location as the Players current location.
+
+ // If the packet contains movement information then we update the To location with the correct XYZ.
+ if (packet.hasPos) {
+ to.setX(packet.x);
+ to.setY(packet.y);
+ to.setZ(packet.z);
+ }
+
+ // If the packet contains look information then we update the To location with the correct Yaw & Pitch.
+ if (packet.hasRot) {
+ to.setYaw(packet.yRot);
+ to.setPitch(packet.xRot);
+ }
+
+ net.sparklypower.sparklypaper.event.player.PlayerPreMoveEvent event = new net.sparklypower.sparklypaper.event.player.PlayerPreMoveEvent(player, from, to, packet.isOnGround(), packet.horizontalCollision(), flag);
+
+ if (event.callEvent()) {
+ isOnGround = event.isOnGround();
+ horizontalCollision = event.isHorizontalCollision();
+ flag = event.isResetFallDistance();
+ }
+ }
+ // SparklyPaper end
+ this.player.onGround = isOnGround; // CraftBukkit - SPIGOT-5810, SPIGOT-5835, SPIGOT-6828: reset by this.player.move // SparklyPaper - Add PlayerPreMoveEvent
boolean didCollide = toX != this.player.getX() || toY != this.player.getY() || toZ != this.player.getZ(); // Paper - needed here as the difference in Y can be reset - also note: this is only a guess at whether collisions took place, floating point errors can make this true when it shouldn't be...
// Paper start - prevent position desync
if (this.awaitingPositionFromClient != null) {
@@ -1590,15 +1620,15 @@ public class ServerGamePacketListenerImpl
&& this.noBlocksAround(this.player);
this.player.serverLevel().getChunkSource().move(this.player);
Vec3 vec3 = new Vec3(this.player.getX() - x, this.player.getY() - y, this.player.getZ() - z);
- this.player.setOnGroundWithMovement(packet.isOnGround(), packet.horizontalCollision(), vec3);
- this.player.doCheckFallDamage(vec3.x, vec3.y, vec3.z, packet.isOnGround());
+ this.player.setOnGroundWithMovement(isOnGround, horizontalCollision, vec3); // SparklyPaper - Add PlayerPreMoveEvent
+ this.player.doCheckFallDamage(vec3.x, vec3.y, vec3.z, isOnGround); // SparklyPaper - Add PlayerPreMoveEvent
this.player.recordMovementThroughBlocks(new Vec3(x, y, z), this.player.position());
this.handlePlayerKnownMovement(vec3);
if (flag) {
this.player.resetFallDistance();
}
- if (packet.isOnGround()
+ if (isOnGround // SparklyPaper - Add PlayerPreMoveEvent
|| this.player.hasLandedInLiquid()
|| this.player.onClimbable()
|| this.player.isSpectator()
@@ -1613,7 +1643,7 @@ public class ServerGamePacketListenerImpl
this.lastGoodZ = this.player.getZ();
} else {
this.internalTeleport(x, y, z, f, f1); // CraftBukkit - SPIGOT-1807: Don't call teleport event, when the client thinks the player is falling, because the chunks are not loaded on the client yet.
- this.player.doCheckFallDamage(this.player.getX() - x, this.player.getY() - y, this.player.getZ() - z, packet.isOnGround());
+ this.player.doCheckFallDamage(this.player.getX() - x, this.player.getY() - y, this.player.getZ() - z, isOnGround); // SparklyPaper - Add PlayerPreMoveEvent
}
}
}

View File

@@ -0,0 +1,22 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Sun, 12 Jan 2025 23:26:44 -0300
Subject: [PATCH] Add PreEntityShootBowEvent
diff --git a/net/minecraft/world/item/BowItem.java b/net/minecraft/world/item/BowItem.java
index 57c933af200551162774f1d473437521e5a85833..9d8a6763efcdd2a57507e208c34faa3f180d5421 100644
--- a/net/minecraft/world/item/BowItem.java
+++ b/net/minecraft/world/item/BowItem.java
@@ -33,6 +33,11 @@ public class BowItem extends ProjectileWeaponItem {
} else {
int i = this.getUseDuration(stack, entity) - timeLeft;
float powerForTime = getPowerForTime(i);
+ // SparklyPaper start - Add PreEntityShootBowEvent
+ net.sparklypower.sparklypaper.event.entity.PreEntityShootBowEvent event = new net.sparklypower.sparklypaper.event.entity.PreEntityShootBowEvent(player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(stack), player.getUsedItemHand() == InteractionHand.MAIN_HAND ? org.bukkit.inventory.EquipmentSlot.HAND : org.bukkit.inventory.EquipmentSlot.OFF_HAND, powerForTime);
+ if (!event.callEvent())
+ return false;
+ // SparklyPaper end
if (powerForTime < 0.1) {
return false;
} else {

View File

@@ -0,0 +1,967 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Tue, 7 Nov 2023 01:34:14 -0300
Subject: [PATCH] Parallel world ticking
"mom can we have folia?" "we already have folia at home" folia at home:
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
index b5817aa8f537593f6d9fc6b612c82ccccb250ac7..2dbbd33690265cbacd4c6516d24bc64890f50990 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
@@ -1031,7 +1031,7 @@ public final class ChunkHolderManager {
if (changedFullStatus.isEmpty()) {
return;
}
- if (!TickThread.isTickThread()) {
+ if (!TickThread.isTickThreadFor(world)) { // SparklyPaper - parallel world ticking
this.taskScheduler.scheduleChunkTask(() -> {
final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = ChunkHolderManager.this.pendingFullLoadUpdate;
for (int i = 0, len = changedFullStatus.size(); i < len; ++i) {
@@ -1057,7 +1057,7 @@ public final class ChunkHolderManager {
// note: never call while inside the chunk system, this will absolutely break everything
public void processUnloads() {
- TickThread.ensureTickThread("Cannot unload chunks off-main");
+ TickThread.ensureTickThread(world, "Cannot unload chunks off-main"); // SparklyPaper - parallel world ticking
if (BLOCK_TICKET_UPDATES.get() == Boolean.TRUE) {
throw new IllegalStateException("Cannot unload chunks recursively");
@@ -1339,7 +1339,7 @@ public final class ChunkHolderManager {
List<NewChunkHolder> changedFullStatus = null;
- final boolean isTickThread = TickThread.isTickThread();
+ final boolean isTickThread = TickThread.isTickThreadFor(world);
boolean ret = false;
final boolean canProcessFullUpdates = processFullUpdates & isTickThread;
diff --git a/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java b/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java
index 4a881636ba21fae9e50950bbba2b4321b71d35ab..ef76ff3671d38b482ac9932560e574d0d8cd9318 100644
--- a/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java
+++ b/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java
@@ -46,7 +46,7 @@ public class BoatDispenseItemBehavior extends DefaultDispenseItemBehavior {
org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(singleItemStack);
org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d1, d2 + d4, d3));
- if (!DispenserBlock.eventFired) {
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
serverLevel.getCraftServer().getPluginManager().callEvent(event);
}
diff --git a/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java b/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java
index bd5bbc7e55c6bea77991fe5a3c0c2580313d16c5..a231e08edba33bd55bed9a34129615f5e6dddb4d 100644
--- a/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java
+++ b/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java
@@ -78,7 +78,7 @@ public class DefaultDispenseItemBehavior implements DispenseItemBehavior {
org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(stack);
org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), org.bukkit.craftbukkit.util.CraftVector.toBukkit(itemEntity.getDeltaMovement()));
- if (!DispenserBlock.eventFired) {
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
level.getCraftServer().getPluginManager().callEvent(event);
}
diff --git a/net/minecraft/core/dispenser/DispenseItemBehavior.java b/net/minecraft/core/dispenser/DispenseItemBehavior.java
index 717c84165d5e25cd384f56b7cb976abf6669b6f0..8771e808cdf90fa6590140654da4eab08e15e58f 100644
--- a/net/minecraft/core/dispenser/DispenseItemBehavior.java
+++ b/net/minecraft/core/dispenser/DispenseItemBehavior.java
@@ -89,7 +89,7 @@ public interface DispenseItemBehavior {
org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(singleItemStack);
org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
- if (!DispenserBlock.eventFired) {
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
serverLevel.getCraftServer().getPluginManager().callEvent(event);
}
@@ -147,7 +147,7 @@ public interface DispenseItemBehavior {
org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(singleItemStack);
org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
- if (!DispenserBlock.eventFired) {
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
serverLevel.getCraftServer().getPluginManager().callEvent(event);
}
@@ -201,7 +201,7 @@ public interface DispenseItemBehavior {
org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(singleItemStack);
org.bukkit.event.block.BlockDispenseArmorEvent event = new org.bukkit.event.block.BlockDispenseArmorEvent(block, craftItem.clone(), entitiesOfClass.get(0).getBukkitLivingEntity());
- if (!DispenserBlock.eventFired) {
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
world.getCraftServer().getPluginManager().callEvent(event);
}
@@ -251,7 +251,7 @@ public interface DispenseItemBehavior {
org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(world, blockSource.pos());
org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(singleCopy);
org.bukkit.event.block.BlockDispenseArmorEvent event = new org.bukkit.event.block.BlockDispenseArmorEvent(block, craftItem.clone(), abstractChestedHorse.getBukkitLivingEntity());
- if (!DispenserBlock.eventFired) {
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
world.getCraftServer().getPluginManager().callEvent(event);
}
@@ -329,7 +329,7 @@ public interface DispenseItemBehavior {
org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(item.copyWithCount(1)); // Paper - single item in event
org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(x, y, z));
- if (!DispenserBlock.eventFired) {
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
level.getCraftServer().getPluginManager().callEvent(event);
}
@@ -389,7 +389,7 @@ public interface DispenseItemBehavior {
org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(item.copyWithCount(1)); // Paper - single item in event
org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(bukkitBlock, craftItem.clone(), org.bukkit.craftbukkit.util.CraftVector.toBukkit(blockPos));
- if (!DispenserBlock.eventFired) {
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
levelAccessor.getMinecraftWorld().getCraftServer().getPluginManager().callEvent(event);
}
@@ -425,7 +425,7 @@ public interface DispenseItemBehavior {
org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(item); // Paper - ignore stack size on damageable items
org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
- if (!DispenserBlock.eventFired) {
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
serverLevel.getCraftServer().getPluginManager().callEvent(event);
}
@@ -482,7 +482,7 @@ public interface DispenseItemBehavior {
org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(item.copyWithCount(1)); // Paper - single item in event
org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
- if (!DispenserBlock.eventFired) {
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
level.getCraftServer().getPluginManager().callEvent(event);
}
@@ -510,8 +510,8 @@ public interface DispenseItemBehavior {
// CraftBukkit start
level.captureTreeGeneration = false;
if (level.capturedBlockStates.size() > 0) {
- org.bukkit.TreeType treeType = net.minecraft.world.level.block.SaplingBlock.treeType;
- net.minecraft.world.level.block.SaplingBlock.treeType = null;
+ org.bukkit.TreeType treeType = net.minecraft.world.level.block.SaplingBlock.treeTypeRT.get(); // SparklyPaper - parallel world ticking
+ net.minecraft.world.level.block.SaplingBlock.treeTypeRT.set(null); // SparklyPaper - parallel world ticking
org.bukkit.Location location = org.bukkit.craftbukkit.util.CraftLocation.toBukkit(blockPos, level.getWorld());
List<org.bukkit.block.BlockState> blocks = new java.util.ArrayList<>(level.capturedBlockStates.values());
level.capturedBlockStates.clear();
@@ -548,7 +548,7 @@ public interface DispenseItemBehavior {
org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(singleItemStack);
org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector((double) blockPos.getX() + 0.5D, (double) blockPos.getY(), (double) blockPos.getZ() + 0.5D));
- if (!DispenserBlock.eventFired) {
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
level.getCraftServer().getPluginManager().callEvent(event);
}
@@ -591,7 +591,7 @@ public interface DispenseItemBehavior {
org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(item.copyWithCount(1)); // Paper - single item in event
org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(bukkitBlock, craftItem.clone(), org.bukkit.craftbukkit.util.CraftVector.toBukkit(blockPos));
- if (!DispenserBlock.eventFired) {
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
level.getCraftServer().getPluginManager().callEvent(event);
}
@@ -644,7 +644,7 @@ public interface DispenseItemBehavior {
org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(item.copyWithCount(1)); // Paper - single item in event
org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(bukkitBlock, craftItem.clone(), org.bukkit.craftbukkit.util.CraftVector.toBukkit(blockPos));
- if (!DispenserBlock.eventFired) {
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
level.getCraftServer().getPluginManager().callEvent(event);
}
@@ -702,7 +702,7 @@ public interface DispenseItemBehavior {
org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(item.copyWithCount(1)); // Paper - only single item in event
org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(bukkitBlock, craftItem.clone(), org.bukkit.craftbukkit.util.CraftVector.toBukkit(blockPos));
- if (!DispenserBlock.eventFired) {
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
serverLevel.getCraftServer().getPluginManager().callEvent(event);
}
@@ -783,7 +783,7 @@ public interface DispenseItemBehavior {
org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(item); // Paper - ignore stack size on damageable items
org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseArmorEvent(block, craftItem.clone(), entitiesOfClass.get(0).getBukkitLivingEntity());
- if (!DispenserBlock.eventFired) {
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
serverLevel.getCraftServer().getPluginManager().callEvent(event);
}
diff --git a/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java b/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java
index 3595bbd05fb3e8fe57e38d4e2df5c6237046b726..2b02d7cbc7fbb916c240f2d16bab365dbc1b8e56 100644
--- a/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java
+++ b/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java
@@ -39,7 +39,7 @@ public class EquipmentDispenseItemBehavior extends DefaultDispenseItemBehavior {
org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack);
org.bukkit.event.block.BlockDispenseArmorEvent event = new org.bukkit.event.block.BlockDispenseArmorEvent(block, craftItem.clone(), (org.bukkit.craftbukkit.entity.CraftLivingEntity) livingEntity.getBukkitEntity());
- if (!DispenserBlock.eventFired) {
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
world.getCraftServer().getPluginManager().callEvent(event);
}
diff --git a/net/minecraft/core/dispenser/MinecartDispenseItemBehavior.java b/net/minecraft/core/dispenser/MinecartDispenseItemBehavior.java
index 116395b6c00a0814922516707544a9ff26d68835..672ebd0cfc144d9b7315dcebf1e8007912ad7412 100644
--- a/net/minecraft/core/dispenser/MinecartDispenseItemBehavior.java
+++ b/net/minecraft/core/dispenser/MinecartDispenseItemBehavior.java
@@ -62,7 +62,7 @@ public class MinecartDispenseItemBehavior extends DefaultDispenseItemBehavior {
org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack1);
org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block2, craftItem.clone(), new org.bukkit.util.Vector(vec31.x, vec31.y, vec31.z));
- if (!DispenserBlock.eventFired) {
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
serverLevel.getCraftServer().getPluginManager().callEvent(event);
}
diff --git a/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java b/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java
index 449d9b72ff4650961daa9d1bd25940f3914a6b12..df07b55924c1edca11eebd9debc75d9f184ede9d 100644
--- a/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java
+++ b/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java
@@ -32,7 +32,7 @@ public class ProjectileDispenseBehavior extends DefaultDispenseItemBehavior {
org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack1);
org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector((double) direction.getStepX(), (double) direction.getStepY(), (double) direction.getStepZ()));
- if (!DispenserBlock.eventFired) {
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
serverLevel.getCraftServer().getPluginManager().callEvent(event);
}
diff --git a/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java b/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java
index 626e9feb6a6e7a2cbc7c63e30ba4fb6b923e85c7..6ac9a0fc92eb9801bed3bc296988ee224afffa14 100644
--- a/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java
+++ b/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java
@@ -25,7 +25,7 @@ public class ShearsDispenseItemBehavior extends OptionalDispenseItemBehavior {
org.bukkit.block.Block bukkitBlock = org.bukkit.craftbukkit.block.CraftBlock.at(serverLevel, blockSource.pos());
org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(item); // Paper - ignore stack size on damageable items
org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
- if (!DispenserBlock.eventFired) {
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
serverLevel.getCraftServer().getPluginManager().callEvent(event);
}
diff --git a/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java b/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java
index 5ab2c8333178335515e619b87ae420f948c83bd1..c98e3748ff467f75f0a0e5a429563feaf06105f3 100644
--- a/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java
+++ b/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java
@@ -27,7 +27,7 @@ public class ShulkerBoxDispenseBehavior extends OptionalDispenseItemBehavior {
org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(item.copyWithCount(1)); // Paper - single item in event
org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockPos.getX(), blockPos.getY(), blockPos.getZ()));
- if (!DispenserBlock.eventFired) {
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
blockSource.level().getCraftServer().getPluginManager().callEvent(event);
}
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index c15c11ab3feaca918ac0185383cd1653200cd186..1bc0e3397a1299bdbe37a69097ece7d7395421f7 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -305,6 +305,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
public static final long SERVER_INIT = System.nanoTime(); // Paper - Lag compensation
public final Set<net.minecraft.world.entity.Entity> entitiesWithScheduledTasks = java.util.concurrent.ConcurrentHashMap.newKeySet(); // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run (concurrent because plugins may schedule tasks async)
public net.sparklypower.sparklypaper.HalloweenManager halloweenManager = new net.sparklypower.sparklypaper.HalloweenManager(); // SparklyPaper - Spooky month optimizations
+ public java.util.concurrent.Semaphore serverLevelTickingSemaphore = null; // SparklyPaper - parallel world ticking
public static <S extends MinecraftServer> S spin(Function<Thread, S> threadFunction) {
ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system
@@ -1720,6 +1721,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
this.isIteratingOverLevels = true; // Paper - Throw exception on world create while being ticked
+ // SparklyPaper start - parallel world ticking
+ java.util.ArrayDeque<java.util.concurrent.Future<ServerLevel>> tasks = new java.util.ArrayDeque<>();
+ try {
for (ServerLevel serverLevel : this.getAllLevels()) {
serverLevel.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - BlockPhysicsEvent
serverLevel.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent
@@ -1736,27 +1740,46 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
profilerFiller.push("tick");
- try {
- long i = Util.getNanos(); // SparklyPaper - track world's MSPT
- serverLevel.tick(hasTimeLeft);
- // SparklyPaper start - track world's MSPT
- long j = Util.getNanos() - i;
-
- // These are from the "tickServer" function
- serverLevel.tickTimes5s.add(this.tickCount, j);
- serverLevel.tickTimes10s.add(this.tickCount, j);
- serverLevel.tickTimes60s.add(this.tickCount, j);
- // SparklyPaper end
- } catch (Throwable var7) {
- CrashReport crashReport = CrashReport.forThrowable(var7, "Exception ticking world");
- serverLevel.fillReportDetails(crashReport);
- throw new ReportedException(crashReport);
- }
+ serverLevelTickingSemaphore.acquire();
+ tasks.add(
+ serverLevel.tickExecutor.submit(() -> {
+ try {
+ ca.spottedleaf.moonrise.common.util.TickThread.ServerLevelTickThread currentThread = (ca.spottedleaf.moonrise.common.util.TickThread.ServerLevelTickThread) Thread.currentThread();
+ currentThread.currentlyTickingServerLevel = serverLevel;
+
+ long i = Util.getNanos(); // SparklyPaper - track world's MSPT
+ serverLevel.tick(hasTimeLeft);
+ // SparklyPaper start - track world's MSPT
+ long j = Util.getNanos() - i;
+
+ // These are from the "tickServer" function
+ serverLevel.tickTimes5s.add(this.tickCount, j);
+ serverLevel.tickTimes10s.add(this.tickCount, j);
+ serverLevel.tickTimes60s.add(this.tickCount, j);
+ // SparklyPaper end
+ } catch (Throwable throwable) {
+ CrashReport crashreport = CrashReport.forThrowable(throwable, "Exception ticking world");
+
+ serverLevel.fillReportDetails(crashreport);
+ throw new ReportedException(crashreport);
+ } finally {
+ serverLevelTickingSemaphore.release();
+ }
+ }, serverLevel)
+ );
profilerFiller.pop();
profilerFiller.pop();
serverLevel.explosionDensityCache.clear(); // Paper - Optimize explosions
}
+
+ while (!tasks.isEmpty()) {
+ tasks.pop().get();
+ }
+ } catch (java.lang.InterruptedException | java.util.concurrent.ExecutionException e) {
+ throw new RuntimeException(e); // Propagate exception
+ }
+ // SparklyPaper end
this.isIteratingOverLevels = false; // Paper - Throw exception on world create while being ticked
profilerFiller.popPush("connection");
@@ -1847,6 +1870,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
Map<ResourceKey<Level>, ServerLevel> oldLevels = this.levels;
Map<ResourceKey<Level>, ServerLevel> newLevels = Maps.newLinkedHashMap(oldLevels);
newLevels.remove(level.dimension());
+ level.tickExecutor.shutdown(); // SparklyPaper - parallel world ticking (We remove it in here instead of ServerLevel.close() because ServerLevel.close() is never called!)
this.levels = Collections.unmodifiableMap(newLevels);
}
// CraftBukkit end
diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java
index 6ac73610f6039290c4ef3dde2206b6863d2b5067..0c5dbfe1d7e17796c1a60bcb1f97673ddff4d94e 100644
--- a/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/net/minecraft/server/dedicated/DedicatedServer.java
@@ -233,6 +233,8 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
}
net.sparklypower.sparklypaper.SparklyPaperCommands.INSTANCE.registerCommands(this);
// SparklyPaper end
+ serverLevelTickingSemaphore = new java.util.concurrent.Semaphore(net.sparklypower.sparklypaper.configs.SparklyPaperConfigUtils.INSTANCE.getConfig().getParallelWorldTicking().getThreads()); // SparklyPaper - parallel world ticking
+ DedicatedServer.LOGGER.info("Using " + serverLevelTickingSemaphore.availablePermits() + " permits for parallel world ticking"); // SparklyPaper - parallel world ticking
// SparklyPaper start - Spooky month optimizations
halloweenManager.startHalloweenEpochTask();
halloweenManager.waitUntilEpochHasBeenUpdated();
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 59465ab6d7723d27b1597cc7094b5044a3f24250..7d0e5184289b37c6c9acc34341b8d84399ba653f 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -184,7 +184,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
private final MinecraftServer server;
public final net.minecraft.world.level.storage.PrimaryLevelData serverLevelData; // CraftBukkit - type
private int lastSpawnChunkRadius;
- final EntityTickList entityTickList = new EntityTickList();
+ final EntityTickList entityTickList = new EntityTickList(this); // SparklyPaper - parallel world ticking
// Paper - rewrite chunk system
private final GameEventDispatcher gameEventDispatcher;
public boolean noSave;
@@ -208,6 +208,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
private final StructureCheck structureCheck;
private final boolean tickTime;
private final RandomSequences randomSequences;
+ public java.util.concurrent.ExecutorService tickExecutor; // SparklyPaper - parallel world ticking
// CraftBukkit start
public final LevelStorageSource.LevelStorageAccess levelStorageAccess;
@@ -687,6 +688,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
this.chunkDataController = new ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.ChunkDataController((ServerLevel)(Object)this, this.chunkTaskScheduler);
// Paper end - rewrite chunk system
this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit
+ this.tickExecutor = java.util.concurrent.Executors.newSingleThreadExecutor(new net.sparklypower.sparklypaper.ServerLevelTickExecutorThreadFactory(getWorld().getName())); // SparklyPaper - parallel world ticking
}
// Paper start
@@ -1226,7 +1228,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
}
// Paper start - rewrite chunk system
if ((++this.tickedBlocksOrFluids & 7L) != 0L) {
- ((ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer)this.server).moonrise$executeMidTickTasks();
+ // ((ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer)this.server).moonrise$executeMidTickTasks(); // SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks)
}
// Paper end - rewrite chunk system
@@ -1239,7 +1241,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
}
// Paper start - rewrite chunk system
if ((++this.tickedBlocksOrFluids & 7L) != 0L) {
- ((ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer)this.server).moonrise$executeMidTickTasks();
+ // ((ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer)this.server).moonrise$executeMidTickTasks(); // SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks)
}
// Paper end - rewrite chunk system
@@ -1497,6 +1499,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
}
private void addPlayer(ServerPlayer player) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this, "Cannot add player off-main"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
Entity entity = this.getEntities().get(player.getUUID());
if (entity != null) {
LOGGER.warn("Force-added player with duplicate UUID {}", player.getUUID());
@@ -1509,7 +1512,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
// CraftBukkit start
private boolean addEntity(Entity entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason) {
- org.spigotmc.AsyncCatcher.catchOp("entity add"); // Spigot
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this, "Cannot add entity off-main"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
entity.generation = false; // Paper - Don't fire sync event during generation; Reset flag if it was added during a ServerLevel generation process
// Paper start - extra debug info
if (entity.valid) {
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index f347ff8d863f4bcef46604c757de112cb3fe445c..532df406e9fb41ab2ccfd59bcc7a614d6ada6548 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -423,6 +423,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
return this.viewDistanceHolder;
}
// Paper end - rewrite chunk system
+ public boolean hasTickedAtLeastOnceInNewWorld = false; // SparklyPaper - parallel world ticking (fixes bug in DreamResourceReset where the inventory is opened AFTER the player has changed worlds, if you click with the quick tp torch in a chest, because the inventory is opened AFTER the player has teleported)
public ServerPlayer(MinecraftServer server, ServerLevel level, GameProfile gameProfile, ClientInformation clientInformation) {
super(level, level.getSharedSpawnPos(), level.getSharedSpawnAngle(), gameProfile);
@@ -792,6 +793,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
@Override
public void tick() {
+ hasTickedAtLeastOnceInNewWorld = true; // SparklyPaper - parallel world ticking (see: PARALLEL_NOTES.md - Opening an inventory after a world switch)
// CraftBukkit start
if (this.joining) {
this.joining = false;
@@ -1415,6 +1417,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
return this;
} else {
// CraftBukkit start
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureOnlyTickThread("Cannot change dimension of a player off-main, from world " + serverLevel().getWorld().getName() + " to world " + level.getWorld().getName()); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
/*
this.isChangingDimension = true;
LevelData levelData = level.getLevelData();
@@ -1768,6 +1771,12 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
return OptionalInt.empty();
} else {
// CraftBukkit start
+ // SparklyPaper start - parallel world ticking (see: PARALLEL_NOTES.md - Opening an inventory after a world switch)
+ if (!hasTickedAtLeastOnceInNewWorld) {
+ MinecraftServer.LOGGER.warn("Ignoring request to open container " + abstractContainerMenu + " because we haven't ticked in the current world yet!", new Throwable());
+ return OptionalInt.empty();
+ }
+ // SparklyPaper end
this.containerMenu = abstractContainerMenu; // Moved up
if (!this.isImmobile())
this.connection
@@ -1832,6 +1841,11 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
}
@Override
public void closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason reason) {
+ // SparklyPaper start - parallel world ticking (debugging)
+ if (net.sparklypower.sparklypaper.configs.SparklyPaperConfigUtils.INSTANCE.getLogContainerCreationStacktraces()) {
+ MinecraftServer.LOGGER.warn("Closing " + this.getBukkitEntity().getName() + " inventory that was created at", this.containerMenu.containerCreationStacktrace);
+ }
+ // SparklyPaper end
org.bukkit.craftbukkit.event.CraftEventFactory.handleInventoryCloseEvent(this, reason); // CraftBukkit
// Paper end - Inventory close reason
this.connection.send(new ClientboundContainerClosePacket(this.containerMenu.containerId));
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index 03feaf0adb8ee87e33744a4615dc2507a02f92d7..d6e13845fce443ae9b8f2c9a4ee5b00c4c5cc831 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -113,7 +113,7 @@ public abstract class PlayerList {
private static final SimpleDateFormat BAN_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd 'at' HH:mm:ss z");
private final MinecraftServer server;
public final List<ServerPlayer> players = new java.util.concurrent.CopyOnWriteArrayList(); // CraftBukkit - ArrayList -> CopyOnWriteArrayList: Iterator safety
- private final Map<UUID, ServerPlayer> playersByUUID = Maps.newHashMap();
+ private final Map<UUID, ServerPlayer> playersByUUID = Maps.newHashMap(); // SparklyPaper - parallel world ticking (we don't need to replace the original map because we never iterate on top of this map)
private final UserBanList bans = new UserBanList(USERBANLIST_FILE);
private final IpBanList ipBans = new IpBanList(IPBANLIST_FILE);
private final ServerOpList ops = new ServerOpList(OPLIST_FILE);
@@ -134,7 +134,7 @@ public abstract class PlayerList {
// CraftBukkit start
private org.bukkit.craftbukkit.CraftServer cserver;
- private final Map<String,ServerPlayer> playersByName = new java.util.HashMap<>();
+ private final Map<String,ServerPlayer> playersByName = new java.util.HashMap<>(); // SparklyPaper - parallel world ticking (we don't need to replace the original map because we never iterate on top of this map)
public @Nullable String collideRuleTeamName; // Paper - Configurable player collision
public PlayerList(MinecraftServer server, LayeredRegistryAccess<RegistryLayer> registries, PlayerDataStorage playerIo, int maxPlayers) {
@@ -150,6 +150,7 @@ public abstract class PlayerList {
abstract public void loadAndSaveFiles(); // Paper - fix converting txt to json file; moved from DedicatedPlayerList constructor
public void placeNewPlayer(Connection connection, ServerPlayer player, CommonListenerCookie cookie) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureOnlyTickThread("Cannot place new player off-main"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
player.isRealPlayer = true; // Paper
player.loginTime = System.currentTimeMillis(); // Paper - Replace OfflinePlayer#getLastPlayed
GameProfile gameProfile = player.getGameProfile();
@@ -714,6 +715,13 @@ public abstract class PlayerList {
return this.respawn(player, keepInventory, reason, eventReason, null);
}
public ServerPlayer respawn(ServerPlayer player, boolean keepInventory, Entity.RemovalReason reason, org.bukkit.event.player.PlayerRespawnEvent.RespawnReason eventReason, org.bukkit.Location location) {
+ System.out.println("respawning player - current player container is " + player.containerMenu + " but their inventory is " + player.inventoryMenu);
+ // SparklyPaper - parallel world ticking (additional concurrency issues logs)
+ if (location != null) // TODO: Is this really never null, or is IntelliJ IDEA tripping? Because I'm pretty sure that this can be null and there isn't any @NotNull annotations
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureOnlyTickThread("Cannot respawn player off-main, from world " + player.serverLevel().getWorld().getName() + " to world " + location.getWorld().getName());
+ else
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureOnlyTickThread("Cannot respawn player off-main, respawning in world " + player.serverLevel().getWorld().getName());
+ // SparklyPaper end
player.stopRiding(); // CraftBukkit
this.players.remove(player);
this.playersByName.remove(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT)); // Spigot
@@ -724,6 +732,7 @@ public abstract class PlayerList {
ServerPlayer serverPlayer = player;
Level fromWorld = player.level();
player.wonGame = false;
+ serverPlayer.hasTickedAtLeastOnceInNewWorld = false; // SparklyPaper - parallel world ticking (see: PARALLEL_NOTES.md - Opening an inventory after a world switch)
// CraftBukkit end
serverPlayer.connection = player.connection;
serverPlayer.restoreFrom(player, keepInventory);
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 1d0151a042ed5de4e235ef0bdac1a0e8240e85e7..731aecb84f87b6de41029b88e0c5c7ce2bab20cb 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -822,7 +822,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
// CraftBukkit start
public void postTick() {
// No clean way to break out of ticking once the entity has been copied to a new world, so instead we move the portalling later in the tick cycle
- if (!(this instanceof ServerPlayer) && this.isAlive()) { // Paper - don't attempt to teleport dead entities
+ if (false && !(this instanceof ServerPlayer) && this.isAlive()) { // Paper - don't attempt to teleport dead entities // SparklyPaper - parallel world ticking (see issue #9, this is executed in the server level tick for non-player entities)
this.handlePortal();
}
}
@@ -3808,6 +3808,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
}
private Entity teleportCrossDimension(ServerLevel level, TeleportTransition teleportTransition) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(level, "Cannot teleport entity to another world off-main, from world " + this.level.getWorld().getName() + " to world " + level.getWorld().getName()); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
List<Entity> passengers = this.getPassengers();
List<Entity> list = new ArrayList<>(passengers.size());
this.ejectPassengers();
diff --git a/net/minecraft/world/inventory/AbstractContainerMenu.java b/net/minecraft/world/inventory/AbstractContainerMenu.java
index 50af953a4698a3c6e16b840fab764dd733b3fbc9..27b6fcb12f1f6a36529fbf19f10ca6330280525a 100644
--- a/net/minecraft/world/inventory/AbstractContainerMenu.java
+++ b/net/minecraft/world/inventory/AbstractContainerMenu.java
@@ -91,8 +91,14 @@ public abstract class AbstractContainerMenu {
}
public void startOpen() {}
// CraftBukkit end
+ public Throwable containerCreationStacktrace; // SparklyPaper - parallel world ticking (debugging)
protected AbstractContainerMenu(@Nullable MenuType<?> menuType, int containerId) {
+ // SparklyPaper - parallel world ticking (debugging)
+ if (net.sparklypower.sparklypaper.configs.SparklyPaperConfigUtils.INSTANCE.getLogContainerCreationStacktraces()) {
+ this.containerCreationStacktrace = new Throwable();
+ }
+ // SparklyPaper end
this.menuType = menuType;
this.containerId = containerId;
}
diff --git a/net/minecraft/world/item/ItemStack.java b/net/minecraft/world/item/ItemStack.java
index 76f50437396f8f856381d0fbef52953ef7c263f6..199fec37b2c2b2ea6a55700cb8f4ec98aeb6504a 100644
--- a/net/minecraft/world/item/ItemStack.java
+++ b/net/minecraft/world/item/ItemStack.java
@@ -407,8 +407,8 @@ public final class ItemStack implements DataComponentHolder {
if (interactionResult.consumesAction() && serverLevel.captureTreeGeneration && !serverLevel.capturedBlockStates.isEmpty()) {
serverLevel.captureTreeGeneration = false;
org.bukkit.Location location = org.bukkit.craftbukkit.util.CraftLocation.toBukkit(clickedPos, serverLevel.getWorld());
- org.bukkit.TreeType treeType = net.minecraft.world.level.block.SaplingBlock.treeType;
- net.minecraft.world.level.block.SaplingBlock.treeType = null;
+ org.bukkit.TreeType treeType = net.minecraft.world.level.block.SaplingBlock.treeTypeRT.get(); // SparklyPaper - parallel world ticking
+ net.minecraft.world.level.block.SaplingBlock.treeTypeRT.set(null);
List<org.bukkit.craftbukkit.block.CraftBlockState> blocks = new java.util.ArrayList<>(serverLevel.capturedBlockStates.values());
serverLevel.capturedBlockStates.clear();
org.bukkit.event.world.StructureGrowEvent structureEvent = null;
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index 644ab8b2c797f0675530a3efe23a878899689d66..b43cc51c23da0228aafd99d56e2a6e5cc05496ed 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -171,6 +171,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
public final io.papermc.paper.antixray.ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray
public net.sparklypower.sparklypaper.configs.SparklyPaperConfig.SparklyPaperWorldConfig sparklyPaperConfig; // SparklyPaper
+ public io.papermc.paper.redstone.RedstoneWireTurbo turbo = new io.papermc.paper.redstone.RedstoneWireTurbo((net.minecraft.world.level.block.RedStoneWireBlock) net.minecraft.world.level.block.Blocks.REDSTONE_WIRE); // SparklyPaper - parallel world ticking (moved to world)
public static BlockPos lastPhysicsProblem; // Spigot
private org.spigotmc.TickLimiter entityLimiter;
private org.spigotmc.TickLimiter tileLimiter;
@@ -1100,6 +1101,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
@Override
public boolean setBlock(BlockPos pos, BlockState state, int flags, int recursionLeft) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread((ServerLevel)this, pos, "Updating block asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
// CraftBukkit start - tree generation
if (this.captureTreeGeneration) {
// Paper start - Protect Bedrock and End Portal/Frames from being destroyed
@@ -1484,7 +1486,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
tickingBlockEntity.tick();
// Paper start - rewrite chunk system
if ((++tickedEntities & 7) == 0) {
- ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel)(Level)(Object)this).moonrise$midTickTasks();
+ // ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel)(Level)(Object)this).moonrise$midTickTasks(); // SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks)
}
// Paper end - rewrite chunk system
}
@@ -1508,7 +1510,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD);
// Paper end - Prevent block entity and entity crashes
}
- this.moonrise$midTickTasks(); // Paper - rewrite chunk system
+ // this.moonrise$midTickTasks(); // Paper - rewrite chunk system // SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks)
}
// Paper start - Option to prevent armor stands from doing entity lookups
@@ -1651,6 +1653,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
@Nullable
public BlockEntity getBlockEntity(BlockPos pos, boolean validate) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThreadOrAsyncThread((ServerLevel) this, "Cannot read world asynchronously"); // SparklyPaper - parallel world ticking
// Paper start - Perf: Optimize capturedTileEntities lookup
net.minecraft.world.level.block.entity.BlockEntity blockEntity;
if (!this.capturedTileEntities.isEmpty() && (blockEntity = this.capturedTileEntities.get(pos)) != null) {
@@ -1668,6 +1671,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
}
public void setBlockEntity(BlockEntity blockEntity) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread((ServerLevel) this, "Cannot modify world asynchronously"); // SparklyPaper - parallel world ticking
BlockPos blockPos = blockEntity.getBlockPos();
if (!this.isOutsideBuildHeight(blockPos)) {
// CraftBukkit start
@@ -1752,6 +1756,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
@Override
public List<Entity> getEntities(@Nullable Entity entity, AABB boundingBox, Predicate<? super Entity> predicate) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread((ServerLevel)this, boundingBox, "Cannot getEntities asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
Profiler.get().incrementCounter("getEntities");
List<Entity> list = Lists.newArrayList();
@@ -2064,8 +2069,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
public abstract RecipeAccess recipeAccess();
public BlockPos getBlockRandomPos(int x, int y, int z, int yMask) {
- this.randValue = this.randValue * 3 + 1013904223;
- int i = this.randValue >> 2;
+ int i = this.random.nextInt() >> 2; // SparklyPaper - parallel world ticking
return new BlockPos(x + (i & 15), y + (i >> 16 & yMask), z + (i >> 8 & 15));
}
diff --git a/net/minecraft/world/level/block/DispenserBlock.java b/net/minecraft/world/level/block/DispenserBlock.java
index e0a4d41e5bcf144ea4c10d6f633c3a95ed2c5aec..ff99bff5b1986798ac8170def2809cc0c553e500 100644
--- a/net/minecraft/world/level/block/DispenserBlock.java
+++ b/net/minecraft/world/level/block/DispenserBlock.java
@@ -50,7 +50,8 @@ public class DispenserBlock extends BaseEntityBlock {
private static final DefaultDispenseItemBehavior DEFAULT_BEHAVIOR = new DefaultDispenseItemBehavior();
public static final Map<Item, DispenseItemBehavior> DISPENSER_REGISTRY = new IdentityHashMap<>();
private static final int TRIGGER_DURATION = 4;
- public static boolean eventFired = false; // CraftBukkit
+ // public static boolean eventFired = false; // CraftBukkit // SparklyPaper - parallel world ticking
+ public static ThreadLocal<Boolean> eventFired = ThreadLocal.withInitial(() -> Boolean.FALSE); // SparklyPaper - parallel world ticking
@Override
public MapCodec<? extends DispenserBlock> codec() {
@@ -96,7 +97,7 @@ public class DispenserBlock extends BaseEntityBlock {
DispenseItemBehavior dispenseMethod = this.getDispenseMethod(level, item);
if (dispenseMethod != DispenseItemBehavior.NOOP) {
if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockPreDispenseEvent(level, pos, item, randomSlot)) return; // Paper - Add BlockPreDispenseEvent
- DispenserBlock.eventFired = false; // CraftBukkit - reset event status
+ DispenserBlock.eventFired.set(Boolean.FALSE); // CraftBukkit - reset event status // SparklyPaper - parallel world ticking
dispenserBlockEntity.setItem(randomSlot, dispenseMethod.dispense(blockSource, item));
}
}
diff --git a/net/minecraft/world/level/block/FungusBlock.java b/net/minecraft/world/level/block/FungusBlock.java
index 85f0eac75784565c658c5178c544f969db3d6f54..bfa0b1f72497cefecc112d9f484896edc9ec6f36 100644
--- a/net/minecraft/world/level/block/FungusBlock.java
+++ b/net/minecraft/world/level/block/FungusBlock.java
@@ -76,9 +76,9 @@ public class FungusBlock extends BushBlock implements BonemealableBlock {
// CraftBukkit start
.map((value) -> {
if (this == Blocks.WARPED_FUNGUS) {
- SaplingBlock.treeType = org.bukkit.TreeType.WARPED_FUNGUS;
+ SaplingBlock.treeTypeRT.set(org.bukkit.TreeType.WARPED_FUNGUS); // SparklyPaper - parallel world ticking
} else if (this == Blocks.CRIMSON_FUNGUS) {
- SaplingBlock.treeType = org.bukkit.TreeType.CRIMSON_FUNGUS;
+ SaplingBlock.treeTypeRT.set(org.bukkit.TreeType.CRIMSON_FUNGUS); // SparklyPaper - parallel world ticking
}
return value;
})
diff --git a/net/minecraft/world/level/block/MushroomBlock.java b/net/minecraft/world/level/block/MushroomBlock.java
index 904369f4d7db41026183f2de7c96c2f0f4dc204d..1ef0cf368fe9de49c7ba0ba3b647108b5bbe3e48 100644
--- a/net/minecraft/world/level/block/MushroomBlock.java
+++ b/net/minecraft/world/level/block/MushroomBlock.java
@@ -94,7 +94,7 @@ public class MushroomBlock extends BushBlock implements BonemealableBlock {
return false;
} else {
level.removeBlock(pos, false);
- SaplingBlock.treeType = (this == Blocks.BROWN_MUSHROOM) ? org.bukkit.TreeType.BROWN_MUSHROOM : org.bukkit.TreeType.RED_MUSHROOM; // CraftBukkit
+ SaplingBlock.treeTypeRT.set((this == Blocks.BROWN_MUSHROOM) ? org.bukkit.TreeType.BROWN_MUSHROOM : org.bukkit.TreeType.RED_MUSHROOM); // CraftBukkit // SparklyPaper - parallel world ticking
if (optional.get().value().place(level, level.getChunkSource().getGenerator(), random, pos)) {
return true;
} else {
diff --git a/net/minecraft/world/level/block/RedStoneWireBlock.java b/net/minecraft/world/level/block/RedStoneWireBlock.java
index 12c9d60314c99fb65e640d255a2d0c6b7790ad4d..833b180741cd361562d86794b16e3cd6a25410d5 100644
--- a/net/minecraft/world/level/block/RedStoneWireBlock.java
+++ b/net/minecraft/world/level/block/RedStoneWireBlock.java
@@ -292,7 +292,7 @@ public class RedStoneWireBlock extends Block {
// Paper start - Optimize redstone (Eigencraft)
// The bulk of the new functionality is found in RedstoneWireTurbo.java
- io.papermc.paper.redstone.RedstoneWireTurbo turbo = new io.papermc.paper.redstone.RedstoneWireTurbo(this);
+ // io.papermc.paper.redstone.RedstoneWireTurbo turbo = new io.papermc.paper.redstone.RedstoneWireTurbo(this); // SparklyPaper - parallel world ticking (moved to world)
/*
* Modified version of pre-existing updateSurroundingRedstone, which is called from
@@ -308,7 +308,7 @@ public class RedStoneWireBlock extends Block {
if (orientation != null) {
source = pos.relative(orientation.getFront().getOpposite());
}
- turbo.updateSurroundingRedstone(worldIn, pos, state, source);
+ worldIn.turbo.updateSurroundingRedstone(worldIn, pos, state, source); // SparklyPaper - parallel world ticking
return;
}
updatePowerStrength(worldIn, pos, state, orientation, blockAdded);
@@ -336,7 +336,7 @@ public class RedStoneWireBlock extends Block {
// [Space Walker] suppress shape updates and emit those manually to
// bypass the new neighbor update stack.
if (level.setBlock(pos, state, Block.UPDATE_KNOWN_SHAPE | Block.UPDATE_CLIENTS)) {
- turbo.updateNeighborShapes(level, pos, state);
+ level.turbo.updateNeighborShapes(level, pos, state); // SparklyPaper - parallel world ticking
}
}
}
diff --git a/net/minecraft/world/level/block/SaplingBlock.java b/net/minecraft/world/level/block/SaplingBlock.java
index e014f052e9b0f5ca6b28044e2389782b7d0e0cb8..c9091b66f5ddf7bd7145becdfc5e252163b03300 100644
--- a/net/minecraft/world/level/block/SaplingBlock.java
+++ b/net/minecraft/world/level/block/SaplingBlock.java
@@ -26,7 +26,7 @@ public class SaplingBlock extends BushBlock implements BonemealableBlock {
protected static final float AABB_OFFSET = 6.0F;
protected static final VoxelShape SHAPE = Block.box(2.0, 0.0, 2.0, 14.0, 12.0, 14.0);
protected final TreeGrower treeGrower;
- public static org.bukkit.TreeType treeType; // CraftBukkit
+ public static final ThreadLocal<org.bukkit.TreeType> treeTypeRT = new ThreadLocal<>(); // CraftBukkit // SparklyPaper - parallel world ticking (from Folia)
@Override
public MapCodec<? extends SaplingBlock> codec() {
@@ -63,8 +63,8 @@ public class SaplingBlock extends BushBlock implements BonemealableBlock {
this.treeGrower.growTree(level, level.getChunkSource().getGenerator(), pos, state, random);
level.captureTreeGeneration = false;
if (!level.capturedBlockStates.isEmpty()) {
- org.bukkit.TreeType treeType = SaplingBlock.treeType;
- SaplingBlock.treeType = null;
+ org.bukkit.TreeType treeType = SaplingBlock.treeTypeRT.get(); // SparklyPaper - parallel world ticking
+ SaplingBlock.treeTypeRT.set(null); // SparklyPaper - parallel world ticking
org.bukkit.Location location = org.bukkit.craftbukkit.util.CraftLocation.toBukkit(pos, level.getWorld());
java.util.List<org.bukkit.block.BlockState> blocks = new java.util.ArrayList<>(level.capturedBlockStates.values());
level.capturedBlockStates.clear();
diff --git a/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java b/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java
index 26db603ed681a6c302596627d4dd5bf8a9bafc4e..a2df3546922ea27b948ed89450428a27f65d93ed 100644
--- a/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java
@@ -77,6 +77,12 @@ public abstract class BaseContainerBlockEntity extends BlockEntity implements Co
return canUnlock(player, code, displayName, null);
}
public static boolean canUnlock(Player player, LockCode code, Component displayName, @Nullable BlockEntity blockEntity) {
+ // SparklyPaper - parallel world ticking (see: PARALLEL_NOTES.md - Opening an inventory after a world switch)
+ if (player instanceof net.minecraft.server.level.ServerPlayer serverPlayer && blockEntity != null && blockEntity.getLevel() != serverPlayer.serverLevel()) {
+ net.minecraft.server.MinecraftServer.LOGGER.warn("Player " + serverPlayer.getScoreboardName() + " (" + serverPlayer.getStringUUID() + ") attempted to open a BlockEntity @ " + blockEntity.getLevel().getWorld().getName() + " " + blockEntity.getBlockPos().getX() + ", " + blockEntity.getBlockPos().getY() + ", " + blockEntity.getBlockPos().getZ() + " while they were in a different world " + serverPlayer.level().getWorld().getName() + " than the block themselves!");
+ return false;
+ }
+ // SparklyPaper end
if (player instanceof net.minecraft.server.level.ServerPlayer serverPlayer && blockEntity != null && blockEntity.getLevel() != null && blockEntity.getLevel().getBlockEntity(blockEntity.getBlockPos()) == blockEntity) {
final org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(blockEntity.getLevel(), blockEntity.getBlockPos());
net.kyori.adventure.text.Component lockedMessage = net.kyori.adventure.text.Component.translatable("container.isLocked", io.papermc.paper.adventure.PaperAdventure.asAdventure(displayName));
diff --git a/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java b/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java
index 1638eccef431fb68775af624110f1968f0c6dabd..a1096b17ddd4f02cdbe5615030803df88d200ed7 100644
--- a/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java
@@ -43,9 +43,9 @@ public class SculkCatalystBlockEntity extends BlockEntity implements GameEventLi
// Paper end - Fix NPE in SculkBloomEvent world access
public static void serverTick(Level level, BlockPos pos, BlockState state, SculkCatalystBlockEntity sculkCatalyst) {
- org.bukkit.craftbukkit.event.CraftEventFactory.sourceBlockOverride = sculkCatalyst.getBlockPos(); // CraftBukkit - SPIGOT-7068: Add source block override, not the most elegant way but better than passing down a BlockPosition up to five methods deep.
+ org.bukkit.craftbukkit.event.CraftEventFactory.sourceBlockOverrideRT.set(pos); // SparklyPaper - parallel world ticking // CraftBukkit - SPIGOT-7068: Add source block override, not the most elegant way but better than passing down a BlockPosition up to five methods deep.
sculkCatalyst.catalystListener.getSculkSpreader().updateCursors(level, pos, level.getRandom(), true);
- org.bukkit.craftbukkit.event.CraftEventFactory.sourceBlockOverride = null; // CraftBukkit
+ org.bukkit.craftbukkit.event.CraftEventFactory.sourceBlockOverrideRT.set(null); // SparklyPaper - parallel world ticking // CraftBukkit
}
@Override
diff --git a/net/minecraft/world/level/block/grower/TreeGrower.java b/net/minecraft/world/level/block/grower/TreeGrower.java
index cf7311c507de09a8f89934e430b2201e8bdffe51..2ad658fa8e42f26a37fed9faff421827cb72607b 100644
--- a/net/minecraft/world/level/block/grower/TreeGrower.java
+++ b/net/minecraft/world/level/block/grower/TreeGrower.java
@@ -204,55 +204,59 @@ public final class TreeGrower {
// CraftBukkit start
private void setTreeType(Holder<ConfiguredFeature<?, ?>> holder) {
ResourceKey<ConfiguredFeature<?, ?>> treeFeature = holder.unwrapKey().get();
+ // SparklyPaper start - parallel world ticking
+ org.bukkit.TreeType treeType;
if (treeFeature == TreeFeatures.OAK || treeFeature == TreeFeatures.OAK_BEES_005) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.TREE;
+ treeType = org.bukkit.TreeType.TREE;
} else if (treeFeature == TreeFeatures.HUGE_RED_MUSHROOM) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.RED_MUSHROOM;
+ treeType = org.bukkit.TreeType.RED_MUSHROOM;
} else if (treeFeature == TreeFeatures.HUGE_BROWN_MUSHROOM) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.BROWN_MUSHROOM;
+ treeType = org.bukkit.TreeType.BROWN_MUSHROOM;
} else if (treeFeature == TreeFeatures.JUNGLE_TREE) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.COCOA_TREE;
+ treeType = org.bukkit.TreeType.COCOA_TREE;
} else if (treeFeature == TreeFeatures.JUNGLE_TREE_NO_VINE) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.SMALL_JUNGLE;
+ treeType = org.bukkit.TreeType.SMALL_JUNGLE;
} else if (treeFeature == TreeFeatures.PINE) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.TALL_REDWOOD;
+ treeType = org.bukkit.TreeType.TALL_REDWOOD;
} else if (treeFeature == TreeFeatures.SPRUCE) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.REDWOOD;
+ treeType = org.bukkit.TreeType.REDWOOD;
} else if (treeFeature == TreeFeatures.ACACIA) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.ACACIA;
+ treeType = org.bukkit.TreeType.ACACIA;
} else if (treeFeature == TreeFeatures.BIRCH || treeFeature == TreeFeatures.BIRCH_BEES_005) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.BIRCH;
+ treeType = org.bukkit.TreeType.BIRCH;
} else if (treeFeature == TreeFeatures.SUPER_BIRCH_BEES_0002) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.TALL_BIRCH;
+ treeType = org.bukkit.TreeType.TALL_BIRCH;
} else if (treeFeature == TreeFeatures.SWAMP_OAK) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.SWAMP;
+ treeType = org.bukkit.TreeType.SWAMP;
} else if (treeFeature == TreeFeatures.FANCY_OAK || treeFeature == TreeFeatures.FANCY_OAK_BEES_005) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.BIG_TREE;
+ treeType = org.bukkit.TreeType.BIG_TREE;
} else if (treeFeature == TreeFeatures.JUNGLE_BUSH) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.JUNGLE_BUSH;
+ treeType = org.bukkit.TreeType.JUNGLE_BUSH;
} else if (treeFeature == TreeFeatures.DARK_OAK) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.DARK_OAK;
+ treeType = org.bukkit.TreeType.DARK_OAK;
} else if (treeFeature == TreeFeatures.MEGA_SPRUCE) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.MEGA_REDWOOD;
+ treeType = org.bukkit.TreeType.MEGA_REDWOOD;
} else if (treeFeature == TreeFeatures.MEGA_PINE) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.MEGA_PINE;
+ treeType = org.bukkit.TreeType.MEGA_PINE;
} else if (treeFeature == TreeFeatures.MEGA_JUNGLE_TREE) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.JUNGLE;
+ treeType = org.bukkit.TreeType.JUNGLE;
} else if (treeFeature == TreeFeatures.AZALEA_TREE) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.AZALEA;
+ treeType = org.bukkit.TreeType.AZALEA;
} else if (treeFeature == TreeFeatures.MANGROVE) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.MANGROVE;
+ treeType = org.bukkit.TreeType.MANGROVE;
} else if (treeFeature == TreeFeatures.TALL_MANGROVE) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.TALL_MANGROVE;
+ treeType = org.bukkit.TreeType.TALL_MANGROVE;
} else if (treeFeature == TreeFeatures.CHERRY || treeFeature == TreeFeatures.CHERRY_BEES_005) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.CHERRY;
+ treeType = org.bukkit.TreeType.CHERRY;
} else if (treeFeature == TreeFeatures.PALE_OAK || treeFeature == TreeFeatures.PALE_OAK_BONEMEAL) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.PALE_OAK;
+ treeType = org.bukkit.TreeType.PALE_OAK;
} else if (treeFeature == TreeFeatures.PALE_OAK_CREAKING) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.PALE_OAK_CREAKING;
+ treeType = org.bukkit.TreeType.PALE_OAK_CREAKING;
} else {
throw new IllegalArgumentException("Unknown tree generator " + treeFeature);
}
+ net.minecraft.world.level.block.SaplingBlock.treeTypeRT.set(treeType); // SparklyPaper - parallel world ticking
+ // SparklyPaper end
}
// CraftBukkit end
}
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
index 761fdcd4a4e18f45547afd8edff44f61c6eeacb4..17fb7161171cbe44d145cf0065826f49183880b0 100644
--- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -360,6 +360,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
@Nullable
public BlockState setBlockState(BlockPos pos, BlockState state, boolean isMoving, boolean doPlace) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.level, pos, "Updating block asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
// CraftBukkit end
int y = pos.getY();
LevelChunkSection section = this.getSection(this.getSectionIndex(y));
diff --git a/net/minecraft/world/level/entity/EntityTickList.java b/net/minecraft/world/level/entity/EntityTickList.java
index 423779a2b690f387a4f0bd07b97b50e0baefda76..24f9632e73d73c2ad68ebf30eb0e4cca7befae2d 100644
--- a/net/minecraft/world/level/entity/EntityTickList.java
+++ b/net/minecraft/world/level/entity/EntityTickList.java
@@ -10,17 +10,26 @@ import net.minecraft.world.entity.Entity;
public class EntityTickList {
private final ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet<net.minecraft.world.entity.Entity> entities = new ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet<>(); // Paper - rewrite chunk system
+ // SparklyPaper start - parallel world ticking
+ // Used to track async entity additions/removals/loops
+ private final net.minecraft.server.level.ServerLevel serverLevel;
+ public EntityTickList(net.minecraft.server.level.ServerLevel serverLevel) {
+ this.serverLevel = serverLevel;
+ }
+ // SparklyPaper end
private void ensureActiveIsNotIterated() {
// Paper - rewrite chunk system
}
public void add(Entity entity) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(entity, "Asynchronous entity ticklist addition"); // Paper // SparklyPaper - parallel world ticking (additional concurrency issues logs)
this.ensureActiveIsNotIterated();
this.entities.add(entity); // Paper - rewrite chunk system
}
public void remove(Entity entity) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(entity, "Asynchronous entity ticklist removal"); // Paper // SparklyPaper - parallel world ticking (additional concurrency issues logs)
this.ensureActiveIsNotIterated();
this.entities.remove(entity); // Paper - rewrite chunk system
}
@@ -30,6 +39,7 @@ public class EntityTickList {
}
public void forEach(Consumer<Entity> entity) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverLevel, "Asynchronous entity ticklist iteration"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
// Paper start - rewrite chunk system
// To ensure nothing weird happens with dimension travelling, do not iterate over new entries...
// (by dfl iterator() is configured to not iterate over new entries)
diff --git a/net/minecraft/world/level/saveddata/maps/MapIndex.java b/net/minecraft/world/level/saveddata/maps/MapIndex.java
index ffe604f8397a002800e6ecc2f878d0f6f1c98703..ee022449c1e00a106687496a1dfb98fc0d86fb72 100644
--- a/net/minecraft/world/level/saveddata/maps/MapIndex.java
+++ b/net/minecraft/world/level/saveddata/maps/MapIndex.java
@@ -34,17 +34,20 @@ public class MapIndex extends SavedData {
@Override
public CompoundTag save(CompoundTag tag, HolderLookup.Provider registries) {
+ synchronized (this.usedAuxIds) { // SparklyPaper start - make map data thread-safe
for (Entry<String> entry : this.usedAuxIds.object2IntEntrySet()) {
tag.putInt(entry.getKey(), entry.getIntValue());
}
-
+ } // SparklyPaper end - make map data thread-safe
return tag;
}
public MapId getFreeAuxValueForMap() {
+ synchronized (this.usedAuxIds) { // SparklyPaper start - make map data thread-safe
int i = this.usedAuxIds.getInt("map") + 1;
this.usedAuxIds.put("map", i);
this.setDirty();
return new MapId(i);
+ } // SparklyPaper end - make map data thread-safe
}
}

View File

@@ -0,0 +1,19 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Sat, 1 Jun 2024 01:22:41 -0300
Subject: [PATCH] SPARKLYPOWER Remap SparklyPower hacky legacy NBT tags
This is only useful for us in SparklyPower, but yeah...
diff --git a/ca/spottedleaf/dataconverter/minecraft/converters/itemstack/ConverterItemStackToDataComponents.java b/ca/spottedleaf/dataconverter/minecraft/converters/itemstack/ConverterItemStackToDataComponents.java
index 2d29d89cc45866822189a62bffbe1a8fe57c477b..688230454b42fd7da72833ca8fe631e6fba06eb9 100644
--- a/ca/spottedleaf/dataconverter/minecraft/converters/itemstack/ConverterItemStackToDataComponents.java
+++ b/ca/spottedleaf/dataconverter/minecraft/converters/itemstack/ConverterItemStackToDataComponents.java
@@ -1221,6 +1221,7 @@ public final class ConverterItemStackToDataComponents {
ret.setString("id", this.id);
ret.setInt("count", this.count);
if (!this.tag.isEmpty()) {
+ net.sparklypower.sparklypaper.LegacyNBTRemapper.INSTANCE.remap(this.tag); // SparklyPaper - Remap SparklyPower hacky legacy NBT tags
this.components.setMap("minecraft:custom_data", this.tag);
}

View File

@@ -0,0 +1,63 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Wed, 12 Jun 2024 11:53:09 -0300
Subject: [PATCH] SPARKLYPOWER Add custom blocks
diff --git a/net/minecraft/world/level/block/Blocks.java b/net/minecraft/world/level/block/Blocks.java
index 0401a5e88fe7840ae667748409411ab73888799c..d83e8b92b77f6b8179b24f07eae528dda5f12ed2 100644
--- a/net/minecraft/world/level/block/Blocks.java
+++ b/net/minecraft/world/level/block/Blocks.java
@@ -6775,6 +6775,52 @@ public class Blocks {
public static final Block POTTED_CLOSED_EYEBLOSSOM = register(
"potted_closed_eyeblossom", properties -> new FlowerPotBlock(CLOSED_EYEBLOSSOM, properties), flowerPotProperties().randomTicks()
);
+ // SparklyPaper start - SparklyPower Survival custom blocks
+ // Blocks' strength should be synced with the replaced block on the client side!
+ public static final Block SPARKLYPOWER_RAINBOW_WOOL = register(
+ "sparklypower_rainbow_wool",
+ Block::new,
+ BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_RED).strength(0.5F).sound(SoundType.GRASS)
+ );
+ public static final Block SPARKLYPOWER_RAINBOW_CONCRETE = register(
+ "sparklypower_rainbow_concrete",
+ Block::new,
+ BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_RED).strength(0.5F).sound(SoundType.GRASS)
+ );
+ public static final Block SPARKLYPOWER_RAINBOW_TERRACOTTA = register(
+ "sparklypower_rainbow_terracotta",
+ Block::new,
+ BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_RED).strength(0.5F).sound(SoundType.GRASS)
+ );
+ public static final Block SPARKLYPOWER_ASPHALT_SERVER = register(
+ "sparklypower_asphalt_server",
+ Block::new,
+ BlockBehaviour.Properties.of()
+ .mapColor(MapColor.COLOR_BLACK)
+ .instrument(NoteBlockInstrument.BASEDRUM)
+ .requiresCorrectToolForDrops()
+ .strength(1.5F, 6.0F)
+ );
+ public static final Block SPARKLYPOWER_ASPHALT_SERVER_SLAB = register(
+ "sparklypower_asphalt_server_slab",
+ SlabBlock::new,
+ BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_BLACK).instrument(NoteBlockInstrument.BASEDRUM).requiresCorrectToolForDrops().strength(2.0F, 6.0F)
+ );
+ public static final Block SPARKLYPOWER_ASPHALT_PLAYER = register(
+ "sparklypower_asphalt_player",
+ Block::new,
+ BlockBehaviour.Properties.of()
+ .mapColor(MapColor.COLOR_BLACK)
+ .instrument(NoteBlockInstrument.BASEDRUM)
+ .requiresCorrectToolForDrops()
+ .strength(1.5F, 6.0F)
+ );
+ public static final Block SPARKLYPOWER_ASPHALT_PLAYER_SLAB = register(
+ "sparklypower_asphalt_player_slab",
+ SlabBlock::new,
+ BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_BLACK).instrument(NoteBlockInstrument.BASEDRUM).requiresCorrectToolForDrops().strength(2.0F, 6.0F)
+ );
+ // SparklyPaper end
private static ToIntFunction<BlockState> litBlockEmission(int lightValue) {
return state -> state.getValue(BlockStateProperties.LIT) ? lightValue : 0;

View File

@@ -0,0 +1,50 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Sat, 12 Jun 2021 16:40:34 +0200
Subject: [PATCH] new fork who dis - Rebrand to SparklyPaper and Build Changes
diff --git a/src/main/java/org/bukkit/craftbukkit/util/Versioning.java b/src/main/java/org/bukkit/craftbukkit/util/Versioning.java
index 774556a62eb240da42e84db4502e2ed43495be17..9bc7b99b5b39a8ffe4118b8d86f5b8065c4fe460 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/Versioning.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/Versioning.java
@@ -11,7 +11,7 @@ public final class Versioning {
public static String getBukkitVersion() {
String result = "Unknown-Version";
- InputStream stream = Bukkit.class.getClassLoader().getResourceAsStream("META-INF/maven/io.papermc.paper/paper-api/pom.properties");
+ InputStream stream = Bukkit.class.getClassLoader().getResourceAsStream("META-INF/maven/net.sparklypower.sparklypaper/sparklypaper-api/pom.properties"); // SparklyPaper
Properties properties = new Properties();
if (stream != null) {
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
index a9339f59f81dff307317ae4afdff0dc296febcc9..2ce97bee092dd7a6d898836376c442b114d54003 100644
--- a/src/main/java/org/spigotmc/WatchdogThread.java
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
@@ -77,14 +77,14 @@ public class WatchdogThread extends Thread {
if (isLongTimeout) {
// Paper end
logger.log(Level.SEVERE, "------------------------------");
- logger.log(Level.SEVERE, "The server has stopped responding! This is (probably) not a Paper bug."); // Paper
+ logger.log(Level.SEVERE, "The server has stopped responding! This is (probably) not a Paper bug. This could be a SparklyPaper bug and, in that case, MrPowerGamerBR probably made a fucky wucky!"); // SparklyPaper - branding changes // Paper
logger.log(Level.SEVERE, "If you see a plugin in the Server thread dump below, then please report it to that author");
logger.log(Level.SEVERE, "\t *Especially* if it looks like HTTP or MySQL operations are occurring");
logger.log(Level.SEVERE, "If you see a world save or edit, then it means you did far more than your server can handle at once");
logger.log(Level.SEVERE, "\t If this is the case, consider increasing timeout-time in spigot.yml but note that this will replace the crash with LARGE lag spikes");
- logger.log(Level.SEVERE, "If you are unsure or still think this is a Paper bug, please report this to https://github.com/PaperMC/Paper/issues");
+ logger.log(Level.SEVERE, "If you are unsure or still think this is a SparklyPaper bug, please report this to https://github.com/SparklyPower/SparklyPaper/issues - and if you think this is a Paper bug, please report this to https://github.com/PaperMC/Paper/issues\""); // SparklyPaper - branding changes
logger.log(Level.SEVERE, "Be sure to include ALL relevant console errors and Minecraft crash reports");
- logger.log(Level.SEVERE, "Paper version: " + Bukkit.getServer().getVersion());
+ logger.log(Level.SEVERE, "SparklyPaper version: " + Bukkit.getServer().getVersion()); // SparklyPaper - branding changes
if (net.minecraft.world.level.Level.lastPhysicsProblem != null) {
logger.log(Level.SEVERE, "------------------------------");
@@ -109,7 +109,7 @@ public class WatchdogThread extends Thread {
}
// Paper end - Different message for short timeout
logger.log(Level.SEVERE, "------------------------------");
- logger.log(Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):"); // Paper
+ logger.log(Level.SEVERE, "Server thread dump (Look for plugins here before reporting to SparklyPaper!):"); // SparklyPaper - branding changes // Paper
FeatureHooks.dumpAllChunkLoadInfo(MinecraftServer.getServer(), isLongTimeout); // Paper - log detailed tick information
WatchdogThread.dumpThread(ManagementFactory.getThreadMXBean().getThreadInfo(MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE), logger);
logger.log(Level.SEVERE, "------------------------------");

View File

@@ -0,0 +1,61 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Mon, 13 Jan 2025 13:20:49 -0300
Subject: [PATCH] SparklyPaper config files
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index d2de789967f8f1942e91d9e4c547113a5e31382b..6f3add0ee20978d84fba844955eefe5fdad71af8 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -111,6 +111,7 @@ import net.minecraft.world.level.storage.PlayerDataStorage;
import net.minecraft.world.level.storage.PrimaryLevelData;
import net.minecraft.world.level.validation.ContentValidationException;
import net.minecraft.world.phys.Vec3;
+import net.sparklypower.sparklypaper.configs.SparklyPaperConfigUtils;
import org.bukkit.BanList;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
@@ -1087,6 +1088,7 @@ public final class CraftServer implements Server {
org.spigotmc.SpigotConfig.init((File) this.console.options.valueOf("spigot-settings")); // Spigot
this.console.paperConfigurations.reloadConfigs(this.console);
+ net.sparklypower.sparklypaper.configs.SparklyPaperConfigUtils.INSTANCE.init((File) console.options.valueOf("sparklypaper-settings")); // SparklyPaper
for (ServerLevel world : this.console.getAllLevels()) {
// world.serverLevelData.setDifficulty(config.difficulty); // Paper - per level difficulty
world.setSpawnSettings(world.serverLevelData.getDifficulty() != Difficulty.PEACEFUL && config.spawnMonsters); // Paper - per level difficulty (from MinecraftServer#setDifficulty(ServerLevel, Difficulty, boolean))
@@ -1102,6 +1104,7 @@ public final class CraftServer implements Server {
}
}
world.spigotConfig.init(); // Spigot
+ world.sparklyPaperConfig = SparklyPaperConfigUtils.INSTANCE.getWorldSettings(world.serverLevelData.getLevelName()); // SparklyPaper
}
Plugin[] pluginClone = pluginManager.getPlugins().clone(); // Paper
@@ -1119,6 +1122,7 @@ public final class CraftServer implements Server {
org.spigotmc.SpigotConfig.registerCommands(); // Spigot
io.papermc.paper.command.PaperCommands.registerCommands(this.console); // Paper
this.spark.registerCommandBeforePlugins(this); // Paper - spark
+ net.sparklypower.sparklypaper.SparklyPaperCommands.INSTANCE.registerCommands(this.console); // SparklyPaper
this.overrideAllCommandBlockCommands = this.commandsConfiguration.getStringList("command-block-overrides").contains("*");
this.ignoreVanillaPermissions = this.commandsConfiguration.getBoolean("ignore-vanilla-permissions");
diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java
index ecb0fcd1f3b3f3d7751eded3cdf0977c1889c9ed..10f84fb104adc6e8cf57f12e9388398e32711f4a 100644
--- a/src/main/java/org/bukkit/craftbukkit/Main.java
+++ b/src/main/java/org/bukkit/craftbukkit/Main.java
@@ -176,6 +176,14 @@ public class Main {
.describedAs("Jar file");
// Paper end
+ // SparklyPaper Start
+ acceptsAll(asList("sparklypaper", "sparklypaper-settings"), "File for SparklyPaper settings")
+ .withRequiredArg()
+ .ofType(File.class)
+ .defaultsTo(new File("sparklypaper.yml"))
+ .describedAs("Yml file");
+ // SparklyPaper end
+
// Paper start
acceptsAll(asList("server-name"), "Name of the server")
.withRequiredArg()

View File

@@ -0,0 +1,19 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Mon, 13 Jan 2025 14:27:53 -0300
Subject: [PATCH] Fix concurrency issues when using imageToBytes
diff --git a/src/main/java/org/bukkit/craftbukkit/map/CraftMapColorCache.java b/src/main/java/org/bukkit/craftbukkit/map/CraftMapColorCache.java
index 8149b9c51b78eb5c689b7218a2ca3aab60e73bcf..c983d8d7e79d55c9757add8ac1093a0a9d98e5b3 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() { // SparklyPaper - fix concurrency issues when using "imageToBytes" in multiple threads
return this.cached || (!this.running.get() && this.initCache().isDone());
}

View File

@@ -0,0 +1,95 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Sun, 19 Nov 2023 12:35:16 -0300
Subject: [PATCH] Skip EntityScheduler's executeTick checks if there isn't any
tasks to be run
On each tick, Paper runs EntityScheduler's executeTick of each entity. This is a bit expensive, due to ArrayDeque's size() call because it ain't a simple "get the current queue size" function, due to the thread checks, and because it needs to iterate all entities in all worlds.
To avoid the hefty ArrayDeque's size() call, we check if we *really* need to execute the executeTick, by adding all entities with scheduled tasks to a global set.
Most entities won't have any scheduled tasks, so this is a nice performance bonus. These optimizations, however, wouldn't work in a Folia environment, but because in SparklyPaper executeTick is always executed on the main thread, it ain't an issue for us (yay).
diff --git a/src/main/java/io/papermc/paper/threadedregions/EntityScheduler.java b/src/main/java/io/papermc/paper/threadedregions/EntityScheduler.java
index c03608fec96b51e1867f43d8f42e5aefb1520e46..15b21fa3907db1b77ed5b5d1050a37f42d27d5ab 100644
--- a/src/main/java/io/papermc/paper/threadedregions/EntityScheduler.java
+++ b/src/main/java/io/papermc/paper/threadedregions/EntityScheduler.java
@@ -36,6 +36,7 @@ public final class EntityScheduler {
* The Entity. Note that it is the CraftEntity, since only that class properly tracks world transfers.
*/
public final CraftEntity entity;
+ public final net.minecraft.server.MinecraftServer server; // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run
private static final record ScheduledTask(Consumer<? extends Entity> run, Consumer<? extends Entity> retired) {}
@@ -46,7 +47,8 @@ public final class EntityScheduler {
private final ArrayDeque<ScheduledTask> currentlyExecuting = new ArrayDeque<>();
- public EntityScheduler(final CraftEntity entity) {
+ public EntityScheduler(final net.minecraft.server.MinecraftServer server, final CraftEntity entity) { // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run
+ this.server = Validate.notNull(server);
this.entity = Validate.notNull(entity);
}
@@ -61,14 +63,16 @@ public final class EntityScheduler {
* @throws IllegalStateException If the scheduler is already retired.
*/
public void retire() {
+ final Entity thisEntity = this.entity.getHandleRaw(); // SparklyPaper - 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;
+ this.server.entitiesWithScheduledTasks.remove(thisEntity); // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run
}
- final Entity thisEntity = this.entity.getHandleRaw();
+ // final Entity thisEntity = this.entity.getHandleRaw(); // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run (moved up)
// correctly handle and order retiring while running executeTick
for (int i = 0, len = this.currentlyExecuting.size(); i < len; ++i) {
@@ -124,6 +128,7 @@ public final class EntityScheduler {
if (this.tickCount == RETIRED_TICK_COUNT) {
return false;
}
+ this.server.entitiesWithScheduledTasks.add(this.entity.getHandleRaw()); // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run
this.oneTimeDelayed.computeIfAbsent(this.tickCount + Math.max(1L, delay), (final long keyInMap) -> {
return new ArrayList<>();
}).add(task);
@@ -143,6 +148,13 @@ public final class EntityScheduler {
TickThread.ensureTickThread(thisEntity, "May not tick entity scheduler asynchronously");
final List<ScheduledTask> toRun;
synchronized (this.stateLock) {
+ // SparklyPaper start - skip EntityScheduler's executeTick checks if there isn't any tasks to be run
+ // Do we *really* have scheduled tasks tho?
+ if (this.currentlyExecuting.isEmpty() && this.oneTimeDelayed.isEmpty()) { // Check if we have any pending tasks and, if not, skip!
+ this.server.entitiesWithScheduledTasks.remove(thisEntity); // We don't! Bye bye!!
+ return;
+ }
+ // SparklyPaper end
if (this.tickCount == RETIRED_TICK_COUNT) {
throw new IllegalStateException("Ticking retired scheduler");
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
index 4562bee2e2795801862bae03d783799fc076b73e..7107acb0a4ad4c84f78185bb8da2db8506c352d1 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
@@ -74,7 +74,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
private final CraftPersistentDataContainer persistentDataContainer = new CraftPersistentDataContainer(CraftEntity.DATA_TYPE_REGISTRY);
protected net.kyori.adventure.pointer.Pointers adventure$pointers; // Paper - implement pointers
// Paper start - Folia shedulers
- public final io.papermc.paper.threadedregions.EntityScheduler taskScheduler = new io.papermc.paper.threadedregions.EntityScheduler(this);
+ public final io.papermc.paper.threadedregions.EntityScheduler taskScheduler; // = new io.papermc.paper.threadedregions.EntityScheduler(this); // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run
private final io.papermc.paper.threadedregions.scheduler.FoliaEntityScheduler apiScheduler = new io.papermc.paper.threadedregions.scheduler.FoliaEntityScheduler(this);
@Override
@@ -87,6 +87,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
this.server = server;
this.entity = entity;
this.entityType = CraftEntityType.minecraftToBukkit(entity.getType());
+ this.taskScheduler = new io.papermc.paper.threadedregions.EntityScheduler(this.entity.getServer(), this); // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run
}
public static <T extends Entity> CraftEntity getEntity(CraftServer server, T entity) {

View File

@@ -0,0 +1,37 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Mon, 13 Jan 2025 15:04:15 -0300
Subject: [PATCH] Optimize canSee checks
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index 98fc89cc7a715d35b62e13f8ecbe56c05605ca64..330e68e79f2201a5107c007d96ebd845bfa3caed 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -207,7 +207,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
private boolean hasPlayedBefore = false;
private final ConversationTracker conversationTracker = new ConversationTracker();
private final Set<String> channels = new HashSet<String>();
- 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<>(); // 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;
@@ -2255,9 +2255,16 @@ 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) || entity.isVisibleByDefault() ^ (!invertedVisibilityEntities.isEmpty() && this.invertedVisibilityEntities.containsKey(entity.getUniqueId())); // SPIGOT-7312: Can always see self // SparklyPaper - optimize canSee checks
}
+ // SparklyPaper - optimize canSee checks
+ // 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 canSeeChunkMapUpdatePlayer(org.bukkit.entity.Entity entity) {
+ return entity.isVisibleByDefault() ^ (!invertedVisibilityEntities.isEmpty() && this.invertedVisibilityEntities.containsKey(entity.getUniqueId())); // SPIGOT-7312: Can always see self // SparklyPaper - optimize canSee checks
+ }
+ // SparklyPaper end
+
public boolean canSeePlayer(UUID uuid) {
org.bukkit.entity.Entity entity = this.getServer().getPlayer(uuid);

View File

@@ -0,0 +1,57 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Mon, 6 Nov 2023 21:54:33 -0300
Subject: [PATCH] Track how much MSPT each world used
diff --git a/src/main/java/io/papermc/paper/command/MSPTCommand.java b/src/main/java/io/papermc/paper/command/MSPTCommand.java
index 8b5293b0c696ef21d0101493ffa41b60bf0bc86b..601198a33adb29316b0617d5390d1620b7c1095c 100644
--- a/src/main/java/io/papermc/paper/command/MSPTCommand.java
+++ b/src/main/java/io/papermc/paper/command/MSPTCommand.java
@@ -78,6 +78,46 @@ public final class MSPTCommand extends Command {
)
)
);
+
+ // SparklyPaper start - track world's 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 serverLevel : server.getAllLevels()) {
+ List<Component> worldTimes = new ArrayList<>();
+ worldTimes.addAll(eval(serverLevel.tickTimes5s.getTimes()));
+ worldTimes.addAll(eval(serverLevel.tickTimes10s.getTimes()));
+ worldTimes.addAll(eval(serverLevel.tickTimes60s.getTimes()));
+
+ sender.sendMessage(text().content("◴ " + serverLevel.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)
+ )
+ )
+ );
+ }
+ // SparklyPaper end
return true;
}

View File

@@ -0,0 +1,36 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Tue, 25 Jun 2024 02:52:32 -0300
Subject: [PATCH] Add CraftItemRecipeEvent
Used when a player OR a crafter block crafts an item, as an alternative to PrepareItemCraftEvent and CraftItemEvent, because both events are not triggered when a item is crafted from a crafter
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
index d7a52220e9525502163f5ee6afbadf2baaae6190..bbd000e3a3e9112e10a09e5d3ef6851b28e900fa 100644
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
@@ -1472,6 +1472,24 @@ public class CraftEventFactory {
Bukkit.getPluginManager().callEvent(crafterCraftEvent);
return crafterCraftEvent;
}
+
+ // SparklyPaper start - add CraftItemRecipeEvent
+ public static net.sparklypower.sparklypaper.event.inventory.CraftItemRecipeEvent callCraftItemRecipeEvent(net.minecraft.world.inventory.CraftingContainer container, Recipe recipe, ItemStack result) {
+ org.bukkit.inventory.ItemStack[] matrix = new org.bukkit.inventory.ItemStack[container.getItems().size()];
+ int i = 0;
+ for (ItemStack item : container.getItems()) {
+ matrix[i] = CraftItemStack.asCraftMirror(item);
+ i++;
+ }
+ org.bukkit.inventory.ItemStack bukkitResult = CraftItemStack.asCraftMirror(result);
+
+ net.sparklypower.sparklypaper.event.inventory.CraftItemRecipeEvent event = new net.sparklypower.sparklypaper.event.inventory.CraftItemRecipeEvent(matrix, recipe, bukkitResult);
+ Bukkit.getPluginManager().callEvent(event);
+
+ return event;
+ }
+ // SparklyPaper end
+
// Paper start
@Deprecated
public static com.destroystokyo.paper.event.entity.ProjectileCollideEvent callProjectileCollideEvent(Entity entity, EntityHitResult position) {

View File

@@ -0,0 +1,37 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Fri, 21 Jun 2024 18:07:25 -0300
Subject: [PATCH] Allow item version downgrades
The server WON'T convert the item data from a newer version to an older version, so proceed with caution
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
index bd8005cd3a52532f4cb2e123da473f1490b59dbb..2d23110b61024847c8ed554ac5652982a35dd74b 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
@@ -85,6 +85,7 @@ import org.bukkit.potion.PotionType;
public final class CraftMagicNumbers implements UnsafeValues {
public static final CraftMagicNumbers INSTANCE = new CraftMagicNumbers();
public static final boolean DISABLE_OLD_API_SUPPORT = Boolean.getBoolean("paper.disableOldApiSupport"); // Paper
+ private static final boolean JUST_CORRUPT_IT = Boolean.getBoolean("sparklypaper.ignoreItemDataVersion"); // SparklyPaper - allow item downgrades
private final Commodore commodore = new Commodore();
@@ -246,7 +247,7 @@ public final class CraftMagicNumbers implements UnsafeValues {
@Override
public Material getMaterial(String material, int version) {
Preconditions.checkArgument(material != null, "material == null");
- Preconditions.checkArgument(version <= this.getDataVersion(), "Newer version! Server downgrades are not supported!");
+ Preconditions.checkArgument(JUST_CORRUPT_IT || version <= this.getDataVersion(), "Newer version! Server downgrades are not supported!"); // SparklyPaper - allow item downgrades
// Fastpath up to date materials
if (version == this.getDataVersion()) {
@@ -676,7 +677,7 @@ public final class CraftMagicNumbers implements UnsafeValues {
throw new RuntimeException(ex);
}
int dataVersion = compound.getInt("DataVersion");
- Preconditions.checkArgument(dataVersion <= getDataVersion(), "Newer version! Server downgrades are not supported!");
+ Preconditions.checkArgument(JUST_CORRUPT_IT || dataVersion <= getDataVersion(), "Newer version! Server downgrades are not supported!"); // SparklyPaper - allow item downgrades
return compound;
}

View File

@@ -0,0 +1,627 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Tue, 7 Nov 2023 01:34:14 -0300
Subject: [PATCH] Parallel world ticking
"mom can we have folia?" "we already have folia at home" folia at home:
diff --git a/src/main/java/ca/spottedleaf/moonrise/common/util/TickThread.java b/src/main/java/ca/spottedleaf/moonrise/common/util/TickThread.java
index 157e5edb507d6d2a922833c70a1c27abc93c9c34..f47b49b02f664725d310887e65477deb07414bde 100644
--- a/src/main/java/ca/spottedleaf/moonrise/common/util/TickThread.java
+++ b/src/main/java/ca/spottedleaf/moonrise/common/util/TickThread.java
@@ -14,6 +14,7 @@ import java.util.concurrent.atomic.AtomicInteger;
public class TickThread extends Thread {
private static final Logger LOGGER = LoggerFactory.getLogger(TickThread.class);
+ public static final boolean HARD_THROW = !Boolean.getBoolean("sparklypaper.disableHardThrow"); // SparklyPaper - parallel world ticking - THIS SHOULD NOT BE DISABLED SINCE IT CAN CAUSE DATA CORRUPTION!!! Anyhow, for production servers, if you want to make a test run to see if the server could crash, you can test it with this disabled
/**
* @deprecated
@@ -22,14 +23,15 @@ public class TickThread extends Thread {
public static void ensureTickThread(final String reason) {
if (!isTickThread()) {
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
- throw new IllegalStateException(reason);
+ if (HARD_THROW)
+ throw new IllegalStateException(reason);
}
}
public static void ensureTickThread(final Level world, final BlockPos pos, final String reason) {
if (!isTickThreadFor(world, pos)) {
final String ex = "Thread " + Thread.currentThread().getName() + " failed main thread check: " +
- reason + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + pos;
+ reason + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + pos + " - " + getTickThreadInformation(world.getServer());
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
@@ -38,7 +40,7 @@ public class TickThread extends Thread {
public static void ensureTickThread(final Level world, final BlockPos pos, final int blockRadius, final String reason) {
if (!isTickThreadFor(world, pos, blockRadius)) {
final String ex = "Thread " + Thread.currentThread().getName() + " failed main thread check: " +
- reason + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + pos + ", block_radius=" + blockRadius;
+ reason + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + pos + ", block_radius=" + blockRadius + " - " + getTickThreadInformation(world.getServer());
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
@@ -56,7 +58,7 @@ public class TickThread extends Thread {
public static void ensureTickThread(final Level world, final int chunkX, final int chunkZ, final String reason) {
if (!isTickThreadFor(world, chunkX, chunkZ)) {
final String ex = "Thread " + Thread.currentThread().getName() + " failed main thread check: " +
- reason + ", world=" + WorldUtil.getWorldName(world) + ", chunk_pos=" + new ChunkPos(chunkX, chunkZ);
+ reason + ", world=" + WorldUtil.getWorldName(world) + ", chunk_pos=" + new ChunkPos(chunkX, chunkZ) + " - " + getTickThreadInformation(world.getServer());
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
@@ -65,7 +67,7 @@ public class TickThread extends Thread {
public static void ensureTickThread(final Entity entity, final String reason) {
if (!isTickThreadFor(entity)) {
final String ex = "Thread " + Thread.currentThread().getName() + " failed main thread check: " +
- reason + ", entity=" + entity;
+ reason + ", entity=" + entity + " - " + getTickThreadInformation(entity.getServer());
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
@@ -74,7 +76,7 @@ public class TickThread extends Thread {
public static void ensureTickThread(final Level world, final AABB aabb, final String reason) {
if (!isTickThreadFor(world, aabb)) {
final String ex = "Thread " + Thread.currentThread().getName() + " failed main thread check: " +
- reason + ", world=" + WorldUtil.getWorldName(world) + ", aabb=" + aabb;
+ reason + ", world=" + WorldUtil.getWorldName(world) + ", aabb=" + aabb + " - " + getTickThreadInformation(world.getServer());
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
@@ -83,12 +85,74 @@ public class TickThread extends Thread {
public static void ensureTickThread(final Level world, final double blockX, final double blockZ, final String reason) {
if (!isTickThreadFor(world, blockX, blockZ)) {
final String ex = "Thread " + Thread.currentThread().getName() + " failed main thread check: " +
- reason + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + new Vec3(blockX, 0.0, blockZ);
+ reason + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + new Vec3(blockX, 0.0, blockZ) + " - " + getTickThreadInformation(world.getServer());
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
}
+ // SparklyPaper - parallel world ticking
+ // This is an additional method to check if the tick thread is bound to a specific world because, by default, Paper's isTickThread methods do not provide this information
+ // Because we only tick worlds in parallel (instead of regions), we can use this for our checks
+ public static void ensureTickThread(final net.minecraft.server.level.ServerLevel world, final String reason) {
+ if (!isTickThreadFor(world)) {
+ LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason + " @ world " + world.getWorld().getName() + " - " + getTickThreadInformation(world.getServer()), new Throwable());
+ if (HARD_THROW)
+ throw new IllegalStateException(reason);
+ }
+ }
+
+ // SparklyPaper - parallel world ticking
+ // This is an additional method to check if it is a tick thread but ONLY a tick thread
+ public static void ensureOnlyTickThread(final String reason) {
+ boolean isTickThread = isTickThread();
+ boolean isServerLevelTickThread = isServerLevelTickThread();
+ if (!isTickThread || isServerLevelTickThread) {
+ LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread ONLY tick thread check: " + reason, new Throwable());
+ if (HARD_THROW)
+ throw new IllegalStateException(reason);
+ }
+ }
+
+ // SparklyPaper - parallel world ticking
+ // This is an additional method to check if the tick thread is bound to a specific world or if it is an async thread.
+ public static void ensureTickThreadOrAsyncThread(final net.minecraft.server.level.ServerLevel world, final String reason) {
+ boolean isValidTickThread = isTickThreadFor(world);
+ boolean isAsyncThread = !isTickThread();
+ boolean isValid = isAsyncThread || isValidTickThread;
+ if (!isValid) {
+ LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread or async thread check: " + reason + " @ world " + world.getWorld().getName() + " - " + getTickThreadInformation(world.getServer()), new Throwable());
+ if (HARD_THROW)
+ throw new IllegalStateException(reason);
+ }
+ }
+
+ public static String getTickThreadInformation(net.minecraft.server.MinecraftServer minecraftServer) {
+ StringBuilder sb = new StringBuilder();
+ Thread currentThread = Thread.currentThread();
+ sb.append("Is tick thread? ");
+ sb.append(currentThread instanceof TickThread);
+ sb.append("; Is server level tick thread? ");
+ sb.append(currentThread instanceof ServerLevelTickThread);
+ if (currentThread instanceof ServerLevelTickThread serverLevelTickThread) {
+ sb.append("; Currently ticking level: ");
+ if (serverLevelTickThread.currentlyTickingServerLevel != null) {
+ sb.append(serverLevelTickThread.currentlyTickingServerLevel.getWorld().getName());
+ } else {
+ sb.append("null");
+ }
+ }
+ sb.append("; Is iterating over levels? ");
+ sb.append(minecraftServer.isIteratingOverLevels);
+ sb.append("; Are we going to hard throw? ");
+ sb.append(HARD_THROW);
+ return sb.toString();
+ }
+
+ public static boolean isServerLevelTickThread() {
+ return Thread.currentThread() instanceof ServerLevelTickThread;
+ }
+
public final int id; /* We don't override getId as the spec requires that it be unique (with respect to all other threads) */
private static final AtomicInteger ID_GENERATOR = new AtomicInteger();
@@ -123,7 +187,11 @@ public class TickThread extends Thread {
}
public static boolean isTickThreadFor(final Level world, final BlockPos pos) {
- return isTickThread();
+ Thread currentThread = Thread.currentThread();
+
+ if (currentThread instanceof ServerLevelTickThread serverLevelTickThread) {
+ return serverLevelTickThread.currentlyTickingServerLevel == world;
+ } else return currentThread instanceof TickThread;
}
public static boolean isTickThreadFor(final Level world, final BlockPos pos, final int blockRadius) {
@@ -131,38 +199,103 @@ public class TickThread extends Thread {
}
public static boolean isTickThreadFor(final Level world, final ChunkPos pos) {
- return isTickThread();
+ Thread currentThread = Thread.currentThread();
+
+ if (currentThread instanceof ServerLevelTickThread serverLevelTickThread) {
+ return serverLevelTickThread.currentlyTickingServerLevel == world;
+ } else return currentThread instanceof TickThread;
}
public static boolean isTickThreadFor(final Level world, final Vec3 pos) {
- return isTickThread();
+ Thread currentThread = Thread.currentThread();
+
+ if (currentThread instanceof ServerLevelTickThread serverLevelTickThread) {
+ return serverLevelTickThread.currentlyTickingServerLevel == world;
+ } else return currentThread instanceof TickThread;
}
public static boolean isTickThreadFor(final Level world, final int chunkX, final int chunkZ) {
- return isTickThread();
+ Thread currentThread = Thread.currentThread();
+
+ if (currentThread instanceof ServerLevelTickThread serverLevelTickThread) {
+ return serverLevelTickThread.currentlyTickingServerLevel == world;
+ } else return currentThread instanceof TickThread;
}
public static boolean isTickThreadFor(final Level world, final AABB aabb) {
- return isTickThread();
+ Thread currentThread = Thread.currentThread();
+
+ if (currentThread instanceof ServerLevelTickThread serverLevelTickThread) {
+ return serverLevelTickThread.currentlyTickingServerLevel == world;
+ } else return currentThread instanceof TickThread;
}
public static boolean isTickThreadFor(final Level world, final double blockX, final double blockZ) {
- return isTickThread();
+ Thread currentThread = Thread.currentThread();
+
+ if (currentThread instanceof ServerLevelTickThread serverLevelTickThread) {
+ return serverLevelTickThread.currentlyTickingServerLevel == world;
+ } else return currentThread instanceof TickThread;
}
public static boolean isTickThreadFor(final Level world, final Vec3 position, final Vec3 deltaMovement, final int buffer) {
- return isTickThread();
+ Thread currentThread = Thread.currentThread();
+
+ if (currentThread instanceof ServerLevelTickThread serverLevelTickThread) {
+ return serverLevelTickThread.currentlyTickingServerLevel == world;
+ } else return currentThread instanceof TickThread;
}
public static boolean isTickThreadFor(final Level world, final int fromChunkX, final int fromChunkZ, final int toChunkX, final int toChunkZ) {
- return isTickThread();
+ Thread currentThread = Thread.currentThread();
+
+ if (currentThread instanceof ServerLevelTickThread serverLevelTickThread) {
+ return serverLevelTickThread.currentlyTickingServerLevel == world;
+ } else return currentThread instanceof TickThread;
}
public static boolean isTickThreadFor(final Level world, final int chunkX, final int chunkZ, final int radius) {
- return isTickThread();
+ Thread currentThread = Thread.currentThread();
+
+ if (currentThread instanceof ServerLevelTickThread serverLevelTickThread) {
+ return serverLevelTickThread.currentlyTickingServerLevel == world;
+ } else return currentThread instanceof TickThread;
+ }
+
+ // SparklyPaper - parallel world ticking
+ // This is an additional method to check if the tick thread is bound to a specific world because, by default, Paper's isTickThread methods do not provide this information
+ // Because we only tick worlds in parallel (instead of regions), we can use this for our checks
+ public static boolean isTickThreadFor(final Level world) {
+ Thread currentThread = Thread.currentThread();
+
+ if (currentThread instanceof ServerLevelTickThread serverLevelTickThread) {
+ return serverLevelTickThread.currentlyTickingServerLevel == world;
+ } else return currentThread instanceof TickThread;
}
public static boolean isTickThreadFor(final Entity entity) {
- return isTickThread();
+ if (entity == null) {
+ return true;
+ }
+
+ Thread currentThread = Thread.currentThread();
+
+ if (currentThread instanceof ServerLevelTickThread serverLevelTickThread) {
+ return serverLevelTickThread.currentlyTickingServerLevel == entity.level();
+ } else return currentThread instanceof TickThread;
+ }
+
+ // SparklyPaper start - parallel world ticking
+ public static class ServerLevelTickThread extends TickThread {
+ public ServerLevelTickThread(String name) {
+ super(name);
+ }
+
+ public ServerLevelTickThread(Runnable run, String name) {
+ super(run, name);
+ }
+
+ public net.minecraft.server.level.ServerLevel currentlyTickingServerLevel;
}
+ // SparklyPaper end
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index ba32db69c95b325a781cb2dff4200e4464a11baf..219a87300838cb4d078553dda666b3207f8191b8 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -457,7 +457,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
}
private boolean unloadChunk0(int x, int z, boolean save) {
- org.spigotmc.AsyncCatcher.catchOp("chunk unload"); // Spigot
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, x, z, "Cannot unload chunk asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
if (!this.isChunkLoaded(x, z)) {
return true;
}
@@ -480,6 +480,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
if (!unloadChunk0(x, z, false)) {
return false;
}
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, x, z, "Cannot regenerate chunk asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
warnUnsafeChunk("regenerating a faraway chunk", x, z); // Paper
final long chunkKey = ChunkCoordIntPair.pair(x, z);
@@ -501,6 +502,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public boolean refreshChunk(int x, int z) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, x, z, "Cannot refresh chunk asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
ChunkHolder playerChunk = this.world.getChunkSource().chunkMap.getVisibleChunkIfPresent(ChunkPos.asLong(x, z));
if (playerChunk == null) return false;
@@ -551,7 +553,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public boolean loadChunk(int x, int z, boolean generate) {
- org.spigotmc.AsyncCatcher.catchOp("chunk load"); // Spigot
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.getHandle(), x, z, "May not sync load chunks asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
warnUnsafeChunk("loading a faraway chunk", x, z); // Paper
ChunkAccess chunk = this.world.getChunkSource().getChunk(x, z, generate || isChunkGenerated(x, z) ? ChunkStatus.FULL : ChunkStatus.EMPTY, true); // Paper
@@ -779,6 +781,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public boolean generateTree(Location loc, TreeType type, BlockChangeDelegate delegate) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, loc.getX(), loc.getZ(), "Cannot generate tree asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
this.world.captureTreeGeneration = true;
this.world.captureBlockStates = true;
boolean grownTree = this.generateTree(loc, type);
@@ -894,6 +897,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
}
public boolean createExplosion(double x, double y, double z, float power, boolean setFire, boolean breakBlocks, Entity source, Consumer<net.minecraft.world.level.ServerExplosion> configurator) {
// Paper end - expand explosion API
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, x, z, "Cannot create explosion asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
net.minecraft.world.level.Level.ExplosionInteraction explosionType;
if (!breakBlocks) {
explosionType = net.minecraft.world.level.Level.ExplosionInteraction.NONE; // Don't break blocks
@@ -985,6 +989,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public int getHighestBlockYAt(int x, int z, org.bukkit.HeightMap heightMap) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, x >> 4, z >> 4, "Cannot retrieve chunk asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
warnUnsafeChunk("getting a faraway chunk", x >> 4, z >> 4); // Paper
// Transient load for this tick
return this.world.getChunk(x >> 4, z >> 4).getHeight(CraftHeightMap.toNMS(heightMap), x, z);
@@ -1015,6 +1020,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public void setBiome(int x, int y, int z, Holder<net.minecraft.world.level.biome.Biome> bb) {
BlockPos pos = new BlockPos(x, 0, z);
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, pos, "Cannot retrieve chunk asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
if (this.world.hasChunkAt(pos)) {
net.minecraft.world.level.chunk.LevelChunk chunk = this.world.getChunkAt(pos);
@@ -2292,6 +2298,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public void sendGameEvent(Entity sourceEntity, org.bukkit.GameEvent gameEvent, Vector position) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, position.getX(), position.getZ(), "Cannot send game event asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
getHandle().gameEvent(sourceEntity != null ? ((CraftEntity) sourceEntity).getHandle(): null, net.minecraft.core.registries.BuiltInRegistries.GAME_EVENT.get(org.bukkit.craftbukkit.util.CraftNamespacedKey.toMinecraft(gameEvent.getKey())).orElseThrow(), org.bukkit.craftbukkit.util.CraftVector.toBlockPos(position));
}
// Paper end
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
index 5cb69d0b822e11a99a96aef4f59986d083b079f4..78f9c3f3b068f638570942961ad41969abc9c028 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
@@ -75,6 +75,11 @@ public class CraftBlock implements Block {
}
public net.minecraft.world.level.block.state.BlockState getNMS() {
+ // Folia start - parallel world ticking
+ if (world instanceof ServerLevel serverWorld) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot read world asynchronously");
+ }
+ // Folia end - parallel world ticking
return this.world.getBlockState(this.position);
}
@@ -157,6 +162,11 @@ public class CraftBlock implements Block {
}
private void setData(final byte data, int flag) {
+ // SparklyPaper start - parallel world ticking
+ if (world instanceof ServerLevel serverWorld) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
+ }
+ // SparklyPaper end - parallel world ticking
this.world.setBlock(this.position, CraftMagicNumbers.getBlock(this.getType(), data), flag);
}
@@ -198,6 +208,12 @@ public class CraftBlock implements Block {
}
public static boolean setTypeAndData(LevelAccessor world, BlockPos position, net.minecraft.world.level.block.state.BlockState old, net.minecraft.world.level.block.state.BlockState blockData, boolean applyPhysics) {
+ // SparklyPaper start - parallel world ticking
+ if (world instanceof ServerLevel serverWorld) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
+ }
+ // SparklyPaper end - parallel world ticking
+
// SPIGOT-611: need to do this to prevent glitchiness. Easier to handle this here (like /setblock) than to fix weirdness in tile entity cleanup
if (old.hasBlockEntity() && blockData.getBlock() != old.getBlock()) { // SPIGOT-3725 remove old tile entity if block changes
// SPIGOT-4612: faster - just clear tile
@@ -343,18 +359,33 @@ public class CraftBlock implements Block {
@Override
public Biome getBiome() {
+ // SparklyPaper start - parallel world ticking
+ if (world instanceof ServerLevel serverWorld) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot read world asynchronously");
+ }
+ // SparklyPaper end - parallel world ticking
return this.getWorld().getBiome(this.getX(), this.getY(), this.getZ());
}
// Paper start
@Override
public Biome getComputedBiome() {
+ // SparklyPaper start - parallel world ticking
+ if (world instanceof ServerLevel serverWorld) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot read world asynchronously");
+ }
+ // SparklyPaper end - parallel world ticking
return this.getWorld().getComputedBiome(this.getX(), this.getY(), this.getZ());
}
// Paper end
@Override
public void setBiome(Biome bio) {
+ // SparklyPaper start - parallel world ticking
+ if (world instanceof ServerLevel serverWorld) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
+ }
+ // SparklyPaper end - parallel world ticking
this.getWorld().setBiome(this.getX(), this.getY(), this.getZ(), bio);
}
@@ -402,6 +433,11 @@ public class CraftBlock implements Block {
@Override
public boolean isBlockFaceIndirectlyPowered(BlockFace face) {
+ // SparklyPaper start - parallel world ticking
+ if (world instanceof ServerLevel serverWorld) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot read world asynchronously");
+ }
+ // SparklyPaper end - parallel world ticking
int power = this.world.getMinecraftWorld().getSignal(this.position, CraftBlock.blockFaceToNotch(face));
Block relative = this.getRelative(face);
@@ -414,6 +450,11 @@ public class CraftBlock implements Block {
@Override
public int getBlockPower(BlockFace face) {
+ // SparklyPaper start - parallel world ticking
+ if (world instanceof ServerLevel serverWorld) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot read world asynchronously");
+ }
+ // SparklyPaper end - parallel world ticking
int power = 0;
net.minecraft.world.level.Level world = this.world.getMinecraftWorld();
int x = this.getX();
@@ -484,6 +525,11 @@ public class CraftBlock implements Block {
@Override
public boolean breakNaturally() {
+ // SparklyPaper start - parallel world ticking
+ if (world instanceof ServerLevel serverWorld) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
+ }
+ // SparklyPaper end - parallel world ticking
return this.breakNaturally(null);
}
@@ -543,6 +589,11 @@ public class CraftBlock implements Block {
@Override
public boolean applyBoneMeal(BlockFace face) {
+ // SparklyPaper start - parallel world ticking
+ if (world instanceof ServerLevel serverWorld) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
+ }
+ // SparklyPaper end - parallel world ticking
Direction direction = CraftBlock.blockFaceToNotch(face);
BlockFertilizeEvent event = null;
ServerLevel world = this.getCraftWorld().getHandle();
@@ -554,8 +605,8 @@ public class CraftBlock implements Block {
world.captureTreeGeneration = false;
if (world.capturedBlockStates.size() > 0) {
- TreeType treeType = SaplingBlock.treeType;
- SaplingBlock.treeType = null;
+ TreeType treeType = SaplingBlock.treeTypeRT.get(); // SparklyPaper - parallel world ticking
+ SaplingBlock.treeTypeRT.set(null); // SparklyPaper - parallel world ticking
List<BlockState> blocks = new ArrayList<>(world.capturedBlockStates.values());
world.capturedBlockStates.clear();
StructureGrowEvent structureEvent = null;
@@ -644,6 +695,11 @@ public class CraftBlock implements Block {
@Override
public RayTraceResult rayTrace(Location start, Vector direction, double maxDistance, FluidCollisionMode fluidCollisionMode) {
+ // SparklyPaper start - parallel world ticking
+ if (world instanceof ServerLevel serverWorld) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot read world asynchronously");
+ }
+ // SparklyPaper end - parallel world ticking
Preconditions.checkArgument(start != null, "Location start cannot be null");
Preconditions.checkArgument(this.getWorld().equals(start.getWorld()), "Location start cannot be a different world");
start.checkFinite();
@@ -685,6 +741,11 @@ public class CraftBlock implements Block {
@Override
public boolean canPlace(BlockData data) {
+ // SparklyPaper start - parallel world ticking
+ if (world instanceof ServerLevel serverWorld) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot read world asynchronously");
+ }
+ // SparklyPaper end - parallel world ticking
Preconditions.checkArgument(data != null, "BlockData cannot be null");
net.minecraft.world.level.block.state.BlockState iblockdata = ((CraftBlockData) data).getState();
net.minecraft.world.level.Level world = this.world.getMinecraftWorld();
@@ -719,6 +780,11 @@ public class CraftBlock implements Block {
@Override
public void tick() {
+ // SparklyPaper start - parallel world ticking
+ if (world instanceof ServerLevel serverWorld) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
+ }
+ // SparklyPaper end - parallel world ticking
final ServerLevel level = this.world.getMinecraftWorld();
this.getNMS().tick(level, this.position, level.random);
}
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
index 04ae258a2f8e98421340d29d5cceedec045171b7..3e90f16d303a8f3d1e2b01e7cbd62cbd991d3f06 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
@@ -25,7 +25,7 @@ public abstract class CraftBlockEntityState<T extends BlockEntity> extends Craft
private final T tileEntity;
private final T snapshot;
public boolean snapshotDisabled; // Paper
- public static boolean DISABLE_SNAPSHOT = false; // Paper
+ public static ThreadLocal<Boolean> DISABLE_SNAPSHOT = ThreadLocal.withInitial(() -> Boolean.FALSE); // SparklyPaper - parallel world ticking
public CraftBlockEntityState(World world, T tileEntity) {
super(world, tileEntity.getBlockPos(), tileEntity.getBlockState());
@@ -34,8 +34,8 @@ public abstract class CraftBlockEntityState<T extends BlockEntity> extends Craft
try { // Paper - Show blockstate location if we failed to read it
// Paper start
- this.snapshotDisabled = DISABLE_SNAPSHOT;
- if (DISABLE_SNAPSHOT) {
+ this.snapshotDisabled = DISABLE_SNAPSHOT.get(); // SparklyPaper - parallel world ticking
+ if (DISABLE_SNAPSHOT.get()) { // SparklyPaper - parallel world ticking
this.snapshot = this.tileEntity;
} else {
this.snapshot = this.createSnapshot(tileEntity);
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java
index fa63a6cfcfcc4eee4503a82d85333c139c8c8b2b..9856860ee2987738bbcad5d752670e30f569ba74 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java
@@ -215,6 +215,12 @@ public class CraftBlockState implements BlockState {
LevelAccessor access = this.getWorldHandle();
CraftBlock block = this.getBlock();
+ // SparklyPaper start - parallel world ticking
+ if (access instanceof net.minecraft.server.level.ServerLevel serverWorld) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
+ }
+ // SparklyPaper end - parallel world ticking
+
if (block.getType() != this.getType()) {
if (!force) {
return false;
@@ -350,6 +356,7 @@ public class CraftBlockState implements BlockState {
@Override
public java.util.Collection<org.bukkit.inventory.ItemStack> getDrops(org.bukkit.inventory.ItemStack item, org.bukkit.entity.Entity entity) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(world.getHandle(), position, "Cannot modify world asynchronously"); // SparklyPaper - parallel world ticking
this.requirePlaced();
net.minecraft.world.item.ItemStack nms = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(item);
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java
index 56453454cbd4b9e9270fc833f8ab38d5fa7a3763..ad507c36d89ce8639fbedbb1df0da66bdc96affa 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java
@@ -249,8 +249,8 @@ public final class CraftBlockStates {
net.minecraft.world.level.block.state.BlockState blockData = craftBlock.getNMS();
BlockEntity tileEntity = craftBlock.getHandle().getBlockEntity(blockPosition);
// Paper start - block state snapshots
- boolean prev = CraftBlockEntityState.DISABLE_SNAPSHOT;
- CraftBlockEntityState.DISABLE_SNAPSHOT = !useSnapshot;
+ boolean prev = CraftBlockEntityState.DISABLE_SNAPSHOT.get(); // SparklyPaper - parallel world ticking
+ CraftBlockEntityState.DISABLE_SNAPSHOT.set(!useSnapshot); // SparklyPaper - parallel world ticking
try {
// Paper end
CraftBlockState blockState = CraftBlockStates.getBlockState(world, blockPosition, blockData, tileEntity);
@@ -258,7 +258,7 @@ public final class CraftBlockStates {
return blockState;
// Paper start
} finally {
- CraftBlockEntityState.DISABLE_SNAPSHOT = prev;
+ CraftBlockEntityState.DISABLE_SNAPSHOT.set(prev); // SparklyPaper - parallel world ticking
}
// Paper end
}
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
index bbd000e3a3e9112e10a09e5d3ef6851b28e900fa..6b91661e920b80fb2ddf2b3e9e2c2839a7986b01 100644
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
@@ -951,7 +951,7 @@ public class CraftEventFactory {
return CraftEventFactory.handleBlockSpreadEvent(world, source, target, block, 2);
}
- public static BlockPos sourceBlockOverride = null; // SPIGOT-7068: Add source block override, not the most elegant way but better than passing down a BlockPosition up to five methods deep.
+ public static final ThreadLocal<BlockPos> sourceBlockOverrideRT = new ThreadLocal<>(); // SPIGOT-7068: Add source block override, not the most elegant way but better than passing down a BlockPosition up to five methods deep. // SparklyPaper - parallel world ticking (this is from Folia, fixes concurrency bugs with sculk catalysts)
public static boolean handleBlockSpreadEvent(LevelAccessor world, BlockPos source, BlockPos target, net.minecraft.world.level.block.state.BlockState block, int flag) {
// Suppress during worldgen
@@ -963,7 +963,7 @@ public class CraftEventFactory {
CraftBlockState state = CraftBlockStates.getBlockState(world, target, flag);
state.setData(block);
- BlockSpreadEvent event = new BlockSpreadEvent(state.getBlock(), CraftBlock.at(world, CraftEventFactory.sourceBlockOverride != null ? CraftEventFactory.sourceBlockOverride : source), state);
+ BlockSpreadEvent event = new BlockSpreadEvent(state.getBlock(), CraftBlock.at(world, CraftEventFactory.sourceBlockOverrideRT.get() != null ? CraftEventFactory.sourceBlockOverrideRT.get() : source), state); // SparklyPaper - parallel world ticking
Bukkit.getPluginManager().callEvent(event);
if (!event.isCancelled()) {
@@ -2248,7 +2248,7 @@ public class CraftEventFactory {
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemStack.copyWithCount(1));
org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(bukkitBlock, craftItem.clone(), CraftVector.toBukkit(to));
- if (!net.minecraft.world.level.block.DispenserBlock.eventFired) {
+ if (!net.minecraft.world.level.block.DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
if (!event.callEvent()) {
return itemStack;
}

View File

@@ -0,0 +1,20 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrPowerGamerBR <git@mrpowergamerbr.com>
Date: Wed, 12 Jun 2024 11:53:09 -0300
Subject: [PATCH] SPARKLYPOWER Add custom blocks
diff --git a/src/test/java/org/bukkit/craftbukkit/legacy/LegacyTest.java b/src/test/java/org/bukkit/craftbukkit/legacy/LegacyTest.java
index 002e635137a8b015af9799d6a8854ebc6330bfb4..0f02a9793937a492003bbfff827ac6483c1baa14 100644
--- a/src/test/java/org/bukkit/craftbukkit/legacy/LegacyTest.java
+++ b/src/test/java/org/bukkit/craftbukkit/legacy/LegacyTest.java
@@ -135,6 +135,9 @@ public class LegacyTest {
// 1.21.4
Material.OPEN_EYEBLOSSOM, Material.CLOSED_EYEBLOSSOM, Material.RESIN_CLUMP, Material.RESIN_BLOCK, Material.RESIN_BRICKS, Material.RESIN_BRICK_STAIRS, Material.RESIN_BRICK_SLAB, Material.RESIN_BRICK_WALL, Material.CHISELED_RESIN_BRICKS,
Material.RESIN_BRICK, Material.POTTED_OPEN_EYEBLOSSOM, Material.POTTED_CLOSED_EYEBLOSSOM,
+ // SparklyPower custom blocks
+ Material.SPARKLYPOWER_RAINBOW_WOOL, Material.SPARKLYPOWER_RAINBOW_CONCRETE, Material.SPARKLYPOWER_RAINBOW_TERRACOTTA, Material.SPARKLYPOWER_ASPHALT_PLAYER, Material.SPARKLYPOWER_ASPHALT_SERVER, Material.SPARKLYPOWER_ASPHALT_PLAYER_SLAB,
+ Material.SPARKLYPOWER_ASPHALT_SERVER_SLAB,
//
Material.LEGACY_AIR, Material.LEGACY_DEAD_BUSH, Material.LEGACY_BURNING_FURNACE, Material.LEGACY_WALL_SIGN, Material.LEGACY_REDSTONE_TORCH_OFF, Material.LEGACY_SKULL, Material.LEGACY_REDSTONE_COMPARATOR_ON, Material.LEGACY_WALL_BANNER, Material.LEGACY_MONSTER_EGG));

View File

@@ -0,0 +1,3 @@
--- a/src/main/resources/data/.paperassetsroot
+++ b/src/main/resources/data/.paperassetsroot
@@ -1,0 +_,0 @@

View File

@@ -0,0 +1,98 @@
package net.sparklypower.sparklypaper;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.minecraft.world.level.block.entity.TickingBlockEntity;
import java.util.Arrays;
import java.util.Collection;
/**
* A list for ServerLevel's blockEntityTickers
* <p>
* This list is behaves identically to ObjectArrayList, but it has an additional method, `removeAllByIndex`, that allows a list of integers to be passed indicating what
* indexes should be deleted from the list
* <p>
* This is faster than using removeAll, since we don't need to compare the identity of each block entity, and faster than looping thru each index manually and deleting with remove,
* since we don't need to resize the array every single remove.
*/
public final class BlockEntityTickersList extends ObjectArrayList<TickingBlockEntity> {
private final IntOpenHashSet toRemove = new IntOpenHashSet();
private int startSearchFromIndex = -1;
/** Creates a new array list with {@link #DEFAULT_INITIAL_CAPACITY} capacity. */
public BlockEntityTickersList() {
super();
}
/**
* Creates a new array list and fills it with a given collection.
*
* @param c a collection that will be used to fill the array list.
*/
public BlockEntityTickersList(final Collection<? extends TickingBlockEntity> c) {
super(c);
}
/**
* Marks an entry as removed
*
* @param index the index of the item on the list to be marked as removed
*/
public void markAsRemoved(final int index) {
// The block entities list always loop starting from 0, so we only need to check if the startSearchFromIndex is -1 and that's it
if (this.startSearchFromIndex == -1)
this.startSearchFromIndex = index;
this.toRemove.add(index);
}
/**
* Removes elements that have been marked as removed.
*/
public void removeMarkedEntries() {
if (this.startSearchFromIndex == -1) // No entries in the list, skip
return;
removeAllByIndex(startSearchFromIndex, toRemove);
toRemove.clear();
this.startSearchFromIndex = -1; // Reset the start search index
}
/**
* Removes elements by their index.
*/
private void removeAllByIndex(final int startSearchFromIndex, final IntOpenHashSet c) { // can't use Set<Integer> because we want to avoid autoboxing when using contains
final int requiredMatches = c.size();
if (requiredMatches == 0)
return; // exit early, we don't need to do anything
final Object[] a = this.a;
int j = startSearchFromIndex;
int matches = 0;
for (int i = startSearchFromIndex; i < size; i++) { // If the user knows the first index to be removed, we can skip a lot of unnecessary comparsions
if (!c.contains(i)) {
// TODO: It can be possible to optimize this loop by tracking the start/finish and then using arraycopy to "skip" the elements,
// this would optimize cases where the index to be removed are far apart, HOWEVER it does have a big performance impact if you are doing
// "arraycopy" for each element
a[j++] = a[i];
} else {
matches++;
}
if (matches == requiredMatches) { // Exit the loop if we already removed everything, we don't need to check anything else
// We need to update the final size here, because we know that we already found everything!
// Because we know that the size must be currentSize - requiredMatches (because we have matched everything), let's update the value
// However, we need to copy the rest of the stuff over
if (i != (size - 1)) { // If it isn't the last index...
// i + 1 because we want to copy the *next* element over
// and the size - i - 1 is because we want to get the current size, minus the current index (which is i), and then - 1 because we want to copy -1 ahead (remember, we are adding +1 to copy the *next* element)
System.arraycopy(a, i + 1, a, j, size - i - 1);
}
j = size - requiredMatches;
break;
}
}
Arrays.fill(a, j, size, null);
size = j;
}
}

View File

@@ -0,0 +1,65 @@
package net.sparklypower.sparklypaper.commands;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.sparklypower.sparklypaper.configs.SparklyPaperConfigUtils;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import java.io.File;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class SparklyPaperCommand extends Command {
public SparklyPaperCommand(String name) {
super(name);
this.description = "SparklyPaper related commands";
this.usageMessage = "/sparklypaper [reload | version]";
this.setPermission("bukkit.command.sparklypaper");
}
@Override
public List<String> tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException {
if (args.length == 1) {
return Stream.of("reload", "version")
.filter(arg -> arg.startsWith(args[0].toLowerCase()))
.collect(Collectors.toList());
}
return Collections.emptyList();
}
@Override
public boolean execute(CommandSender sender, String commandLabel, String[] args) {
if (!testPermission(sender)) return true;
if (args.length != 1) {
sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage);
return false;
}
if (args[0].equalsIgnoreCase("reload")) {
Command.broadcastCommandMessage(sender, ChatColor.RED + "Please note that this command is not supported and may cause issues.");
Command.broadcastCommandMessage(sender, ChatColor.RED + "If you encounter any issues please use the /stop command to restart your server.");
MinecraftServer console = MinecraftServer.getServer();
SparklyPaperConfigUtils.INSTANCE.init((File) console.options.valueOf("sparklypaper-settings"));
for (ServerLevel level : console.getAllLevels()) {
level.sparklyPaperConfig = SparklyPaperConfigUtils.INSTANCE.getWorldSettings(level.serverLevelData.getLevelName());
}
console.server.reloadCount++;
Command.broadcastCommandMessage(sender, ChatColor.GREEN + "SparklyPaper config reload complete.");
} else if (args[0].equalsIgnoreCase("version")) {
Command verCmd = org.bukkit.Bukkit.getServer().getCommandMap().getCommand("version");
if (verCmd != null) {
return verCmd.execute(sender, commandLabel, new String[0]);
}
}
return true;
}
}

View File

@@ -0,0 +1,93 @@
package net.sparklypower.sparklypaper
import com.mojang.logging.LogUtils
import java.time.LocalDateTime
import java.time.Month
import java.time.ZoneOffset
import java.util.concurrent.*
class HalloweenManager {
companion object {
private val LOGGER = LogUtils.getLogger()
}
private var spookySeasonStartEpoch = 0L
private var spookySeasonEndEpoch = 0L
private var halloweenStartEpoch = 0L
private var halloweenEndEpoch = 0L
private var executor = Executors.newSingleThreadScheduledExecutor(object: ThreadFactory {
override fun newThread(p0: Runnable): Thread {
val thread = Thread(p0)
thread.name = "halloween-timer-updater"
thread.priority = 1 // Minimum priority
return thread
}
})
private var latch = CountDownLatch(1)
fun startHalloweenEpochTask() {
var isFirst = true
executor.scheduleAtFixedRate({
updateEpoch()
if (isFirst)
latch.countDown()
isFirst = false
}, 0L, 90L, TimeUnit.DAYS) // Every 90 days
}
fun waitUntilEpochHasBeenUpdated() {
latch.await()
}
fun updateEpoch() {
LOGGER.info("Updating Spooky Season and Halloween Time")
this.spookySeasonStartEpoch = getEpochMillisAtDate(20, Month.OCTOBER, false)
this.spookySeasonEndEpoch = getEpochMillisAtDate(3, Month.NOVEMBER, true)
this.halloweenStartEpoch = getEpochMillisAtDate(31, Month.OCTOBER, false)
this.halloweenEndEpoch = getEpochMillisAtDate(31, Month.OCTOBER, true)
LOGGER.info("Updated Spooky Season and Halloween Time!")
}
fun isSpookySeason() = System.currentTimeMillis() in spookySeasonStartEpoch until spookySeasonEndEpoch
fun isHalloween() = System.currentTimeMillis() in halloweenStartEpoch until halloweenEndEpoch
private fun getEpochMillisAtDate(dayOfMonth: Int, month: Month, isEnd: Boolean): Long {
// Get the current year
val currentYear = LocalDateTime.now().year
// Define the target date (20/10/CurrentYear at midnight)
val targetDate = LocalDateTime.of(
currentYear,
month,
dayOfMonth,
if (isEnd)
23
else
0,
if (isEnd)
59
else
0,
if (isEnd)
59
else
0,
if (isEnd)
999_999_999
else
0,
)
// Check if the target date is in the past
val now = LocalDateTime.now()
val adjustedDate = if (now.isAfter(targetDate)) {
// If in the past, adjust to the same date in the next year
targetDate.plusYears(1)
} else {
// If in the future or today, use the original target date
targetDate
}
// Convert the adjusted date to epoch time in milliseconds
return adjustedDate.atZone(ZoneOffset.systemDefault()).toInstant().toEpochMilli()
}
}

View File

@@ -0,0 +1,95 @@
package net.sparklypower.sparklypaper
import ca.spottedleaf.dataconverter.types.MapType
object LegacyNBTRemapper {
/**
* Remaps hacky direct NBT storage used in SparklyPower to proper PersistentDataContainer data
*/
fun remap(tag: MapType<String>) {
val perfectDreamsMap = tag.getMap<String>("PerfectDreams")
if (perfectDreamsMap != null) {
val publicBukkitValuesMap = tag.getOrCreateMap<String>("PublicBukkitValues")
// The "setBoolean" functions do set bytes behind the scenes, just like how we do the things in SparklyPower
perfectDreamsMap.getStringAndRemove("isJetpack")?.let {
publicBukkitValuesMap.setBoolean("sparklypower:is_jetpack", it.toBoolean())
}
perfectDreamsMap.getStringAndRemove("disallowCrafting")?.let {
publicBukkitValuesMap.setBoolean("sparklypower:disallow_crafting", it.toBoolean())
}
perfectDreamsMap.getStringAndRemove("poop")?.let {
publicBukkitValuesMap.setBoolean("sparklypower:is_poop", it.toBoolean())
}
perfectDreamsMap.getStringAndRemove("renamedBySeuZe")?.let {
publicBukkitValuesMap.setBoolean("sparklypower:is_renamed_by_seu_ze", it.toBoolean())
}
perfectDreamsMap.getStringAndRemove("isMonsterPickaxe")?.let {
publicBukkitValuesMap.setBoolean("sparklypower:is_monster_tool", it.toBoolean())
}
perfectDreamsMap.getStringAndRemove("itemOwner")?.let {
publicBukkitValuesMap.setString("sparklypower:item_owner", it)
}
perfectDreamsMap.getStringAndRemove("DreamFusca")?.let {
publicBukkitValuesMap.setString("sparklypower:fusca_info", it)
}
perfectDreamsMap.getStringAndRemove("isFusca")?.let {
publicBukkitValuesMap.setBoolean("sparklypower:is_fusca", it.toBoolean())
}
perfectDreamsMap.getStringAndRemove("fancyLeatherArmor")?.let {
publicBukkitValuesMap.setBoolean("sparklypower:is_fancy_leather_armor", it.toBoolean())
}
perfectDreamsMap.getStringAndRemove("caixaSecretaLevel")?.let {
publicBukkitValuesMap.setInt("sparklypower:caixa_secreta_level", it.toInt())
}
perfectDreamsMap.getStringAndRemove("caixaSecretaWorld")?.let {
publicBukkitValuesMap.setString("sparklypower:caixa_secreta_world", it)
}
perfectDreamsMap.getStringAndRemove("isMoveSpawners")?.let {
publicBukkitValuesMap.setBoolean("sparklypower:is_move_spawners_tool", it.toBoolean())
}
perfectDreamsMap.getStringAndRemove("spawnerType")?.let {
publicBukkitValuesMap.setString("sparklypower:spawner_type", it)
}
perfectDreamsMap.getStringAndRemove("isMochila")?.let {
publicBukkitValuesMap.setBoolean("sparklypower:is_mochila", it.toBoolean())
}
perfectDreamsMap.getStringAndRemove("mochilaId")?.let {
publicBukkitValuesMap.setLong("sparklypower:mochila_id", it.toLong())
}
perfectDreamsMap.getStringAndRemove("customMapOwner")?.let {
publicBukkitValuesMap.setString("sparklypower:map_custom_owner", it)
}
perfectDreamsMap.getStringAndRemove("quickTeleport")?.let {
publicBukkitValuesMap.setBoolean("sparklypower:is_quick_resources_teleport", it.toBoolean())
}
// If it is empty, then it means that we have migrated everything and we can remove the old PerfectDreams tag, yay!
if (perfectDreamsMap.isEmpty)
tag.remove("PerfectDreams")
}
}
private fun MapType<String>.getStringAndRemove(key: String): String? {
val v = getString(key)
remove(key)
return v
}
}

View File

@@ -0,0 +1,20 @@
package net.sparklypower.sparklypaper
import ca.spottedleaf.moonrise.common.util.TickThread
import java.util.concurrent.ThreadFactory
class ServerLevelTickExecutorThreadFactory(private val worldName: String) : ThreadFactory {
override fun newThread(p0: Runnable): Thread {
val tickThread = TickThread.ServerLevelTickThread(p0, "serverlevel-tick-worker [$worldName]")
if (tickThread.isDaemon) {
tickThread.isDaemon = false
}
if (tickThread.priority != 5) {
tickThread.priority = 5
}
return tickThread
}
}

View File

@@ -0,0 +1,22 @@
package net.sparklypower.sparklypaper
import net.minecraft.server.MinecraftServer
import net.sparklypower.sparklypaper.commands.SparklyPaperCommand
import org.bukkit.command.Command
import org.checkerframework.checker.nullness.qual.NonNull
import org.checkerframework.framework.qual.DefaultQualifier
@DefaultQualifier(NonNull::class)
object SparklyPaperCommands {
private val COMMANDS = mapOf(
"sparklypaper" to SparklyPaperCommand("sparklypaper")
)
fun registerCommands(server: MinecraftServer) {
COMMANDS.forEach { (s: String, command: Command) ->
server.server.commandMap.register(
s, "SparklyPaper", command
)
}
}
}

View File

@@ -0,0 +1,50 @@
package net.sparklypower.sparklypaper.configs
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
class SparklyPaperConfig(
@SerialName("config-version")
override val configVersion: Int,
@SerialName("parallel-world-ticking")
val parallelWorldTicking: ParallelWorldTicking,
@SerialName("world-settings")
val worldSettings: Map<String, SparklyPaperWorldConfig>
) : UpgradeableConfig {
override fun isNewest() = true
override fun upgradeToNext() = error("This config is already the newest version!")
@Serializable
class ParallelWorldTicking(
val threads: Int
)
@Serializable
class SparklyPaperWorldConfig(
@SerialName("skip-map-item-data-updates-if-map-does-not-have-craftmaprenderer")
val skipMapItemDataUpdatesIfMapDoesNotHaveCraftMapRenderer: Boolean,
@SerialName("blazingly-simple-farm-checks")
val blazinglySimpleFarmChecks: BlazinglySimpleFarmChecks,
@SerialName("ticks-per")
val ticksPer: TicksPer,
) {
@Serializable
data class BlazinglySimpleFarmChecks(
val enabled: Boolean,
@SerialName("default-growth-speed")
val defaultGrowthSpeed: Float,
@SerialName("moist-growth-speed")
val moistGrowthSpeed: Float,
@SerialName("skip-middle-aging-stages-for-crops")
val skipMiddleAgingStagesForCrops: Boolean
)
@Serializable
data class TicksPer(
@SerialName("hopper-cooldown-when-target-container-is-full")
val hopperCooldownWhenTargetContainerIsFull: Int
)
}
}

View File

@@ -0,0 +1,94 @@
package net.sparklypower.sparklypaper.configs
import com.charleskorn.kaml.*
import com.google.common.base.Throwables
import kotlinx.serialization.SerializationException
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import net.sparklypower.sparklypaper.configs.previous.SparklyPaperConfigV1
import org.bukkit.Bukkit
import java.io.File
import java.util.logging.Level
object SparklyPaperConfigUtils {
private const val CURRENT_CONFIG_VERSION = 2
val yaml = Yaml(
configuration = YamlConfiguration(
strictMode = false
)
)
val deserializationStrategiesForVersions = mapOf(
1 to SparklyPaperConfigV1.serializer(),
CURRENT_CONFIG_VERSION to SparklyPaperConfig.serializer()
)
lateinit var config: SparklyPaperConfig
val logContainerCreationStacktraces = java.lang.Boolean.getBoolean("sparklypaper.logContainerCreationStacktraces")
fun init(configFile: File) {
// Write default config if the file doesn't exist
if (!configFile.exists()) {
configFile.writeText(
yaml.encodeToString(
SparklyPaperConfig(
CURRENT_CONFIG_VERSION,
SparklyPaperConfig.ParallelWorldTicking(
threads = 8
),
mapOf(
"default" to SparklyPaperConfig.SparklyPaperWorldConfig(
skipMapItemDataUpdatesIfMapDoesNotHaveCraftMapRenderer = true,
blazinglySimpleFarmChecks = SparklyPaperConfig.SparklyPaperWorldConfig.BlazinglySimpleFarmChecks(
enabled = false,
defaultGrowthSpeed = 1.0f,
moistGrowthSpeed = 5.0f,
skipMiddleAgingStagesForCrops = true
),
SparklyPaperConfig.SparklyPaperWorldConfig.TicksPer(
hopperCooldownWhenTargetContainerIsFull = 0
)
)
)
)
)
)
}
val loadedConfig = try {
// Read the version file from the config before attempting to parse
val yamlNode = yaml.parseToYamlNode(configFile.readText())
var configVersion = yamlNode.yamlMap.getScalar("config-version")?.toInt() ?: 1 // The first config version didn't have the "config-version" key
if (configVersion != CURRENT_CONFIG_VERSION) {
var upgradedVersion: UpgradeableConfig? = null
while (configVersion != CURRENT_CONFIG_VERSION) {
Bukkit.getLogger().log(Level.INFO, "Attempting to upgrade SparklyPaper Config from version $configVersion to the next version...")
val upgradeableConfig = yaml.decodeFromYamlNode(deserializationStrategiesForVersions[configVersion]!!, yamlNode)
if (!upgradeableConfig.isNewest()) {
upgradedVersion = upgradeableConfig.upgradeToNext()
Bukkit.getLogger().log(Level.INFO, "Upgraded SparklyPaper Config from version $configVersion to ${upgradedVersion.configVersion}")
configVersion = upgradedVersion.configVersion
}
}
upgradedVersion as SparklyPaperConfig
} else {
yaml.decodeFromYamlNode(SparklyPaperConfig.serializer(), yamlNode)
}
} catch (e: SerializationException) {
Bukkit.getLogger().log(Level.SEVERE, "Could not load sparklypaper.yml, please correct your syntax errors", e)
throw Throwables.propagate(e)
}
// Rewrite the config file to remove old fields and stuff
// TODO: Maybe handle this in another way? This feels kinda bad
configFile.writeText(yaml.encodeToString(loadedConfig))
config = loadedConfig
}
fun getWorldSettings(levelName: String): SparklyPaperConfig.SparklyPaperWorldConfig {
return config.worldSettings[levelName] ?: config.worldSettings["default"] ?: error("Missing default world-settings in sparklypaper.yml!")
}
}

View File

@@ -0,0 +1,7 @@
package net.sparklypower.sparklypaper.configs
interface UpgradeableConfig {
abstract val configVersion: Int
fun isNewest(): Boolean
fun upgradeToNext(): UpgradeableConfig
}

View File

@@ -0,0 +1,65 @@
package net.sparklypower.sparklypaper.configs.previous
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import net.sparklypower.sparklypaper.configs.SparklyPaperConfig
import net.sparklypower.sparklypaper.configs.UpgradeableConfig
@Serializable
class SparklyPaperConfigV1(
@SerialName("parallel-world-ticking")
val parallelWorldTicking: ParallelWorldTicking,
@SerialName("world-settings")
val worldSettings: Map<String, SparklyPaperWorldConfig>
) : UpgradeableConfig {
override val configVersion = 1
override fun isNewest() = false
override fun upgradeToNext(): UpgradeableConfig {
return SparklyPaperConfig(
2,
SparklyPaperConfig.ParallelWorldTicking(
this.parallelWorldTicking.threads
),
worldSettings.mapValues {
SparklyPaperConfig.SparklyPaperWorldConfig(
it.value.skipMapItemDataUpdatesIfMapDoesNotHaveCraftMapRenderer,
SparklyPaperConfig.SparklyPaperWorldConfig.BlazinglySimpleFarmChecks(
it.value.blazinglySimpleFarmChecks.enabled,
it.value.blazinglySimpleFarmChecks.defaultGrowthSpeed,
it.value.blazinglySimpleFarmChecks.moistGrowthSpeed,
it.value.blazinglySimpleFarmChecks.skipMiddleAgingStagesForCrops,
),
SparklyPaperConfig.SparklyPaperWorldConfig.TicksPer(
0
)
)
}
)
}
@Serializable
class ParallelWorldTicking(
val threads: Int
)
@Serializable
class SparklyPaperWorldConfig(
@SerialName("skip-map-item-data-updates-if-map-does-not-have-craftmaprenderer")
val skipMapItemDataUpdatesIfMapDoesNotHaveCraftMapRenderer: Boolean,
@SerialName("blazingly-simple-farm-checks")
val blazinglySimpleFarmChecks: BlazinglySimpleFarmChecks
) {
@Serializable
data class BlazinglySimpleFarmChecks(
val enabled: Boolean,
@SerialName("default-growth-speed")
val defaultGrowthSpeed: Float,
@SerialName("moist-growth-speed")
val moistGrowthSpeed: Float,
@SerialName("skip-middle-aging-stages-for-crops")
val skipMiddleAgingStagesForCrops: Boolean
)
}
}