port RedstonerPlots a bit
This commit is contained in:
105
src/main/kotlin/io/dico/parcels2/Options.kt
Normal file
105
src/main/kotlin/io/dico/parcels2/Options.kt
Normal file
@@ -0,0 +1,105 @@
|
||||
package io.dico.parcels2
|
||||
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||
import io.dico.parcels2.storage.Storage
|
||||
import io.dico.parcels2.storage.StorageFactory
|
||||
import io.dico.parcels2.storage.yamlObjectMapper
|
||||
import org.bukkit.Bukkit
|
||||
import org.bukkit.Bukkit.createBlockData
|
||||
import org.bukkit.GameMode
|
||||
import org.bukkit.Material
|
||||
import org.bukkit.block.Biome
|
||||
import org.bukkit.block.data.BlockData
|
||||
import java.io.Reader
|
||||
import java.io.Writer
|
||||
import java.util.*
|
||||
|
||||
class Options {
|
||||
var worlds: Map<String, WorldOptions> = HashMap()
|
||||
private set
|
||||
var storage: StorageOptions = StorageOptions("mysql", DataConnectionOptions())
|
||||
|
||||
fun addWorld(name: String, options: WorldOptions) = (worlds as MutableMap).put(name, options)
|
||||
|
||||
fun addDefaultWorld() = addWorld("plotworld", WorldOptions())
|
||||
|
||||
fun writeTo(writer: Writer) = yamlObjectMapper.writeValue(writer, this)
|
||||
|
||||
fun mergeFrom(reader: Reader) = yamlObjectMapper.readerForUpdating(this).readValue<Options>(reader)
|
||||
|
||||
override fun toString(): String = yamlObjectMapper.writeValueAsString(this)
|
||||
|
||||
}
|
||||
|
||||
data class WorldOptions(var gameMode: GameMode? = GameMode.CREATIVE,
|
||||
var dayTime: Boolean = true,
|
||||
var noWeather: Boolean = true,
|
||||
var dropEntityItems: Boolean = true,
|
||||
var doTileDrops: Boolean = false,
|
||||
var disableExplosions: Boolean = true,
|
||||
var blockPortalCreation: Boolean = true,
|
||||
var blockMobSpawning: Boolean = true,
|
||||
var blockedItems: Set<Material> = EnumSet.of(Material.FLINT_AND_STEEL, Material.SNOWBALL),
|
||||
var axisLimit: Int = 10,
|
||||
var generator: GeneratorOptions = DefaultGeneratorOptions()) {
|
||||
|
||||
}
|
||||
|
||||
abstract class GeneratorOptions {
|
||||
|
||||
abstract fun generatorFactory(): GeneratorFactory
|
||||
|
||||
fun getGenerator(worldName: String) = generatorFactory().newParcelGenerator(worldName, this)
|
||||
|
||||
}
|
||||
|
||||
data class DefaultGeneratorOptions(var defaultBiome: Biome = Biome.JUNGLE,
|
||||
var wallType: BlockData = createBlockData(Material.STONE_SLAB),
|
||||
var floorType: BlockData = createBlockData(Material.QUARTZ_BLOCK),
|
||||
var fillType: BlockData = createBlockData(Material.QUARTZ_BLOCK),
|
||||
var pathMainType: BlockData = createBlockData(Material.SANDSTONE),
|
||||
var pathAltType: BlockData = createBlockData(Material.REDSTONE_BLOCK),
|
||||
var parcelSize: Int = 101,
|
||||
var pathSize: Int = 9,
|
||||
var floorHeight: Int = 64,
|
||||
var offsetX: Int = 0,
|
||||
var offsetZ: Int = 0) : GeneratorOptions() {
|
||||
|
||||
override fun generatorFactory(): GeneratorFactory = DefaultParcelGenerator.Factory
|
||||
|
||||
}
|
||||
|
||||
class StorageOptions(val dialect: String,
|
||||
val options: Any) {
|
||||
|
||||
@get:JsonIgnore
|
||||
val factory = StorageFactory.getFactory(dialect) ?: throw IllegalArgumentException("Invalid storage dialect: $dialect")
|
||||
|
||||
fun newStorageInstance(): Storage = factory.newStorageInstance(dialect, options)
|
||||
|
||||
}
|
||||
|
||||
data class DataConnectionOptions(val address: String = "localhost",
|
||||
val database: String = "parcels",
|
||||
val username: String = "root",
|
||||
val password: String = "",
|
||||
val poolSize: Int = 4) {
|
||||
|
||||
fun splitAddressAndPort(defaultPort: Int = 3306): Pair<String, Int>? {
|
||||
val idx = address.indexOf(":").takeUnless { it == -1 } ?: return Pair(address, defaultPort)
|
||||
|
||||
val addressName = address.substring(0, idx).takeUnless { it.isBlank() } ?: return null.also {
|
||||
logger.error("(Invalidly) blank address in data storage options")
|
||||
}
|
||||
|
||||
val port = address.substring(idx).toIntOrNull() ?: return null.also {
|
||||
logger.error("Invalid port number in data storage options: $it, using $defaultPort as default")
|
||||
}
|
||||
|
||||
return Pair(addressName, port)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
data class DataFileOptions(val location: String = "/flatfile-storage/")
|
||||
@@ -2,6 +2,7 @@ package io.dico.parcels2
|
||||
|
||||
import io.dico.parcels2.math.Vec2i
|
||||
import io.dico.parcels2.util.getPlayerName
|
||||
import io.dico.parcels2.util.hasBuildAnywhere
|
||||
import org.bukkit.Bukkit
|
||||
import org.bukkit.entity.Player
|
||||
import java.util.*
|
||||
@@ -10,13 +11,29 @@ class Parcel(val world: ParcelWorld,
|
||||
val pos: Vec2i,
|
||||
var data: ParcelData = ParcelData()) {
|
||||
|
||||
|
||||
val id get() = "${pos.x}:${pos.z}"
|
||||
|
||||
}
|
||||
|
||||
class ParcelData {
|
||||
val owner: ParcelOwner? = null
|
||||
val added = mutableMapOf<UUID, Boolean>()
|
||||
private val added = mutableMapOf<UUID, AddedStatus>()
|
||||
var owner: ParcelOwner? = null
|
||||
|
||||
fun setAddedStatus(uuid: UUID): AddedStatus = added.getOrDefault(uuid, AddedStatus.DEFAULT)
|
||||
fun setAddedStatus(uuid: UUID, state: AddedStatus) = state.takeIf { it != AddedStatus.DEFAULT }?.let { added[uuid] = it }
|
||||
?: added.remove(uuid)
|
||||
|
||||
fun isBanned(uuid: UUID) = setAddedStatus(uuid) == AddedStatus.BANNED
|
||||
fun isAllowed(uuid: UUID) = setAddedStatus(uuid) == AddedStatus.ALLOWED
|
||||
fun canBuild(player: Player) = isAllowed(player.uniqueId)
|
||||
|| owner?.matches(player, allowNameMatch = false) ?: false
|
||||
|| player.hasBuildAnywhere
|
||||
}
|
||||
|
||||
enum class AddedStatus {
|
||||
DEFAULT,
|
||||
ALLOWED,
|
||||
BANNED
|
||||
}
|
||||
|
||||
data class ParcelOwner(val uuid: UUID? = null,
|
||||
@@ -29,11 +46,13 @@ data class ParcelOwner(val uuid: UUID? = null,
|
||||
val playerName get() = getPlayerName(uuid, name)
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
val offlinePlayer get() = (uuid?.let { Bukkit.getOfflinePlayer(it) } ?: Bukkit.getOfflinePlayer(name))
|
||||
?.takeIf { it.isOnline() || it.hasPlayedBefore() }
|
||||
val offlinePlayer
|
||||
get() = (uuid?.let { Bukkit.getOfflinePlayer(it) } ?: Bukkit.getOfflinePlayer(name))
|
||||
?.takeIf { it.isOnline() || it.hasPlayedBefore() }
|
||||
|
||||
fun matches(player: Player, allowNameMatch: Boolean = false): Boolean {
|
||||
return player.uniqueId == uuid || (allowNameMatch && player.name == name)
|
||||
return uuid?.let { it == player.uniqueId } ?: false
|
||||
|| (allowNameMatch && name?.let { it == player.name } ?: false)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,174 @@
|
||||
package io.dico.parcels2
|
||||
|
||||
class ParcelWorld {
|
||||
import io.dico.parcels2.math.Vec2i
|
||||
import io.dico.parcels2.math.floor
|
||||
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
|
||||
|
||||
val worlds: Map<String, ParcelWorld> get() = _worlds
|
||||
private val _worlds: MutableMap<String, ParcelWorld> = HashMap()
|
||||
|
||||
fun getWorld(name: String): ParcelWorld? = _worlds.get(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)
|
||||
}
|
||||
}
|
||||
|
||||
fun loadWorlds(options: Options) {
|
||||
for ((worldName, worldOptions) in options.worlds.entries) {
|
||||
val world: ParcelWorld
|
||||
try {
|
||||
world = ParcelWorld(worldName, worldOptions, worldOptions.generator.getGenerator(worldName))
|
||||
} catch (ex: Exception) {
|
||||
ex.printStackTrace()
|
||||
continue
|
||||
}
|
||||
|
||||
_worlds.put(worldName, world)
|
||||
|
||||
if (Bukkit.getWorld(worldName) == null) {
|
||||
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(val name: String,
|
||||
val options: WorldOptions,
|
||||
val generator: ParcelGenerator) : ParcelProvider by generator {
|
||||
val world: World by lazy {
|
||||
val tmp = Bukkit.getWorld(name)
|
||||
if (tmp == null) {
|
||||
throw NullPointerException("World $name does not appear to be loaded")
|
||||
}
|
||||
tmp
|
||||
}
|
||||
|
||||
val container: ParcelContainer = DefaultParcelContainer(this)
|
||||
|
||||
fun parcelByID(x: Int, z: Int): Parcel? {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
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}")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
abstract class ParcelContainer {
|
||||
|
||||
abstract fun ployByID(x: Int, z: Int): Parcel?
|
||||
|
||||
abstract fun nextEmptyParcel(): Parcel?
|
||||
|
||||
}
|
||||
|
||||
class DefaultParcelContainer(private val world: ParcelWorld) : 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?.ployByID(x, z) ?: Parcel(world, Vec2i(x, z))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
override fun ployByID(x: Int, z: Int): Parcel? {
|
||||
return parcels[x][z]
|
||||
}
|
||||
|
||||
override fun nextEmptyParcel(): Parcel? {
|
||||
TODO()
|
||||
}
|
||||
|
||||
fun allParcels(): Sequence<Parcel> = buildSequence {
|
||||
for (array in parcels) {
|
||||
yieldAll(array.iterator())
|
||||
}
|
||||
}
|
||||
|
||||
fun loadAllData() {
|
||||
/*
|
||||
val channel = Main.instance.storage.readParcelData(allParcels(), 100).channel
|
||||
launch(Main.instance.storage.asyncDispatcher) {
|
||||
for ((parcel, data) in channel) {
|
||||
if (data != null) {
|
||||
parcel.data = data
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
TODO()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +1,15 @@
|
||||
package io.dico.parcels2
|
||||
|
||||
import org.bukkit.plugin.java.JavaPlugin
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
val logger = LoggerFactory.getLogger("ParcelsPlugin")
|
||||
|
||||
class ParcelsPlugin : JavaPlugin() {
|
||||
|
||||
override fun onEnable() {
|
||||
super.onEnable()
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
263
src/main/kotlin/io/dico/parcels2/WorldGenerator.kt
Normal file
263
src/main/kotlin/io/dico/parcels2/WorldGenerator.kt
Normal file
@@ -0,0 +1,263 @@
|
||||
package io.dico.parcels2
|
||||
|
||||
import io.dico.parcels2.math.Vec2i
|
||||
import io.dico.parcels2.math.clamp
|
||||
import io.dico.parcels2.math.even
|
||||
import io.dico.parcels2.math.umod
|
||||
import org.bukkit.*
|
||||
import org.bukkit.Bukkit.createBlockData
|
||||
import org.bukkit.block.Biome
|
||||
import org.bukkit.block.Block
|
||||
import org.bukkit.block.BlockFace
|
||||
import org.bukkit.block.Skull
|
||||
import org.bukkit.block.data.BlockData
|
||||
import org.bukkit.block.data.type.Sign
|
||||
import org.bukkit.block.data.type.Slab
|
||||
import org.bukkit.entity.Entity
|
||||
import org.bukkit.generator.BlockPopulator
|
||||
import org.bukkit.generator.ChunkGenerator
|
||||
import java.util.*
|
||||
import kotlin.coroutines.experimental.buildIterator
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
abstract class ParcelGenerator : ChunkGenerator(), ParcelProvider {
|
||||
abstract val world: ParcelWorld
|
||||
|
||||
abstract val factory: GeneratorFactory
|
||||
|
||||
abstract override fun generateChunkData(world: World?, random: Random?, chunkX: Int, chunkZ: Int, biome: BiomeGrid?): ChunkData
|
||||
|
||||
abstract fun populate(world: World?, random: Random?, chunk: Chunk?)
|
||||
|
||||
abstract override fun getFixedSpawnLocation(world: World?, random: Random?): Location
|
||||
|
||||
override fun getDefaultPopulators(world: World?): MutableList<BlockPopulator> {
|
||||
return Collections.singletonList(object : BlockPopulator() {
|
||||
override fun populate(world: World?, random: Random?, chunk: Chunk?) {
|
||||
this@ParcelGenerator.populate(world, random, chunk)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
abstract fun updateOwner(parcel: Parcel)
|
||||
|
||||
abstract fun getBottomCoord(parcel: Parcel): Vec2i
|
||||
|
||||
abstract fun getHomeLocation(parcel: Parcel): Location
|
||||
|
||||
abstract fun setBiome(parcel: Parcel, biome: Biome)
|
||||
|
||||
abstract fun getEntities(parcel: Parcel): Collection<Entity>
|
||||
|
||||
abstract fun getBlocks(parcel: Parcel, yRange: IntRange = 0..255): Iterator<Block>
|
||||
|
||||
}
|
||||
|
||||
interface GeneratorFactory {
|
||||
companion object GeneratorFactories {
|
||||
private val map: MutableMap<String, GeneratorFactory> = HashMap()
|
||||
|
||||
fun registerFactory(generator: GeneratorFactory): Boolean = map.putIfAbsent(generator.name, generator) == null
|
||||
|
||||
fun getFactory(name: String): GeneratorFactory? = map.get(name)
|
||||
|
||||
init {
|
||||
registerFactory(DefaultParcelGenerator.Factory)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val name: String
|
||||
|
||||
val optionsClass: KClass<out GeneratorOptions>
|
||||
|
||||
fun newParcelGenerator(worldName: String, options: GeneratorOptions): ParcelGenerator
|
||||
|
||||
}
|
||||
|
||||
class DefaultParcelGenerator(name: String, private val o: DefaultGeneratorOptions) : ParcelGenerator() {
|
||||
override val world: ParcelWorld by lazy { TODO() }
|
||||
override val factory = Factory
|
||||
|
||||
companion object Factory : GeneratorFactory {
|
||||
override val name get() = "default"
|
||||
override val optionsClass get() = DefaultGeneratorOptions::class
|
||||
override fun newParcelGenerator(worldName: String, options: GeneratorOptions): ParcelGenerator {
|
||||
return DefaultParcelGenerator(worldName, options as DefaultGeneratorOptions)
|
||||
}
|
||||
}
|
||||
|
||||
val sectionSize = o.parcelSize + o.pathSize
|
||||
val pathOffset = (if (o.pathSize % 2 == 0) o.pathSize + 2 else o.pathSize + 1) / 2
|
||||
val makePathMain = o.pathSize > 2
|
||||
val makePathAlt = o.pathSize > 4
|
||||
|
||||
private inline fun <T> generate(chunkX: Int,
|
||||
chunkZ: Int,
|
||||
floor: T, wall:
|
||||
T, pathMain: T,
|
||||
pathAlt: T,
|
||||
fill: T,
|
||||
setter: (Int, Int, Int, T) -> Unit) {
|
||||
|
||||
val floorHeight = o.floorHeight
|
||||
val parcelSize = o.parcelSize
|
||||
val sectionSize = sectionSize
|
||||
val pathOffset = pathOffset
|
||||
val makePathMain = makePathMain
|
||||
val makePathAlt = makePathAlt
|
||||
|
||||
// parcel bottom x and z
|
||||
// umod is unsigned %: the result is always >= 0
|
||||
val pbx = ((chunkX shl 4) - o.offsetX) umod sectionSize
|
||||
val pbz = ((chunkZ shl 4) - o.offsetZ) umod sectionSize
|
||||
|
||||
var curHeight: Int
|
||||
var x: Int
|
||||
var z: Int
|
||||
for (cx in 0..15) {
|
||||
for (cz in 0..15) {
|
||||
x = (pbx + cx) % sectionSize - pathOffset
|
||||
z = (pbz + cz) % sectionSize - pathOffset
|
||||
curHeight = floorHeight
|
||||
|
||||
val type = when {
|
||||
(x in 0 until parcelSize && z in 0 until parcelSize) -> floor
|
||||
(x in -1..parcelSize && z in -1..parcelSize) -> {
|
||||
curHeight++
|
||||
wall
|
||||
}
|
||||
(makePathAlt && x in -2 until parcelSize + 2 && z in -2 until parcelSize + 2) -> pathAlt
|
||||
(makePathMain) -> pathMain
|
||||
else -> {
|
||||
curHeight++
|
||||
wall
|
||||
}
|
||||
}
|
||||
|
||||
for (y in 0 until curHeight) {
|
||||
setter(cx, y, cz, fill)
|
||||
}
|
||||
setter(cx, curHeight, cz, type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun generateChunkData(world: World?, random: Random?, chunkX: Int, chunkZ: Int, biome: BiomeGrid?): ChunkData {
|
||||
val out = Bukkit.createChunkData(world)
|
||||
generate(chunkX, chunkZ, o.floorType, o.wallType, o.pathMainType, o.pathAltType, o.fillType) { x, y, z, type ->
|
||||
out.setBlock(x, y, z, type)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
|
||||
override fun populate(world: World?, random: Random?, chunk: Chunk?) {
|
||||
/*
|
||||
generate(chunk!!.x, chunk.z, o.floorType.data, o.wallType.data, o.pathMainType.data, o.pathAltType.data, o.fillType.data) { x, y, z, type ->
|
||||
if (type == 0.toByte()) chunk.getBlock(x, y, z).setData(type, false)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
override fun getFixedSpawnLocation(world: World?, random: Random?): Location {
|
||||
val fix = if (o.parcelSize.even) 0.5 else 0.0
|
||||
return Location(world, o.offsetX + fix, o.floorHeight + 1.0, o.offsetZ + fix)
|
||||
}
|
||||
|
||||
override fun parcelAt(x: Int, z: Int): Parcel? {
|
||||
val sectionSize = sectionSize
|
||||
val parcelSize = o.parcelSize
|
||||
val absX = x - o.offsetX - pathOffset
|
||||
val absZ = z - o.offsetZ - pathOffset
|
||||
val modX = absX umod sectionSize
|
||||
val modZ = absZ umod sectionSize
|
||||
if (0 <= modX && modX < parcelSize && 0 <= modZ && modZ < parcelSize) {
|
||||
return world.parcelByID((absX - modX) / sectionSize, (absZ - modZ) / sectionSize)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun getBottomCoord(parcel: Parcel): Vec2i = Vec2i(sectionSize * parcel.pos.x + pathOffset + o.offsetX,
|
||||
sectionSize * parcel.pos.z + pathOffset + o.offsetZ)
|
||||
|
||||
override fun getHomeLocation(parcel: Parcel): Location {
|
||||
val bottom = getBottomCoord(parcel)
|
||||
return Location(world.world, bottom.x.toDouble(), o.floorHeight + 1.0, bottom.z + (o.parcelSize - 1) / 2.0, -90F, 0F)
|
||||
}
|
||||
|
||||
override fun updateOwner(parcel: Parcel) {
|
||||
val world = this.world.world
|
||||
val b = getBottomCoord(parcel)
|
||||
|
||||
val wallBlock = world.getBlockAt(b.x - 1, o.floorHeight + 1, b.z - 1)
|
||||
val signBlock = world.getBlockAt(b.x - 2, o.floorHeight + 1, b.z - 1)
|
||||
val skullBlock = world.getBlockAt(b.x - 1, o.floorHeight + 2, b.z - 1)
|
||||
|
||||
val owner = parcel.data?.owner
|
||||
if (owner == null) {
|
||||
wallBlock.blockData = o.wallType
|
||||
signBlock.type = Material.AIR
|
||||
skullBlock.type = Material.AIR
|
||||
} else {
|
||||
|
||||
val wallBlockType: BlockData = if (o.wallType is Slab)
|
||||
(o.wallType.clone() as Slab).apply { type = Slab.Type.DOUBLE }
|
||||
else
|
||||
o.wallType
|
||||
|
||||
wallBlock.blockData = wallBlockType
|
||||
|
||||
signBlock.blockData = (createBlockData(Material.WALL_SIGN) as Sign).apply { rotation = BlockFace.NORTH }
|
||||
|
||||
val sign = signBlock.state as org.bukkit.block.Sign
|
||||
sign.setLine(0, parcel.id)
|
||||
sign.setLine(2, owner.playerName)
|
||||
sign.update()
|
||||
|
||||
skullBlock.type = Material.PLAYER_HEAD
|
||||
val skull = skullBlock.state as Skull
|
||||
if (owner.uuid != null) {
|
||||
skull.owningPlayer = owner.offlinePlayer
|
||||
} else {
|
||||
skull.owner = owner.name
|
||||
}
|
||||
skull.rotation = BlockFace.WEST
|
||||
skull.update()
|
||||
}
|
||||
}
|
||||
|
||||
override fun setBiome(parcel: Parcel, biome: Biome) {
|
||||
val world = this.world.world
|
||||
val b = getBottomCoord(parcel)
|
||||
val parcelSize = o.parcelSize
|
||||
for (x in b.x until b.x + parcelSize) {
|
||||
for (z in b.z until b.z + parcelSize) {
|
||||
world.setBiome(x, z, biome)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getEntities(parcel: Parcel): Collection<Entity> {
|
||||
val world = this.world.world
|
||||
val b = getBottomCoord(parcel)
|
||||
val parcelSize = o.parcelSize
|
||||
val center = Location(world, (b.x + parcelSize) / 2.0, 128.0, (b.z + parcelSize) / 2.0)
|
||||
return world.getNearbyEntities(center, parcelSize / 2.0 + 0.2, 128.0, parcelSize / 2.0 + 0.2)
|
||||
}
|
||||
|
||||
override fun getBlocks(parcel: Parcel, yRange: IntRange): Iterator<Block> = buildIterator {
|
||||
val range = yRange.clamp(0, 255)
|
||||
val world = this@DefaultParcelGenerator.world.world
|
||||
val b = getBottomCoord(parcel)
|
||||
val parcelSize = o.parcelSize
|
||||
for (x in b.x until b.x + parcelSize) {
|
||||
for (z in b.z until b.z + parcelSize) {
|
||||
for (y in range) {
|
||||
yield(world.getBlockAt(x, y, z))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
32
src/main/kotlin/io/dico/parcels2/math/NumberExtensions.kt
Normal file
32
src/main/kotlin/io/dico/parcels2/math/NumberExtensions.kt
Normal file
@@ -0,0 +1,32 @@
|
||||
package io.dico.parcels2.math
|
||||
|
||||
fun Double.floor(): Int {
|
||||
val down = toInt()
|
||||
if (down.toDouble() != this && (java.lang.Double.doubleToRawLongBits(this).ushr(63).toInt()) == 1) {
|
||||
return down-1
|
||||
}
|
||||
return down
|
||||
}
|
||||
|
||||
infix fun Int.umod(divisor: Int): Int {
|
||||
val out = this % divisor
|
||||
if (out < 0) {
|
||||
return out + divisor
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
val Int.even: Boolean get() = and(1) == 0
|
||||
|
||||
fun IntRange.clamp(min: Int, max: Int): IntRange {
|
||||
if (first < min) {
|
||||
if (last > max) {
|
||||
return IntRange(min, max)
|
||||
}
|
||||
return IntRange(min, last)
|
||||
}
|
||||
if (last > max) {
|
||||
return IntRange(first, max)
|
||||
}
|
||||
return this
|
||||
}
|
||||
37
src/main/kotlin/io/dico/parcels2/storage/Backing.kt
Normal file
37
src/main/kotlin/io/dico/parcels2/storage/Backing.kt
Normal file
@@ -0,0 +1,37 @@
|
||||
package io.dico.parcels2.storage
|
||||
|
||||
import io.dico.parcels2.Parcel
|
||||
import io.dico.parcels2.ParcelData
|
||||
import io.dico.parcels2.ParcelOwner
|
||||
import io.dico.parcels2.storage.SerializableParcel
|
||||
import kotlinx.coroutines.experimental.channels.ProducerScope
|
||||
import java.util.*
|
||||
|
||||
interface Backing {
|
||||
|
||||
val name: String
|
||||
|
||||
suspend fun init()
|
||||
|
||||
suspend fun shutdown()
|
||||
|
||||
/**
|
||||
* This producer function is capable of constantly reading plots from a potentially infinite sequence,
|
||||
* and provide plotdata for it as read from the database.
|
||||
*/
|
||||
|
||||
suspend fun ProducerScope<Pair<Parcel, ParcelData?>>.produceParcelData(parcels: Sequence<Parcel>)
|
||||
|
||||
suspend fun readParcelData(plotFor: Parcel): ParcelData?
|
||||
|
||||
suspend fun getOwnedParcels(user: ParcelOwner): List<SerializableParcel>
|
||||
|
||||
suspend fun setParcelOwner(plotFor: Parcel, owner: ParcelOwner?)
|
||||
|
||||
suspend fun setParcelPlayerState(plotFor: Parcel, player: UUID, state: Boolean?)
|
||||
|
||||
suspend fun setParcelAllowsInteractInventory(plot: Parcel, value: Boolean)
|
||||
|
||||
suspend fun setParcelAllowsInteractInputs(plot: Parcel, value: Boolean)
|
||||
|
||||
}
|
||||
85
src/main/kotlin/io/dico/parcels2/storage/Exposed.kt
Normal file
85
src/main/kotlin/io/dico/parcels2/storage/Exposed.kt
Normal file
@@ -0,0 +1,85 @@
|
||||
package io.dico.parcels2.storage
|
||||
|
||||
import com.zaxxer.hikari.HikariDataSource
|
||||
import io.dico.parcels2.Parcel
|
||||
import io.dico.parcels2.ParcelData
|
||||
import io.dico.parcels2.ParcelOwner
|
||||
import kotlinx.coroutines.experimental.channels.ProducerScope
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
import org.jetbrains.exposed.sql.ReferenceOption
|
||||
import org.jetbrains.exposed.sql.Table
|
||||
import java.util.*
|
||||
import javax.sql.DataSource
|
||||
|
||||
object ParcelsTable : Table() {
|
||||
val id = integer("id").autoIncrement().primaryKey()
|
||||
val px = integer("px")
|
||||
val pz = integer("pz")
|
||||
val world_uuid = binary("world_uuid", 16).also { uniqueIndex("location", it, px, pz) }
|
||||
val world = varchar("world", 32).nullable()
|
||||
val owner_uuid = binary("owner_uuid", 16).nullable()
|
||||
val owner = varchar("owner", 16).nullable()
|
||||
}
|
||||
|
||||
object ParcelsAddedTable : Table() {
|
||||
val id = integer("id").references(ParcelsTable.id, ReferenceOption.CASCADE)
|
||||
val player_uuid = binary("player_uuid", 16).also { uniqueIndex("pair", id, it) }
|
||||
val allowed_flag = bool("allowed_flag")
|
||||
}
|
||||
|
||||
object PlayerAddedTable : Table() {
|
||||
val owner_uuid = binary("owner_uuid", 16)
|
||||
val player_uuid = binary("player_uuid", 16).also { uniqueIndex("pair", owner_uuid, it) }
|
||||
val allowed_flag = bool("allowed_flag")
|
||||
}
|
||||
|
||||
class ExposedBacking(val dataSource: DataSource) : Backing {
|
||||
override val name get() = "Exposed"
|
||||
lateinit var database: Database
|
||||
|
||||
override suspend fun init() {
|
||||
database = Database.connect(dataSource)
|
||||
}
|
||||
|
||||
override suspend fun shutdown() {
|
||||
if (dataSource is HikariDataSource) {
|
||||
dataSource.close()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun ProducerScope<Pair<Parcel, ParcelData?>>.produceParcelData(parcels: Sequence<Parcel>) {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun readParcelData(plotFor: Parcel): ParcelData? {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun getOwnedParcels(user: ParcelOwner): List<SerializableParcel> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun setParcelOwner(plotFor: Parcel, owner: ParcelOwner?) {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun setParcelPlayerState(plotFor: Parcel, player: UUID, state: Boolean?) {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun setParcelAllowsInteractInventory(plot: Parcel, value: Boolean) {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun setParcelAllowsInteractInputs(plot: Parcel, value: Boolean) {
|
||||
TODO()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
51
src/main/kotlin/io/dico/parcels2/storage/Hikari.kt
Normal file
51
src/main/kotlin/io/dico/parcels2/storage/Hikari.kt
Normal file
@@ -0,0 +1,51 @@
|
||||
package io.dico.parcels2.storage
|
||||
|
||||
import com.zaxxer.hikari.HikariConfig
|
||||
import com.zaxxer.hikari.HikariDataSource
|
||||
import io.dico.parcels2.DataConnectionOptions
|
||||
import javax.sql.DataSource
|
||||
|
||||
fun getHikariDataSource(dialectName: String,
|
||||
driver: String,
|
||||
dco: DataConnectionOptions): DataSource = with(HikariConfig()) {
|
||||
|
||||
val (address, port) = dco.splitAddressAndPort() ?: throw IllegalArgumentException("Invalid address: ${dco.address}")
|
||||
|
||||
poolName = "redstonerplots"
|
||||
maximumPoolSize = dco.poolSize
|
||||
dataSourceClassName = driver
|
||||
username = dco.username
|
||||
password = dco.password
|
||||
connectionTimeout = 15000
|
||||
leakDetectionThreshold = 10000
|
||||
connectionTestQuery = "SELECT 1"
|
||||
|
||||
addDataSourceProperty("serverName", address)
|
||||
addDataSourceProperty("port", port.toString())
|
||||
addDataSourceProperty("databaseName", dco.database)
|
||||
|
||||
// copied from github.com/lucko/LuckPerms
|
||||
if (dialectName.toLowerCase() == "mariadb") {
|
||||
addDataSourceProperty("properties", "useUnicode=true;characterEncoding=utf8")
|
||||
} else {
|
||||
// doesn't exist on the MariaDB driver
|
||||
addDataSourceProperty("cachePrepStmts", "true")
|
||||
addDataSourceProperty("alwaysSendSetIsolation", "false")
|
||||
addDataSourceProperty("cacheServerConfiguration", "true")
|
||||
addDataSourceProperty("elideSetAutoCommits", "true")
|
||||
addDataSourceProperty("useLocalSessionState", "true")
|
||||
|
||||
// already set as default on mariadb
|
||||
addDataSourceProperty("useServerPrepStmts", "true")
|
||||
addDataSourceProperty("prepStmtCacheSize", "250")
|
||||
addDataSourceProperty("prepStmtCacheSqlLimit", "2048")
|
||||
addDataSourceProperty("cacheCallableStmts", "true")
|
||||
|
||||
// make sure unicode characters can be used.
|
||||
addDataSourceProperty("characterEncoding", "utf8")
|
||||
addDataSourceProperty("useUnicode", "true")
|
||||
}
|
||||
|
||||
HikariDataSource(this)
|
||||
|
||||
}
|
||||
95
src/main/kotlin/io/dico/parcels2/storage/Jackson.kt
Normal file
95
src/main/kotlin/io/dico/parcels2/storage/Jackson.kt
Normal file
@@ -0,0 +1,95 @@
|
||||
package io.dico.parcels2.storage
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator
|
||||
import com.fasterxml.jackson.core.JsonParser
|
||||
import com.fasterxml.jackson.databind.*
|
||||
import com.fasterxml.jackson.databind.deser.std.StdDeserializer
|
||||
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
|
||||
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
||||
import io.dico.parcels2.*
|
||||
import org.bukkit.Bukkit
|
||||
import org.bukkit.block.data.BlockData
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.full.isSuperclassOf
|
||||
|
||||
val yamlObjectMapper = ObjectMapper(YAMLFactory()).apply {
|
||||
propertyNamingStrategy = PropertyNamingStrategy.KEBAB_CASE
|
||||
|
||||
val kotlinModule = KotlinModule()
|
||||
|
||||
with(kotlinModule) {
|
||||
setSerializerModifier(object : BeanSerializerModifier() {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun modifySerializer(config: SerializationConfig?, beanDesc: BeanDescription?, serializer: JsonSerializer<*>?): JsonSerializer<*> {
|
||||
if (GeneratorOptions::class.isSuperclassOf(beanDesc?.beanClass?.kotlin as KClass<*>)) {
|
||||
return GeneratorOptionsSerializer(serializer as JsonSerializer<GeneratorOptions>)
|
||||
}
|
||||
|
||||
return super.modifySerializer(config, beanDesc, serializer)
|
||||
}
|
||||
})
|
||||
|
||||
addSerializer(BlockDataSerializer())
|
||||
addDeserializer(BlockData::class.java, BlockDataDeserializer())
|
||||
|
||||
/*
|
||||
addSerializer(StorageOptionsSerializer())
|
||||
addDeserializer(StorageOptions::class.java, StorageOptionsDeserializer())
|
||||
*/
|
||||
|
||||
addDeserializer(GeneratorOptions::class.java, GeneratorOptionsDeserializer())
|
||||
}
|
||||
|
||||
registerModule(kotlinModule)
|
||||
}
|
||||
|
||||
private class BlockDataSerializer : StdSerializer<BlockData>(BlockData::class.java) {
|
||||
|
||||
override fun serialize(value: BlockData, gen: JsonGenerator, provider: SerializerProvider) {
|
||||
gen.writeString(value.asString)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class BlockDataDeserializer : StdDeserializer<BlockData>(BlockData::class.java) {
|
||||
|
||||
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): BlockData? {
|
||||
try {
|
||||
return Bukkit.createBlockData(p.valueAsString)
|
||||
} catch (ex: Exception) {
|
||||
throw RuntimeException("Exception occurred at ${p.currentLocation}", ex)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
class StorageOptionsDeserializer : JsonDeserializer<StorageOptions>() {
|
||||
|
||||
override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): StorageOptions {
|
||||
val node = p!!.readValueAsTree<JsonNode>()
|
||||
val dialect = node.get("dialect").asText()
|
||||
val optionsNode = node.get("options")
|
||||
val factory = StorageFactory.getFactory(dialect) ?: throw IllegalStateException("Unknown storage dialect: $dialect")
|
||||
val options = p.codec.treeToValue(optionsNode, factory.optionsClass.java)
|
||||
return StorageOptions(dialect, factory, options)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class StorageOptionsSerializer : StdSerializer<StorageOptions>(StorageOptions::class.java) {
|
||||
|
||||
override fun serialize(value: StorageOptions?, gen: JsonGenerator?, serializers: SerializerProvider?) {
|
||||
with(gen!!) {
|
||||
writeStartObject()
|
||||
writeStringField("dialect", value!!.dialect)
|
||||
writeFieldName("options")
|
||||
writeObject(value.options)
|
||||
writeEndObject()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,28 @@
|
||||
package io.dico.parcels2.storage
|
||||
|
||||
import io.dico.parcels2.Parcel
|
||||
import io.dico.parcels2.ParcelWorld
|
||||
import io.dico.parcels2.math.Vec2i
|
||||
import org.bukkit.Bukkit
|
||||
import org.bukkit.World
|
||||
import java.util.*
|
||||
|
||||
data class SerializableWorld(val name: String? = null,
|
||||
val uid: UUID? = null) {
|
||||
|
||||
init {
|
||||
uid ?: name ?: throw IllegalArgumentException("uuid and/or name must be present")
|
||||
}
|
||||
|
||||
val world: World? by lazy { uid?.let { Bukkit.getWorld(it) } ?: name?.let { Bukkit.getWorld(it) } }
|
||||
val parcelWorld: ParcelWorld? by lazy { TODO() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by storage backing options to encompass the location of a parcel
|
||||
*/
|
||||
data class SerializableParcel(val world: SerializableWorld,
|
||||
val coord: Vec2i) {
|
||||
|
||||
val parcel: Parcel? by lazy { TODO() }
|
||||
}
|
||||
@@ -1,69 +1,65 @@
|
||||
package io.dico.parcels2.storage
|
||||
|
||||
import kotlinx.coroutines.experimental.CoroutineDispatcher
|
||||
import kotlinx.coroutines.experimental.CoroutineScope
|
||||
import kotlinx.coroutines.experimental.CoroutineStart
|
||||
import kotlinx.coroutines.experimental.asCoroutineDispatcher
|
||||
import kotlinx.coroutines.experimental.channels.ProducerJob
|
||||
import io.dico.parcels2.Parcel
|
||||
import io.dico.parcels2.ParcelData
|
||||
import io.dico.parcels2.ParcelOwner
|
||||
import kotlinx.coroutines.experimental.*
|
||||
import kotlinx.coroutines.experimental.channels.ReceiveChannel
|
||||
import kotlinx.coroutines.experimental.channels.produce
|
||||
import java.util.*
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.Executor
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
/*
|
||||
interface Storage {
|
||||
|
||||
val name: String
|
||||
|
||||
val syncDispatcher: CoroutineDispatcher
|
||||
|
||||
val asyncDispatcher: CoroutineDispatcher
|
||||
|
||||
fun init(): CompletableFuture<Unit>
|
||||
fun init(): Deferred<Unit>
|
||||
|
||||
fun shutdown(): CompletableFuture<Unit>
|
||||
fun shutdown(): Deferred<Unit>
|
||||
|
||||
fun readPlotData(plotFor: Plot): CompletableFuture<PlotData?>
|
||||
fun readParcelData(parcelFor: Parcel): Deferred<ParcelData?>
|
||||
|
||||
fun readPlotData(plotsFor: Sequence<Plot>, channelCapacity: Int): ProducerJob<Pair<Plot, PlotData?>>
|
||||
fun readParcelData(parcelsFor: Sequence<Parcel>, channelCapacity: Int): ReceiveChannel<Pair<Parcel, ParcelData?>>
|
||||
|
||||
fun getOwnedPlots(user: PlotOwner): CompletableFuture<List<SerializablePlot>>
|
||||
fun getOwnedParcels(user: ParcelOwner): Deferred<List<SerializableParcel>>
|
||||
|
||||
fun setPlotOwner(plotFor: Plot, owner: PlotOwner?): CompletableFuture<Unit>
|
||||
fun setParcelOwner(parcelFor: Parcel, owner: ParcelOwner?): Deferred<Unit>
|
||||
|
||||
fun setPlotPlayerState(plotFor: Plot, player: UUID, state: Boolean?): CompletableFuture<Unit>
|
||||
fun setParcelPlayerState(parcelFor: Parcel, player: UUID, state: Boolean?): Deferred<Unit>
|
||||
|
||||
fun setPlotAllowsInteractInventory(plot: Plot, value: Boolean): CompletableFuture<Unit>
|
||||
fun setParcelAllowsInteractInventory(parcel: Parcel, value: Boolean): Deferred<Unit>
|
||||
|
||||
fun setPlotAllowsInteractInputs(plot: Plot, value: Boolean): CompletableFuture<Unit>
|
||||
fun setParcelAllowsInteractInputs(parcel: Parcel, value: Boolean): Deferred<Unit>
|
||||
|
||||
}
|
||||
|
||||
class StorageWithBacking internal constructor(val backing: Backing) : Storage {
|
||||
class StorageWithCoroutineBacking internal constructor(val backing: Backing) : Storage {
|
||||
override val name get() = backing.name
|
||||
override val syncDispatcher = Executor { it.run() }.asCoroutineDispatcher()
|
||||
override val asyncDispatcher = Executors.newFixedThreadPool(4) { Thread(it, "AbstractStorageThread") }.asCoroutineDispatcher()
|
||||
val poolSize: Int get() = 4
|
||||
override val asyncDispatcher = Executors.newFixedThreadPool(poolSize) { Thread(it, "Parcels2_StorageThread") }.asCoroutineDispatcher()
|
||||
|
||||
private fun <T> future(block: suspend CoroutineScope.() -> T) = kotlinx.coroutines.experimental.future.future(asyncDispatcher, CoroutineStart.ATOMIC, block)
|
||||
private fun <T> future(block: suspend CoroutineScope.() -> T) = async(context = asyncDispatcher, start = CoroutineStart.ATOMIC, block = block)
|
||||
|
||||
override fun init(): CompletableFuture<Unit> = future { backing.init() }
|
||||
override fun init() = future { backing.init() }
|
||||
|
||||
override fun shutdown(): CompletableFuture<Unit> = future { backing.shutdown() }
|
||||
override fun shutdown() = future { backing.shutdown() }
|
||||
|
||||
override fun readPlotData(plotFor: Plot) = future { backing.readPlotData(plotFor) }
|
||||
override fun readParcelData(parcelFor: Parcel) = future { backing.readParcelData(parcelFor) }
|
||||
|
||||
override fun readPlotData(plotsFor: Sequence<Plot>, channelCapacity: Int) =
|
||||
produce<Pair<Plot, PlotData?>>(asyncDispatcher, capacity = channelCapacity) { backing.producePlotData(this, plotsFor) }
|
||||
override fun readParcelData(parcelsFor: Sequence<Parcel>, channelCapacity: Int) = produce(asyncDispatcher, capacity = channelCapacity) {
|
||||
with(backing) { produceParcelData(parcelsFor) }
|
||||
}
|
||||
|
||||
override fun getOwnedPlots(user: PlotOwner) = future { backing.getOwnedPlots(user) }
|
||||
override fun getOwnedParcels(user: ParcelOwner) = future { backing.getOwnedParcels(user) }
|
||||
|
||||
override fun setPlotOwner(plotFor: Plot, owner: PlotOwner?) = future { backing.setPlotOwner(plotFor, owner) }
|
||||
override fun setParcelOwner(parcelFor: Parcel, owner: ParcelOwner?) = future { backing.setParcelOwner(parcelFor, owner) }
|
||||
|
||||
override fun setPlotPlayerState(plotFor: Plot, player: UUID, state: Boolean?) = future { backing.setPlotPlayerState(plotFor, player, state) }
|
||||
override fun setParcelPlayerState(parcelFor: Parcel, player: UUID, state: Boolean?) = future { backing.setParcelPlayerState(parcelFor, player, state) }
|
||||
|
||||
override fun setPlotAllowsInteractInventory(plot: Plot, value: Boolean) = future { backing.setPlotAllowsInteractInventory(plot, value) }
|
||||
override fun setParcelAllowsInteractInventory(parcel: Parcel, value: Boolean) = future { backing.setParcelAllowsInteractInventory(parcel, value) }
|
||||
|
||||
override fun setPlotAllowsInteractInputs(plot: Plot, value: Boolean) = future { backing.setPlotAllowsInteractInputs(plot, value) }
|
||||
override fun setParcelAllowsInteractInputs(parcel: Parcel, value: Boolean) = future { backing.setParcelAllowsInteractInputs(parcel, value) }
|
||||
}
|
||||
*/
|
||||
46
src/main/kotlin/io/dico/parcels2/storage/StorageFactory.kt
Normal file
46
src/main/kotlin/io/dico/parcels2/storage/StorageFactory.kt
Normal file
@@ -0,0 +1,46 @@
|
||||
package io.dico.parcels2.storage
|
||||
|
||||
import io.dico.parcels2.DataConnectionOptions
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
interface StorageFactory {
|
||||
companion object StorageFactories {
|
||||
private val map: MutableMap<String, StorageFactory> = HashMap()
|
||||
|
||||
fun registerFactory(method: String, generator: StorageFactory): Boolean = map.putIfAbsent(method.toLowerCase(), generator) == null
|
||||
|
||||
fun getFactory(method: String): StorageFactory? = map[method.toLowerCase()]
|
||||
|
||||
init {
|
||||
// have to write the code like this in kotlin.
|
||||
// This code is absolutely disgusting
|
||||
ConnectionStorageFactory().register(this)
|
||||
}
|
||||
}
|
||||
|
||||
val optionsClass: KClass<out Any>
|
||||
|
||||
fun newStorageInstance(method: String, options: Any): Storage
|
||||
|
||||
}
|
||||
|
||||
class ConnectionStorageFactory : StorageFactory {
|
||||
override val optionsClass = DataConnectionOptions::class
|
||||
|
||||
private val types: Map<String, String> = with(HashMap<String, String>()) {
|
||||
put("mysql", "com.mysql.jdbc.jdbc2.optional.MysqlDataSource")
|
||||
this
|
||||
}
|
||||
|
||||
fun register(companion: StorageFactory.StorageFactories) {
|
||||
types.keys.forEach {
|
||||
companion.registerFactory(it, this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun newStorageInstance(dialect: String, options: Any): Storage {
|
||||
val driverClass = types[dialect.toLowerCase()] ?: throw IllegalArgumentException("Storage dialect $dialect is not supported")
|
||||
return StorageWithCoroutineBacking(ExposedBacking(getHikariDataSource(dialect, driverClass, options as DataConnectionOptions)))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package io.dico.parcels2.storage.backing
|
||||
|
||||
import org.jetbrains.exposed.sql.ReferenceOption
|
||||
import org.jetbrains.exposed.sql.Table
|
||||
|
||||
object ParcelsTable : Table() {
|
||||
val id = integer("id").autoIncrement().primaryKey()
|
||||
val px = integer("px")
|
||||
val pz = integer("pz")
|
||||
val world_uuid = binary("world_uuid", 16).also { uniqueIndex("location", it, px, pz) }
|
||||
val world = varchar("world", 32).nullable()
|
||||
val owner_uuid = binary("owner_uuid", 16).nullable()
|
||||
val owner = varchar("owner", 16).nullable()
|
||||
}
|
||||
|
||||
object ParcelsAddedTable : Table() {
|
||||
val id = integer("id").references(ParcelsTable.id, ReferenceOption.CASCADE)
|
||||
val player_uuid = binary("player_uuid", 16).also { uniqueIndex("pair", id, it) }
|
||||
val allowed_flag = bool("allowed_flag")
|
||||
}
|
||||
|
||||
object PlayerAddedTable : Table() {
|
||||
val owner_uuid = binary("owner_uuid", 16)
|
||||
val player_uuid = binary("player_uuid", 16).also { uniqueIndex("pair", owner_uuid, it) }
|
||||
val allowed_flag = bool("allowed_flag")
|
||||
}
|
||||
|
||||
class AbstractParcelsDatabase {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
package io.dico.parcels2.util
|
||||
|
||||
import io.dico.dicore.Formatting
|
||||
import io.dico.parcels2.ParcelsPlugin
|
||||
import io.dico.parcels2.logger
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.plugin.java.JavaPlugin
|
||||
|
||||
val Player.hasBanBypass get() = hasPermission("plots.admin.bypass.ban")
|
||||
val Player.hasBuildAnywhere get() = hasPermission("plots.admin.bypass.build")
|
||||
val Player.hasGamemodeBypass get() = hasPermission("plots.admin.bypass.gamemode")
|
||||
val Player.hasAdminManage get() = hasPermission("plots.admin.manage")
|
||||
val Player.hasPlotHomeOthers get() = hasPermission("plots.command.home.others")
|
||||
val Player.hasRandomSpecific get() = hasPermission("plots.command.random.specific")
|
||||
inline val Player.hasBanBypass get() = hasPermission("plots.admin.bypass.ban")
|
||||
inline val Player.hasBuildAnywhere get() = hasPermission("plots.admin.bypass.build")
|
||||
inline val Player.hasGamemodeBypass get() = hasPermission("plots.admin.bypass.gamemode")
|
||||
inline val Player.hasAdminManage get() = hasPermission("plots.admin.manage")
|
||||
inline val Player.hasPlotHomeOthers get() = hasPermission("plots.command.home.others")
|
||||
inline val Player.hasRandomSpecific get() = hasPermission("plots.command.random.specific")
|
||||
val Player.plotLimit: Int
|
||||
get() {
|
||||
for (info in effectivePermissions) {
|
||||
@@ -18,15 +22,15 @@ val Player.plotLimit: Int
|
||||
return Int.MAX_VALUE
|
||||
}
|
||||
return limitString.toIntOrNull() ?: DEFAULT_LIMIT.also {
|
||||
Main.instance.logger.severe("$name has permission '$perm'. The suffix can not be parsed to an integer (or *).")
|
||||
logger.warn("$name has permission '$perm'. The suffix can not be parsed to an integer (or *).")
|
||||
}
|
||||
}
|
||||
}
|
||||
return DEFAULT_LIMIT
|
||||
}
|
||||
|
||||
val DEFAULT_LIMIT = 1
|
||||
internal val prefix = Formatting.translateChars('&', "&4[&c${Main.instance.name}&4] &a")
|
||||
private const val DEFAULT_LIMIT = 1
|
||||
private val prefix = Formatting.translateChars('&', "&4[&c${JavaPlugin.getPlugin(ParcelsPlugin::class.java).name}&4] &a")
|
||||
|
||||
fun Player.sendPlotMessage(except: Boolean = false, nopermit: Boolean = false, message: String) {
|
||||
if (except) {
|
||||
|
||||
Reference in New Issue
Block a user