9
0
mirror of https://github.com/SparklyPower/SparklyPaper.git synced 2025-12-22 16:39:33 +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,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
)
}
}