diff --git a/build.gradle.kts b/build.gradle.kts index 47efc71..ca471ea 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -16,6 +16,7 @@ repositories { dependencies { library(kotlin("stdlib")) library("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0-RC2") + library("org.jetbrains.kotlinx:kotlinx-datetime:0.5.0") library(kotlin("scripting-common")) library(kotlin("scripting-jvm")) @@ -34,6 +35,8 @@ dependencies { testImplementation(kotlin("test")) testImplementation("io.kotest:kotest-assertions-core:5.8.0") testImplementation("com.github.seeseemelk:MockBukkit-v1.20:3.9.0") + + testImplementation("org.jetbrains.kotlinx:kotlinx-datetime:0.5.0") } group = "io.github.addoncommunity.galactifun" diff --git a/settings.gradle.kts b/settings.gradle.kts index ab58153..17c1b97 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,5 +1 @@ -/* - * This file was generated by the Gradle 'init' task. - */ - rootProject.name = "Galactifun2" diff --git a/src/main/kotlin/io/github/addoncommunity/galactifun/Galactifun2.kt b/src/main/kotlin/io/github/addoncommunity/galactifun/Galactifun2.kt index 70509af..cba02ba 100644 --- a/src/main/kotlin/io/github/addoncommunity/galactifun/Galactifun2.kt +++ b/src/main/kotlin/io/github/addoncommunity/galactifun/Galactifun2.kt @@ -16,7 +16,7 @@ import io.github.addoncommunity.galactifun.scripting.evalScript import io.github.addoncommunity.galactifun.util.units.Distance.Companion.au import io.github.addoncommunity.galactifun.util.units.Distance.Companion.kilometers import io.github.addoncommunity.galactifun.util.units.Mass.Companion.kilograms -import io.github.addoncommunity.galactifun.util.years +import io.github.addoncommunity.galactifun.util.units.years import io.github.seggan.kfun.AbstractAddon import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion import io.github.thebusybiscuit.slimefun4.implementation.Slimefun diff --git a/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/StarSystem.kt b/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/StarSystem.kt index f15edf2..d07ef49 100644 --- a/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/StarSystem.kt +++ b/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/StarSystem.kt @@ -3,6 +3,7 @@ package io.github.addoncommunity.galactifun.api.objects import io.github.addoncommunity.galactifun.api.objects.properties.Orbit import io.github.addoncommunity.galactifun.util.units.Distance import io.github.addoncommunity.galactifun.util.units.Mass +import kotlinx.datetime.Instant import org.bukkit.Material import org.bukkit.inventory.ItemStack @@ -14,13 +15,13 @@ class StarSystem( override val radius: Distance ) : UniversalObject(name, ItemStack(Material.SUNFLOWER)) { - override fun distanceTo(other: UniversalObject): Distance { + override fun distanceTo(other: UniversalObject, time: Instant): Distance { return if (orbitLevel > other.orbitLevel) { - other.orbit.semimajorAxis + distanceTo(other.orbiting!!) + other.orbit.semimajorAxis + distanceTo(other.orbiting!!, time) } else if (orbitLevel == other.orbitLevel) { this.orbit.semimajorAxis - other.orbit.semimajorAxis } else { - super.distanceTo(other) + super.distanceTo(other, time) } } } \ No newline at end of file diff --git a/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/TheUniverse.kt b/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/TheUniverse.kt index bcd6a28..4170d4b 100644 --- a/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/TheUniverse.kt +++ b/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/TheUniverse.kt @@ -2,17 +2,15 @@ package io.github.addoncommunity.galactifun.api.objects import io.github.addoncommunity.galactifun.api.objects.properties.Orbit import io.github.addoncommunity.galactifun.util.units.Distance -import io.github.addoncommunity.galactifun.util.units.Distance.Companion.lightYears import io.github.addoncommunity.galactifun.util.units.Mass import org.bukkit.Material import org.bukkit.inventory.ItemStack -import kotlin.time.Duration.Companion.days object TheUniverse : UniversalObject("The Universe", ItemStack(Material.NETHER_STAR)) { - override val orbiting = null - override val orbit = Orbit(0.lightYears, 0.days) + override val orbit: Orbit + get() = error("The Universe does not have an orbit") override val mass: Mass - get() = error("The Universe does nto have a defined mass") + get() = error("The Universe does not have a defined mass") override val radius: Distance get() = error("The Universe does not have a defined radius") } \ No newline at end of file diff --git a/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/UniversalObject.kt b/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/UniversalObject.kt index 91809ae..a7c8462 100644 --- a/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/UniversalObject.kt +++ b/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/UniversalObject.kt @@ -2,11 +2,13 @@ package io.github.addoncommunity.galactifun.api.objects import io.github.addoncommunity.galactifun.api.objects.properties.Orbit import io.github.addoncommunity.galactifun.util.Constants +import io.github.addoncommunity.galactifun.util.LazyDouble import io.github.addoncommunity.galactifun.util.units.Distance -import io.github.addoncommunity.galactifun.util.units.Distance.Companion.lightYears +import io.github.addoncommunity.galactifun.util.units.Distance.Companion.meters import io.github.addoncommunity.galactifun.util.units.Mass import io.github.thebusybiscuit.slimefun4.libraries.dough.items.CustomItemStack import io.github.thebusybiscuit.slimefun4.utils.ChatUtils +import kotlinx.datetime.Instant import org.bukkit.inventory.ItemStack import kotlin.math.cos import kotlin.math.sqrt @@ -18,20 +20,24 @@ abstract class UniversalObject protected constructor(name: String, baseItem: Ite val item = CustomItemStack(baseItem, name) - abstract val orbiting: UniversalObject? abstract val orbit: Orbit abstract val mass: Mass abstract val radius: Distance - val mu: Double - get() = Constants.GRAVITATIONAL_CONSTANT * mass.kilograms - val escapeVelocity: Double - get() = sqrt(2 * Constants.GRAVITATIONAL_CONSTANT * mass.kilograms / radius.kilometers) - val parkingOrbit: Distance - get() = radius / 10 + val gravitationalParameter by LazyDouble { Constants.GRAVITATIONAL_CONSTANT * mass.kilograms } + val escapeVelocity by LazyDouble { sqrt(2 * Constants.GRAVITATIONAL_CONSTANT * mass.kilograms / radius.kilometers) } + val parkingOrbit: Orbit by lazy { + Orbit( + parent = this, + semimajorAxis = radius / 10, + eccentricity = 0.0, + argumentOfPeriapsis = 0.0, + timeOfPeriapsis = Instant.fromEpochMilliseconds(0) + ) + } val orbitLevel: Int - get() = if (orbiting == null) 0 else orbiting!!.orbitLevel + 1 + get() = if (this is TheUniverse) 0 else orbit.parent.orbitLevel + 1 private val _orbiters = mutableListOf() val orbiters: List = _orbiters @@ -40,17 +46,17 @@ abstract class UniversalObject protected constructor(name: String, baseItem: Ite _orbiters.add(orbiter) } - open fun distanceTo(other: UniversalObject): Distance { + open fun distanceTo(other: UniversalObject, time: Instant): Distance { if (orbitLevel == 0 || orbitLevel < other.orbitLevel) { - return other.orbit.semimajorAxis + distanceTo(other.orbiting!!) + return other.orbit.semimajorAxis + distanceTo(other.orbit.parent, time) } - if (orbiting == other.orbiting) { - val thisDist = orbit.semimajorAxis.lightYears - val otherDist = other.orbit.semimajorAxis.lightYears - val cosAngle = cos(orbit.trueAnomaly - other.orbit.trueAnomaly) - return sqrt(thisDist * thisDist + otherDist * otherDist - 2 * thisDist * otherDist * cosAngle).lightYears + if (orbit.parent == other.orbit.parent) { + val thisDist = orbit.radius(time).meters + val otherDist = other.orbit.radius(time).meters + val cosAngle = cos(orbit.trueAnomaly(time) - other.orbit.trueAnomaly(time)) + return sqrt(thisDist * thisDist + otherDist * otherDist - 2 * thisDist * otherDist * cosAngle).meters } - return orbit.semimajorAxis + orbiting!!.distanceTo(other) + return orbit.semimajorAxis + orbit.parent.distanceTo(other, time) } override fun equals(other: Any?): Boolean { diff --git a/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/planet/PlanetaryObject.kt b/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/planet/PlanetaryObject.kt index e1b0a98..e5a5ffe 100644 --- a/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/planet/PlanetaryObject.kt +++ b/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/planet/PlanetaryObject.kt @@ -2,6 +2,7 @@ package io.github.addoncommunity.galactifun.api.objects.planet import io.github.addoncommunity.galactifun.api.objects.UniversalObject import io.github.addoncommunity.galactifun.api.objects.properties.DayCycle +import io.github.addoncommunity.galactifun.api.objects.properties.Orbit import io.github.addoncommunity.galactifun.api.objects.properties.OrbitPosition import io.github.addoncommunity.galactifun.api.objects.properties.atmosphere.Atmosphere import io.github.addoncommunity.galactifun.core.managers.PlanetManager @@ -9,6 +10,7 @@ import io.github.addoncommunity.galactifun.util.Constants import io.github.addoncommunity.galactifun.util.units.Distance import io.github.addoncommunity.galactifun.util.units.Distance.Companion.meters import io.github.seggan.kfun.location.plus +import kotlinx.datetime.Instant import org.bukkit.Location import org.bukkit.inventory.ItemStack import kotlin.math.abs @@ -30,22 +32,26 @@ abstract class PlanetaryObject(name: String, baseItem: ItemStack) : UniversalObj return orbitPosition.centerLocation + location } - fun getDeltaVForTransferTo(other: PlanetaryObject): Double { + fun getDeltaVForTransferTo(other: PlanetaryObject, time: Instant): Double { if (this == other) return 0.0 - val thisParents = generateSequence(this as UniversalObject) { it.orbiting }.toList() + val thisParents = generateSequence(this as UniversalObject) { + if (it.orbitLevel == 0) null else it.orbit.parent + }.toList() if (other in thisParents) { - return other.getDeltaVForTransferTo(this) + return other.getDeltaVForTransferTo(this, time) } - val otherParents = generateSequence(other as UniversalObject) { it.orbiting }.toList() + val otherParents = generateSequence(other as UniversalObject) { + if (it.orbitLevel == 0) null else it.orbit.parent + }.toList() if (this in otherParents) { var height = other.parkingOrbit var dV = 0.0 for (obj in otherParents) { if (obj == this) break - dV += abs(obj.escapeVelocity - visViva(obj.mu, height, height)) - height = obj.orbit.semimajorAxis + dV += abs(obj.escapeVelocity - visViva(obj.gravitationalParameter, height.radius(time), height.semimajorAxis)) + height = obj.orbit } - dV += hohmannTransfer(mass.kilograms * Constants.GRAVITATIONAL_CONSTANT, height, parkingOrbit) + dV += hohmannTransfer(height, parkingOrbit, time) return dV } else { val closestParent = thisParents.first { it in otherParents } @@ -64,10 +70,13 @@ abstract class PlanetaryObject(name: String, baseItem: ItemStack) : UniversalObj private fun visViva(mu: Double, r: Distance, a: Distance): Double = sqrt(mu * (2 / r.meters - 1 / a.meters)) -private fun hohmannTransfer(mu: Double, parkingR: Distance, targetR: Distance): Double { +private fun hohmannTransfer(parking: Orbit, target: Orbit, time: Instant): Double { + val parkingR = parking.radius(time) + val targetR = target.radius(time) val transferA = (parkingR + targetR) / 2 - val firstManeuver = abs(visViva(mu, parkingR, transferA) - visViva(mu, parkingR, parkingR)) - val secondManeuver = abs(visViva(mu, targetR, targetR) - visViva(mu, targetR, transferA)) + val mu = parking.parent.gravitationalParameter + val firstManeuver = abs(visViva(mu, parkingR, transferA) - visViva(mu, parkingR, parking.semimajorAxis)) + val secondManeuver = abs(visViva(mu, targetR, target.semimajorAxis) - visViva(mu, targetR, transferA)) return firstManeuver + secondManeuver } @@ -102,7 +111,7 @@ private fun brachistochroneTransfer( return BrachistochroneTransfer(dV, time.seconds) } -data class BrachistochroneTransfer( +private data class BrachistochroneTransfer( val deltaV: Double, val time: Duration ) diff --git a/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/properties/Orbit.kt b/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/properties/Orbit.kt index bbc1e31..e19605d 100644 --- a/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/properties/Orbit.kt +++ b/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/properties/Orbit.kt @@ -1,23 +1,55 @@ package io.github.addoncommunity.galactifun.api.objects.properties +import io.github.addoncommunity.galactifun.api.objects.UniversalObject +import io.github.addoncommunity.galactifun.util.Constants +import io.github.addoncommunity.galactifun.util.LazyDouble import io.github.addoncommunity.galactifun.util.units.Distance -import kotlin.math.PI -import kotlin.time.Duration -import kotlin.time.DurationUnit +import io.github.addoncommunity.galactifun.util.units.Distance.Companion.meters +import io.github.addoncommunity.galactifun.util.units.doubleSeconds +import kotlinx.datetime.Instant +import java.util.* +import kotlin.math.acos +import kotlin.math.cos +import kotlin.math.sin +import kotlin.math.sqrt -class Orbit(val semimajorAxis: Distance, year: Duration) { +data class Orbit( + val parent: UniversalObject, + val semimajorAxis: Distance, + val eccentricity: Double, + // Our orbits are always flat, so inclination is always 0, and we don't need to store it + val argumentOfPeriapsis: Double, + val timeOfPeriapsis: Instant +) { + val meanMotion by LazyDouble { + val a = semimajorAxis.meters + sqrt(parent.gravitationalParameter / (a * a * a)) + } - private val year = - EARTH_YEAR * year.toDouble(DurationUnit.DAYS) / 365.25 * 1200000 // why 1200000? it was in the original code + fun meanAnomaly(time: Instant): Double { + val t = (time - timeOfPeriapsis) / Constants.ORBIT_TIME_SCALE + return meanMotion * t.doubleSeconds + } - val trueAnomaly: Double - get() { - if (year == 0.0) return 0.0 - return (System.currentTimeMillis() % year) * PI * 2 / year - } + private val eccentricAnomalyCache = WeakHashMap() + + fun eccentricAnomaly(time: Instant): Double { + return eccentricAnomalyCache.getOrPut(time) { kelpersEquation(meanAnomaly(time), eccentricity) } + } + + fun trueAnomaly(time: Instant): Double { + val cosE = cos(eccentricAnomaly(time)) + return acos((cosE - eccentricity) / (1 - eccentricity * cosE)) + } + + fun radius(time: Instant): Distance { + val e = eccentricAnomaly(time) + val a = semimajorAxis.meters + return (a * (1 - eccentricity * cos(e))).meters + } } -/** - * The number of Minecraft days in an Earth year - */ -private const val EARTH_YEAR = 30 \ No newline at end of file +tailrec fun kelpersEquation(m: Double, e: Double, guessE: Double = m): Double { + val nextGuess = m + e * sin(guessE) + return if (nextGuess == guessE) nextGuess else kelpersEquation(m, e, nextGuess) +} \ No newline at end of file diff --git a/src/main/kotlin/io/github/addoncommunity/galactifun/base/BaseUniverse.kt b/src/main/kotlin/io/github/addoncommunity/galactifun/base/BaseUniverse.kt index ed86448..8987325 100644 --- a/src/main/kotlin/io/github/addoncommunity/galactifun/base/BaseUniverse.kt +++ b/src/main/kotlin/io/github/addoncommunity/galactifun/base/BaseUniverse.kt @@ -9,7 +9,7 @@ import io.github.addoncommunity.galactifun.base.objects.earth.Moon import io.github.addoncommunity.galactifun.util.units.Distance.Companion.kilometers import io.github.addoncommunity.galactifun.util.units.Distance.Companion.lightYears import io.github.addoncommunity.galactifun.util.units.Mass.Companion.kilograms -import io.github.addoncommunity.galactifun.util.years +import io.github.addoncommunity.galactifun.util.units.years import org.bukkit.Material import org.bukkit.inventory.ItemStack diff --git a/src/main/kotlin/io/github/addoncommunity/galactifun/base/objects/earth/Earth.kt b/src/main/kotlin/io/github/addoncommunity/galactifun/base/objects/earth/Earth.kt index 036ca45..c32401f 100644 --- a/src/main/kotlin/io/github/addoncommunity/galactifun/base/objects/earth/Earth.kt +++ b/src/main/kotlin/io/github/addoncommunity/galactifun/base/objects/earth/Earth.kt @@ -8,7 +8,7 @@ import io.github.addoncommunity.galactifun.base.BaseUniverse import io.github.addoncommunity.galactifun.pluginInstance import io.github.addoncommunity.galactifun.util.units.Distance.Companion.kilometers import io.github.addoncommunity.galactifun.util.units.Mass.Companion.kilograms -import io.github.addoncommunity.galactifun.util.years +import io.github.addoncommunity.galactifun.util.units.years import org.bukkit.Material import org.bukkit.World import org.bukkit.WorldCreator diff --git a/src/main/kotlin/io/github/addoncommunity/galactifun/util/Constants.kt b/src/main/kotlin/io/github/addoncommunity/galactifun/util/Constants.kt index 0469323..0752d06 100644 --- a/src/main/kotlin/io/github/addoncommunity/galactifun/util/Constants.kt +++ b/src/main/kotlin/io/github/addoncommunity/galactifun/util/Constants.kt @@ -11,8 +11,16 @@ object Constants { const val GRAVITATIONAL_CONSTANT = 6.674e-11 const val EARTH_GRAVITY = 9.81 + /** + * The maximum radix for the [Int.toString] and [String.toInt] functions. + */ const val MAX_RADIX = 36 + /** + * How much faster time is concerning orbital mechanics than in real life. + */ + const val ORBIT_TIME_SCALE = 12.0 + fun locationZero(world: World?): Location { return Location(world, 0.0, 0.0, 0.0) } diff --git a/src/main/kotlin/io/github/addoncommunity/galactifun/util/LazyDouble.kt b/src/main/kotlin/io/github/addoncommunity/galactifun/util/LazyDouble.kt new file mode 100644 index 0000000..e4720a2 --- /dev/null +++ b/src/main/kotlin/io/github/addoncommunity/galactifun/util/LazyDouble.kt @@ -0,0 +1,21 @@ +package io.github.addoncommunity.galactifun.util + +import kotlin.reflect.KProperty + +class LazyDouble(private val supplier: () -> Double) { + + private var value = Double.NaN + private var initialized = false + operator fun getValue(thisRef: Any?, property: KProperty<*>): Double { + if (!initialized) { + value = supplier() + initialized = true + } + return value + } + + operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Double) { + this.value = value + initialized = true + } +} \ No newline at end of file diff --git a/src/main/kotlin/io/github/addoncommunity/galactifun/util/RandomUtils.kt b/src/main/kotlin/io/github/addoncommunity/galactifun/util/RandomUtils.kt index edc432d..3045661 100644 --- a/src/main/kotlin/io/github/addoncommunity/galactifun/util/RandomUtils.kt +++ b/src/main/kotlin/io/github/addoncommunity/galactifun/util/RandomUtils.kt @@ -14,8 +14,6 @@ import org.bukkit.event.player.PlayerTeleportEvent import org.bukkit.metadata.FixedMetadataValue import java.util.* import java.util.concurrent.CompletableFuture -import kotlin.time.Duration -import kotlin.time.Duration.Companion.days fun String.key(): NamespacedKey = NamespacedKey(pluginInstance, this) @@ -90,9 +88,3 @@ inline fun Map.mergeMaps(other: Map, merge: (V, V) -> V): Map } operator fun TextColor.plus(s: String): TextComponent = Component.text().color(this).content(s).build() - -val Double.years: Duration - get() = (this * 365.25).days - -val Int.years: Duration - get() = (this * 365.25).days \ No newline at end of file diff --git a/src/main/kotlin/io/github/addoncommunity/galactifun/util/units/Time.kt b/src/main/kotlin/io/github/addoncommunity/galactifun/util/units/Time.kt new file mode 100644 index 0000000..ce1c851 --- /dev/null +++ b/src/main/kotlin/io/github/addoncommunity/galactifun/util/units/Time.kt @@ -0,0 +1,14 @@ +package io.github.addoncommunity.galactifun.util.units + +import kotlin.time.Duration +import kotlin.time.Duration.Companion.days +import kotlin.time.DurationUnit + +inline val Double.years: Duration + get() = (this * 365.25).days + +inline val Int.years: Duration + get() = this.toDouble().years + +inline val Duration.doubleSeconds: Double + get() = toDouble(DurationUnit.SECONDS) \ No newline at end of file