Archived
0
This repository has been archived on 2024-08-27. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Parcels/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt

215 lines
6.6 KiB
Kotlin

package io.dico.parcels2
import io.dico.parcels2.math.Vec2i
import io.dico.parcels2.math.floor
import io.dico.parcels2.storage.SerializableParcel
import io.dico.parcels2.storage.SerializableWorld
import io.dico.parcels2.storage.Storage
import io.dico.parcels2.util.doAwait
import kotlinx.coroutines.experimental.launch
import org.bukkit.Bukkit
import org.bukkit.Location
import org.bukkit.World
import org.bukkit.WorldCreator
import org.bukkit.block.Block
import org.bukkit.entity.Entity
import org.bukkit.entity.Player
import java.util.*
import kotlin.coroutines.experimental.buildSequence
import kotlin.reflect.jvm.javaMethod
import kotlin.reflect.jvm.kotlinFunction
class Worlds(private val plugin: ParcelsPlugin) {
val worlds: Map<String, ParcelWorld> get() = _worlds
private val _worlds: MutableMap<String, ParcelWorld> = HashMap()
fun getWorld(name: String): ParcelWorld? = _worlds[name]
fun getWorld(world: World): ParcelWorld? = getWorld(world.name)
fun getParcelAt(block: Block): Parcel? = getParcelAt(block.world, block.x, block.z)
fun getParcelAt(player: Player): Parcel? = getParcelAt(player.location)
fun getParcelAt(location: Location): Parcel? = getParcelAt(location.world, location.x.floor(), location.z.floor())
fun getParcelAt(world: World, x: Int, z: Int): Parcel? = getParcelAt(world.name, x, z)
fun getParcelAt(world: String, x: Int, z: Int): Parcel? {
with(getWorld(world) ?: return null) {
return generator.parcelAt(x, z)
}
}
init {
val function = ::loadWorlds
function.javaMethod!!.kotlinFunction
}
operator fun SerializableParcel.invoke(): Parcel? {
return world()?.parcelByID(pos)
}
operator fun SerializableWorld.invoke(): ParcelWorld? {
return world?.let { getWorld(it) }
}
fun loadWorlds(options: Options) {
for ((worldName, worldOptions) in options.worlds.entries) {
val world: ParcelWorld
try {
world = ParcelWorld(
worldName,
worldOptions,
worldOptions.generator.getGenerator(this, worldName),
plugin.storage)
} catch (ex: Exception) {
ex.printStackTrace()
continue
}
_worlds.put(worldName, world)
if (Bukkit.getWorld(worldName) == null) {
plugin.doAwait {
cond = {
try {
// server.getDefaultGameMode() throws an error before any worlds are initialized.
// createWorld() below calls that method.
// Plugin needs to load on STARTUP for generators to be registered correctly.
// Means we need to await the initial worlds getting loaded.
plugin.server.defaultGameMode; true
} catch (ex: Throwable) {
false
}
}
onSuccess = {
val bworld = WorldCreator(worldName).generator(world.generator).createWorld()
val spawn = world.generator.getFixedSpawnLocation(bworld, null)
bworld.setSpawnLocation(spawn.x.floor(), spawn.y.floor(), spawn.z.floor())
}
}
}
}
}
}
interface ParcelProvider {
fun parcelAt(x: Int, z: Int): Parcel?
fun parcelAt(vec: Vec2i): Parcel? = parcelAt(vec.x, vec.z)
fun parcelAt(loc: Location): Parcel? = parcelAt(loc.x.floor(), loc.z.floor())
fun parcelAt(entity: Entity): Parcel? = parcelAt(entity.location)
fun parcelAt(block: Block): Parcel? = parcelAt(block.x, block.z)
}
class ParcelWorld constructor(val name: String,
val options: WorldOptions,
val generator: ParcelGenerator,
val storage: Storage) : ParcelProvider by generator, ParcelContainer {
val world: World by lazy { Bukkit.getWorld(name) ?: throw NullPointerException("World $name does not appear to be loaded") }
val container: ParcelContainer = DefaultParcelContainer(this, storage)
override fun parcelByID(x: Int, z: Int): Parcel? {
return container.parcelByID(x, z)
}
override fun nextEmptyParcel(): Parcel? {
return container.nextEmptyParcel()
}
fun parcelByID(id: Vec2i): Parcel? = parcelByID(id.x, id.z)
fun enforceOptionsIfApplicable() {
val world = world
val options = options
if (options.dayTime) {
world.setGameRuleValue("doDaylightCycle", "false")
world.setTime(6000)
}
if (options.noWeather) {
world.setStorm(false)
world.setThundering(false)
world.weatherDuration = Integer.MAX_VALUE
}
world.setGameRuleValue("doTileDrops", "${options.doTileDrops}")
}
}
interface ParcelContainer {
fun parcelByID(x: Int, z: Int): Parcel?
fun nextEmptyParcel(): Parcel?
}
typealias ParcelContainerFactory = (ParcelWorld) -> ParcelContainer
class DefaultParcelContainer(private val world: ParcelWorld,
private val storage: Storage) : ParcelContainer {
private var parcels: Array<Array<Parcel>>
init {
parcels = initArray(world.options.axisLimit, world)
}
fun resizeIfSizeChanged() {
if (parcels.size / 2 != world.options.axisLimit) {
resize(world.options.axisLimit)
}
}
fun resize(axisLimit: Int) {
parcels = initArray(axisLimit, world, this)
}
fun initArray(axisLimit: Int, world: ParcelWorld, cur: DefaultParcelContainer? = null): Array<Array<Parcel>> {
val arraySize = 2 * axisLimit + 1
return Array(arraySize) {
val x = it - axisLimit
Array(arraySize) {
val z = it - axisLimit
cur?.parcelByID(x, z) ?: Parcel(world, Vec2i(x, z))
}
}
}
override fun parcelByID(x: Int, z: Int): Parcel? {
return parcels.getOrNull(x)?.getOrNull(z)
}
override fun nextEmptyParcel(): Parcel? {
return parcels[0][0]
TODO()
}
fun allParcels(): Sequence<Parcel> = buildSequence {
for (array in parcels) {
yieldAll(array.iterator())
}
}
fun loadAllData() {
val channel = storage.readParcelData(allParcels(), 100)
launch(storage.asyncDispatcher) {
for ((parcel, data) in channel) {
data?.let { parcel.copyDataIgnoringDatabase(it) }
}
}
}
}