Improve database abstractions, add GlobalAddedData, some other things
This commit is contained in:
@@ -51,7 +51,7 @@ project(":dicore3:dicore3-command") {
|
|||||||
dependencies {
|
dependencies {
|
||||||
c.kotlinStd(kotlin("stdlib-jdk8"))
|
c.kotlinStd(kotlin("stdlib-jdk8"))
|
||||||
c.kotlinStd(kotlin("reflect"))
|
c.kotlinStd(kotlin("reflect"))
|
||||||
c.kotlinStd(kotlinx("coroutines-core:0.23.4"))
|
c.kotlinStd(kotlinx("coroutines-core:0.24.0"))
|
||||||
|
|
||||||
compile(project(":dicore3:dicore3-core"))
|
compile(project(":dicore3:dicore3-core"))
|
||||||
compile("com.thoughtworks.paranamer:paranamer:2.8")
|
compile("com.thoughtworks.paranamer:paranamer:2.8")
|
||||||
@@ -86,6 +86,8 @@ tasks {
|
|||||||
val compileKotlin by getting(KotlinCompile::class) {
|
val compileKotlin by getting(KotlinCompile::class) {
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
javaParameters = true
|
javaParameters = true
|
||||||
|
suppressWarnings = true
|
||||||
|
//freeCompilerArgs = listOf("-XXLanguage:+InlineClasses", "-Xuse-experimental=kotlin.Experimental")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
47
src/main/kotlin/io/dico/parcels2/AddedData.kt
Normal file
47
src/main/kotlin/io/dico/parcels2/AddedData.kt
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package io.dico.parcels2
|
||||||
|
|
||||||
|
import io.dico.parcels2.util.uuid
|
||||||
|
import org.bukkit.OfflinePlayer
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
interface AddedData {
|
||||||
|
val added: Map<UUID, AddedStatus>
|
||||||
|
|
||||||
|
fun getAddedStatus(uuid: UUID): AddedStatus
|
||||||
|
fun setAddedStatus(uuid: UUID, status: AddedStatus): Boolean
|
||||||
|
|
||||||
|
fun compareAndSetAddedStatus(uuid: UUID, expect: AddedStatus, status: AddedStatus): Boolean =
|
||||||
|
(getAddedStatus(uuid) == expect).also { if (it) setAddedStatus(uuid, status) }
|
||||||
|
|
||||||
|
fun isAllowed(uuid: UUID) = getAddedStatus(uuid) == AddedStatus.ALLOWED
|
||||||
|
fun allow(uuid: UUID) = setAddedStatus(uuid, AddedStatus.ALLOWED)
|
||||||
|
fun disallow(uuid: UUID) = compareAndSetAddedStatus(uuid, AddedStatus.ALLOWED, AddedStatus.DEFAULT)
|
||||||
|
fun isBanned(uuid: UUID) = getAddedStatus(uuid) == AddedStatus.BANNED
|
||||||
|
fun ban(uuid: UUID) = setAddedStatus(uuid, AddedStatus.BANNED)
|
||||||
|
fun unban(uuid: UUID) = compareAndSetAddedStatus(uuid, AddedStatus.BANNED, AddedStatus.DEFAULT)
|
||||||
|
|
||||||
|
fun isAllowed(player: OfflinePlayer) = isAllowed(player.uuid)
|
||||||
|
fun allow(player: OfflinePlayer) = allow(player.uuid)
|
||||||
|
fun disallow(player: OfflinePlayer) = disallow(player.uuid)
|
||||||
|
fun isBanned(player: OfflinePlayer) = isBanned(player.uuid)
|
||||||
|
fun ban(player: OfflinePlayer) = ban(player.uuid)
|
||||||
|
fun unban(player: OfflinePlayer) = unban(player.uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
open class AddedDataHolder(override var added: MutableMap<UUID, AddedStatus>
|
||||||
|
= mutableMapOf<UUID, AddedStatus>()) : AddedData {
|
||||||
|
override fun getAddedStatus(uuid: UUID): AddedStatus = added.getOrDefault(uuid, AddedStatus.DEFAULT)
|
||||||
|
override fun setAddedStatus(uuid: UUID, status: AddedStatus): Boolean = status.takeIf { it != AddedStatus.DEFAULT }
|
||||||
|
?.let { added.put(uuid, it) != it }
|
||||||
|
?: added.remove(uuid) != null
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class AddedStatus {
|
||||||
|
DEFAULT,
|
||||||
|
ALLOWED,
|
||||||
|
BANNED;
|
||||||
|
|
||||||
|
val isDefault get() = this == DEFAULT
|
||||||
|
val isAllowed get() = this == ALLOWED
|
||||||
|
val isBanned get() = this == BANNED
|
||||||
|
}
|
||||||
@@ -1,38 +1,25 @@
|
|||||||
|
@file:Suppress("UNCHECKED_CAST")
|
||||||
|
|
||||||
package io.dico.parcels2
|
package io.dico.parcels2
|
||||||
|
|
||||||
import io.dico.parcels2.util.uuid
|
|
||||||
import kotlinx.coroutines.experimental.CompletableDeferred
|
|
||||||
import kotlinx.coroutines.experimental.Deferred
|
|
||||||
import org.bukkit.OfflinePlayer
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
interface GlobalAddedData : AddedData {
|
interface GlobalAddedData : AddedData {
|
||||||
val uuid: UUID
|
val owner: ParcelOwner
|
||||||
}
|
}
|
||||||
|
|
||||||
class GlobalAddedDataManager(val plugin: ParcelsPlugin) {
|
interface GlobalAddedDataManager {
|
||||||
private val map = mutableMapOf<UUID, GlobalAddedData?>()
|
operator fun get(owner: ParcelOwner): GlobalAddedData
|
||||||
|
}
|
||||||
|
|
||||||
operator fun get(player: OfflinePlayer) = get(player.uuid)
|
class GlobalAddedDataManagerImpl(val plugin: ParcelsPlugin) : GlobalAddedDataManager {
|
||||||
|
private val map = mutableMapOf<ParcelOwner, GlobalAddedData>()
|
||||||
operator fun get(uuid: UUID): GlobalAddedData? {
|
|
||||||
|
|
||||||
|
override fun get(owner: ParcelOwner): GlobalAddedData {
|
||||||
|
return map[owner] ?: GlobalAddedDataImpl(owner).also { map[owner] = it }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getDeferred(uuid: UUID): Deferred<AddedData> {
|
private inner class GlobalAddedDataImpl(override val owner: ParcelOwner,
|
||||||
get(uuid)?.let { return CompletableDeferred(it) }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun getAsync(uuid: UUID): GlobalAddedData {
|
|
||||||
val data = plugin.storage.readGlobalAddedData(ParcelOwner(uuid = uuid)).await()
|
|
||||||
?: return GlobalAddedDataImpl(uuid)
|
|
||||||
val result = GlobalAddedDataImpl(uuid, data)
|
|
||||||
map[uuid] = result
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class GlobalAddedDataImpl(override val uuid: UUID,
|
|
||||||
data: MutableMap<UUID, AddedStatus> = emptyData)
|
data: MutableMap<UUID, AddedStatus> = emptyData)
|
||||||
: AddedDataHolder(data), GlobalAddedData {
|
: AddedDataHolder(data), GlobalAddedData {
|
||||||
|
|
||||||
@@ -45,7 +32,7 @@ class GlobalAddedDataManager(val plugin: ParcelsPlugin) {
|
|||||||
data = mutableMapOf()
|
data = mutableMapOf()
|
||||||
}
|
}
|
||||||
return super.setAddedStatus(uuid, status).also {
|
return super.setAddedStatus(uuid, status).also {
|
||||||
if (it) plugin.storage.setGlobalAddedStatus(ParcelOwner(uuid = this.uuid), uuid, status)
|
if (it) plugin.storage.setGlobalAddedStatus(owner, uuid, status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,5 +46,3 @@ class GlobalAddedDataManager(val plugin: ParcelsPlugin) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,42 +1,16 @@
|
|||||||
package io.dico.parcels2
|
package io.dico.parcels2
|
||||||
|
|
||||||
import io.dico.parcels2.util.Vec2i
|
import io.dico.parcels2.util.Vec2i
|
||||||
import io.dico.parcels2.util.getPlayerName
|
|
||||||
import io.dico.parcels2.util.hasBuildAnywhere
|
import io.dico.parcels2.util.hasBuildAnywhere
|
||||||
import io.dico.parcels2.util.isValid
|
|
||||||
import io.dico.parcels2.util.uuid
|
|
||||||
import org.bukkit.Bukkit
|
import org.bukkit.Bukkit
|
||||||
import org.bukkit.OfflinePlayer
|
import org.bukkit.OfflinePlayer
|
||||||
import org.bukkit.entity.Player
|
import org.bukkit.entity.Player
|
||||||
import org.joda.time.DateTime
|
import org.joda.time.DateTime
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
interface AddedData {
|
|
||||||
val added: Map<UUID, AddedStatus>
|
|
||||||
|
|
||||||
fun getAddedStatus(uuid: UUID): AddedStatus
|
|
||||||
fun setAddedStatus(uuid: UUID, status: AddedStatus): Boolean
|
|
||||||
|
|
||||||
fun compareAndSetAddedStatus(uuid: UUID, expect: AddedStatus, status: AddedStatus): Boolean =
|
|
||||||
(getAddedStatus(uuid) == expect).also { if (it) setAddedStatus(uuid, status) }
|
|
||||||
|
|
||||||
fun isAllowed(uuid: UUID) = getAddedStatus(uuid) == AddedStatus.ALLOWED
|
|
||||||
fun allow(uuid: UUID) = setAddedStatus(uuid, AddedStatus.ALLOWED)
|
|
||||||
fun disallow(uuid: UUID) = compareAndSetAddedStatus(uuid, AddedStatus.ALLOWED, AddedStatus.DEFAULT)
|
|
||||||
fun isBanned(uuid: UUID) = getAddedStatus(uuid) == AddedStatus.BANNED
|
|
||||||
fun ban(uuid: UUID) = setAddedStatus(uuid, AddedStatus.BANNED)
|
|
||||||
fun unban(uuid: UUID) = compareAndSetAddedStatus(uuid, AddedStatus.BANNED, AddedStatus.DEFAULT)
|
|
||||||
|
|
||||||
fun isAllowed(player: OfflinePlayer) = isAllowed(player.uuid)
|
|
||||||
fun allow(player: OfflinePlayer) = allow(player.uuid)
|
|
||||||
fun disallow(player: OfflinePlayer) = disallow(player.uuid)
|
|
||||||
fun isBanned(player: OfflinePlayer) = isBanned(player.uuid)
|
|
||||||
fun ban(player: OfflinePlayer) = ban(player.uuid)
|
|
||||||
fun unban(player: OfflinePlayer) = unban(player.uuid)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ParcelData : AddedData {
|
interface ParcelData : AddedData {
|
||||||
var owner: ParcelOwner?
|
var owner: ParcelOwner?
|
||||||
|
val since: DateTime?
|
||||||
|
|
||||||
fun canBuild(player: OfflinePlayer, checkAdmin: Boolean = true, checkGlobal: Boolean = true): Boolean
|
fun canBuild(player: OfflinePlayer, checkAdmin: Boolean = true, checkGlobal: Boolean = true): Boolean
|
||||||
|
|
||||||
@@ -83,6 +57,8 @@ class Parcel(val world: ParcelWorld, val pos: Vec2i) : ParcelData {
|
|||||||
override fun isAllowed(uuid: UUID) = data.isAllowed(uuid)
|
override fun isAllowed(uuid: UUID) = data.isAllowed(uuid)
|
||||||
override fun canBuild(player: OfflinePlayer, checkAdmin: Boolean, checkGlobal: Boolean) = data.canBuild(player)
|
override fun canBuild(player: OfflinePlayer, checkAdmin: Boolean, checkGlobal: Boolean) = data.canBuild(player)
|
||||||
|
|
||||||
|
override val since: DateTime? get() = data.since
|
||||||
|
|
||||||
override var owner: ParcelOwner?
|
override var owner: ParcelOwner?
|
||||||
get() = data.owner
|
get() = data.owner
|
||||||
set(value) {
|
set(value) {
|
||||||
@@ -94,7 +70,7 @@ class Parcel(val world: ParcelWorld, val pos: Vec2i) : ParcelData {
|
|||||||
|
|
||||||
override fun setAddedStatus(uuid: UUID, status: AddedStatus): Boolean {
|
override fun setAddedStatus(uuid: UUID, status: AddedStatus): Boolean {
|
||||||
return data.setAddedStatus(uuid, status).also {
|
return data.setAddedStatus(uuid, status).also {
|
||||||
if (it) world.storage.setParcelPlayerState(this, uuid, status.asBoolean)
|
if (it) world.storage.setParcelPlayerStatus(this, uuid, status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,16 +93,9 @@ class Parcel(val world: ParcelWorld, val pos: Vec2i) : ParcelData {
|
|||||||
var hasBlockVisitors: Boolean = false; private set
|
var hasBlockVisitors: Boolean = false; private set
|
||||||
}
|
}
|
||||||
|
|
||||||
open class AddedDataHolder(override var added: MutableMap<UUID, AddedStatus>
|
|
||||||
= mutableMapOf<UUID, AddedStatus>()) : AddedData {
|
|
||||||
override fun getAddedStatus(uuid: UUID): AddedStatus = added.getOrDefault(uuid, AddedStatus.DEFAULT)
|
|
||||||
override fun setAddedStatus(uuid: UUID, status: AddedStatus): Boolean = status.takeIf { it != AddedStatus.DEFAULT }
|
|
||||||
?.let { added.put(uuid, it) != it }
|
|
||||||
?: added.remove(uuid) != null
|
|
||||||
}
|
|
||||||
|
|
||||||
class ParcelDataHolder : AddedDataHolder(), ParcelData {
|
class ParcelDataHolder : AddedDataHolder(), ParcelData {
|
||||||
override var owner: ParcelOwner? = null
|
override var owner: ParcelOwner? = null
|
||||||
|
override var since: DateTime? = null
|
||||||
override fun canBuild(player: OfflinePlayer, checkAdmin: Boolean, checkGlobal: Boolean) = isAllowed(player.uniqueId)
|
override fun canBuild(player: OfflinePlayer, checkAdmin: Boolean, checkGlobal: Boolean) = isAllowed(player.uniqueId)
|
||||||
|| owner.let { it != null && it.matches(player, allowNameMatch = false) }
|
|| owner.let { it != null && it.matches(player, allowNameMatch = false) }
|
||||||
|| (checkAdmin && player is Player && player.hasBuildAnywhere)
|
|| (checkAdmin && player is Player && player.hasBuildAnywhere)
|
||||||
@@ -135,55 +104,3 @@ class ParcelDataHolder : AddedDataHolder(), ParcelData {
|
|||||||
override var allowInteractInventory = true
|
override var allowInteractInventory = true
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class AddedStatus {
|
|
||||||
DEFAULT,
|
|
||||||
ALLOWED,
|
|
||||||
BANNED;
|
|
||||||
|
|
||||||
val asBoolean
|
|
||||||
get() = when (this) {
|
|
||||||
DEFAULT -> null
|
|
||||||
ALLOWED -> true
|
|
||||||
BANNED -> false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("UsePropertyAccessSyntax")
|
|
||||||
class ParcelOwner(val uuid: UUID? = null,
|
|
||||||
name: String? = null,
|
|
||||||
val since: DateTime? = null) {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun create(uuid: UUID?, name: String?, time: DateTime? = null): ParcelOwner? {
|
|
||||||
return uuid?.let { ParcelOwner(uuid, name, time) }
|
|
||||||
?: name?.let { ParcelOwner(uuid, name, time) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val name: String?
|
|
||||||
|
|
||||||
init {
|
|
||||||
uuid ?: name ?: throw IllegalArgumentException("uuid and/or name must be present")
|
|
||||||
|
|
||||||
if (name != null) this.name = name
|
|
||||||
else {
|
|
||||||
val offlinePlayer = Bukkit.getOfflinePlayer(uuid).takeIf { it.isValid }
|
|
||||||
this.name = offlinePlayer?.name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val playerName get() = getPlayerName(uuid, name)
|
|
||||||
|
|
||||||
fun matches(player: OfflinePlayer, allowNameMatch: Boolean = false): Boolean {
|
|
||||||
return uuid?.let { it == player.uniqueId } ?: false
|
|
||||||
|| (allowNameMatch && name?.let { it == player.name } ?: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
val onlinePlayer: Player? get() = uuid?.let { Bukkit.getPlayer(uuid) }
|
|
||||||
val onlinePlayerAllowingNameMatch: Player? get() = onlinePlayer ?: name?.let { Bukkit.getPlayer(name) }
|
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
val offlinePlayer
|
|
||||||
get() = (uuid?.let { Bukkit.getOfflinePlayer(it) } ?: Bukkit.getOfflinePlayer(name))
|
|
||||||
?.takeIf { it.isValid }
|
|
||||||
}
|
|
||||||
|
|||||||
53
src/main/kotlin/io/dico/parcels2/ParcelOwner.kt
Normal file
53
src/main/kotlin/io/dico/parcels2/ParcelOwner.kt
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package io.dico.parcels2
|
||||||
|
|
||||||
|
import io.dico.parcels2.util.getPlayerNameOrDefault
|
||||||
|
import io.dico.parcels2.util.isValid
|
||||||
|
import io.dico.parcels2.util.uuid
|
||||||
|
import org.bukkit.Bukkit
|
||||||
|
import org.bukkit.OfflinePlayer
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
@Suppress("UsePropertyAccessSyntax")
|
||||||
|
class ParcelOwner private constructor(val uuid: UUID?,
|
||||||
|
name: String?) {
|
||||||
|
var name: String? = name
|
||||||
|
get() = field ?: getPlayerNameOrDefault(uuid!!).also { field = it }
|
||||||
|
private set
|
||||||
|
|
||||||
|
constructor(name: String) : this(null, name)
|
||||||
|
constructor(uuid: UUID) : this(uuid, null)
|
||||||
|
constructor(player: OfflinePlayer) : this(player.uuid, player.name)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun nameless(player: OfflinePlayer) = ParcelOwner(player.uuid, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline val hasUUID: Boolean get() = uuid != null
|
||||||
|
val onlinePlayer: Player? get() = uuid?.let { Bukkit.getPlayer(uuid) }
|
||||||
|
val onlinePlayerAllowingNameMatch: Player? get() = onlinePlayer ?: name?.let { Bukkit.getPlayer(name) }
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
val offlinePlayer
|
||||||
|
get() = (uuid?.let { Bukkit.getOfflinePlayer(it) } ?: Bukkit.getOfflinePlayer(name))
|
||||||
|
?.takeIf { it.isValid }
|
||||||
|
|
||||||
|
fun matches(player: OfflinePlayer, allowNameMatch: Boolean = false): Boolean {
|
||||||
|
return uuid?.let { it == player.uniqueId } ?: false
|
||||||
|
|| (allowNameMatch && name?.let { it == player.name } ?: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun equals(other: ParcelOwner): Boolean {
|
||||||
|
return if (hasUUID) other.hasUUID && uuid == other.uuid
|
||||||
|
else !other.hasUUID && name == other.name
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return other is ParcelOwner && equals(other)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return if (hasUUID) uuid!!.hashCode() else name!!.hashCode()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -219,7 +219,7 @@ class DefaultParcelContainer(private val world: ParcelWorld,
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun loadAllData() {
|
fun loadAllData() {
|
||||||
val channel = storage.readParcelData(allParcels(), 100)
|
val channel = storage.readParcelData(allParcels())
|
||||||
launch(storage.asyncDispatcher) {
|
launch(storage.asyncDispatcher) {
|
||||||
for ((parcel, data) in channel) {
|
for ((parcel, data) in channel) {
|
||||||
data?.let { parcel.copyDataIgnoringDatabase(it) }
|
data?.let { parcel.copyDataIgnoringDatabase(it) }
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ import io.dico.parcels2.listener.ParcelEntityTracker
|
|||||||
import io.dico.parcels2.listener.ParcelListeners
|
import io.dico.parcels2.listener.ParcelListeners
|
||||||
import io.dico.parcels2.storage.Storage
|
import io.dico.parcels2.storage.Storage
|
||||||
import io.dico.parcels2.storage.yamlObjectMapper
|
import io.dico.parcels2.storage.yamlObjectMapper
|
||||||
|
import io.dico.parcels2.util.FunctionHelper
|
||||||
import io.dico.parcels2.util.tryCreate
|
import io.dico.parcels2.util.tryCreate
|
||||||
|
import kotlinx.coroutines.experimental.asCoroutineDispatcher
|
||||||
import org.bukkit.Bukkit
|
import org.bukkit.Bukkit
|
||||||
import org.bukkit.plugin.java.JavaPlugin
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
@@ -25,20 +27,15 @@ class ParcelsPlugin : JavaPlugin() {
|
|||||||
lateinit var options: Options; private set
|
lateinit var options: Options; private set
|
||||||
lateinit var worlds: Worlds; private set
|
lateinit var worlds: Worlds; private set
|
||||||
lateinit var storage: Storage; private set
|
lateinit var storage: Storage; private set
|
||||||
|
lateinit var globalAddedData: GlobalAddedDataManager; private set
|
||||||
|
|
||||||
val registrator = Registrator(this)
|
val registrator = Registrator(this)
|
||||||
lateinit var entityTracker: ParcelEntityTracker; private set
|
lateinit var entityTracker: ParcelEntityTracker; private set
|
||||||
private var listeners: ParcelListeners? = null
|
private var listeners: ParcelListeners? = null
|
||||||
private var cmdDispatcher: ICommandDispatcher? = null
|
private var cmdDispatcher: ICommandDispatcher? = null
|
||||||
val worktimeLimiter: WorktimeLimiter by lazy { TickWorktimeLimiter(this, options.tickWorktime) }
|
|
||||||
|
|
||||||
val mainThreadDispatcher = object : Executor {
|
val functionHelper: FunctionHelper = FunctionHelper(this)
|
||||||
private val mainThread = Thread.currentThread()
|
val worktimeLimiter: WorktimeLimiter by lazy { TickWorktimeLimiter(this, options.tickWorktime) }
|
||||||
override fun execute(command: Runnable) {
|
|
||||||
if (Thread.currentThread() === mainThread) command.run()
|
|
||||||
else server.scheduler.runTask(this@ParcelsPlugin, command)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onEnable() {
|
override fun onEnable() {
|
||||||
plogger.info("Debug enabled: ${plogger.isDebugEnabled}")
|
plogger.info("Debug enabled: ${plogger.isDebugEnabled}")
|
||||||
@@ -73,6 +70,7 @@ class ParcelsPlugin : JavaPlugin() {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
globalAddedData = GlobalAddedDataManagerImpl(this)
|
||||||
entityTracker = ParcelEntityTracker(worlds)
|
entityTracker = ParcelEntityTracker(worlds)
|
||||||
registerListeners()
|
registerListeners()
|
||||||
registerCommands()
|
registerCommands()
|
||||||
|
|||||||
@@ -218,7 +218,7 @@ class DefaultParcelGenerator(val worlds: Worlds, val name: String, private val o
|
|||||||
|
|
||||||
val sign = signBlock.state as org.bukkit.block.Sign
|
val sign = signBlock.state as org.bukkit.block.Sign
|
||||||
sign.setLine(0, parcel.id)
|
sign.setLine(0, parcel.id)
|
||||||
sign.setLine(2, owner.playerName)
|
sign.setLine(2, owner.name)
|
||||||
sign.update()
|
sign.update()
|
||||||
|
|
||||||
skullBlock.type = Material.PLAYER_HEAD
|
skullBlock.type = Material.PLAYER_HEAD
|
||||||
|
|||||||
@@ -64,4 +64,4 @@ val attachables: Set<Material> = EnumSet.of(
|
|||||||
WALL_SIGN,
|
WALL_SIGN,
|
||||||
LILY_PAD,
|
LILY_PAD,
|
||||||
DANDELION
|
DANDELION
|
||||||
);
|
)
|
||||||
@@ -6,6 +6,7 @@ import io.dico.parcels2.util.get
|
|||||||
import org.bukkit.World
|
import org.bukkit.World
|
||||||
import org.bukkit.block.data.BlockData
|
import org.bukkit.block.data.BlockData
|
||||||
|
|
||||||
|
// TODO order paste such that attachables are placed after the block they depend on
|
||||||
class Schematic {
|
class Schematic {
|
||||||
val size: Vec3i get() = _size!!
|
val size: Vec3i get() = _size!!
|
||||||
private var _size: Vec3i? = null
|
private var _size: Vec3i? = null
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
package io.dico.parcels2.blockvisitor
|
package io.dico.parcels2.blockvisitor
|
||||||
|
|
||||||
import kotlinx.coroutines.experimental.*
|
import io.dico.parcels2.ParcelsPlugin
|
||||||
import org.bukkit.plugin.Plugin
|
import io.dico.parcels2.util.FunctionHelper
|
||||||
|
import kotlinx.coroutines.experimental.CancellationException
|
||||||
|
import kotlinx.coroutines.experimental.Job
|
||||||
import org.bukkit.scheduler.BukkitTask
|
import org.bukkit.scheduler.BukkitTask
|
||||||
import java.lang.System.currentTimeMillis
|
import java.lang.System.currentTimeMillis
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.Executor
|
|
||||||
import java.util.logging.Level
|
import java.util.logging.Level
|
||||||
import kotlin.coroutines.experimental.Continuation
|
import kotlin.coroutines.experimental.Continuation
|
||||||
import kotlin.coroutines.experimental.intrinsics.COROUTINE_SUSPENDED
|
import kotlin.coroutines.experimental.intrinsics.COROUTINE_SUSPENDED
|
||||||
@@ -101,9 +102,7 @@ private interface WorkerContinuation : Worker, WorkerScope {
|
|||||||
* There is a configurable maxiumum amount of milliseconds that can be allocated to all workers together in each server tick
|
* There is a configurable maxiumum amount of milliseconds that can be allocated to all workers together in each server tick
|
||||||
* This object attempts to split that maximum amount of milliseconds equally between all jobs
|
* This object attempts to split that maximum amount of milliseconds equally between all jobs
|
||||||
*/
|
*/
|
||||||
class TickWorktimeLimiter(private val plugin: Plugin, var options: TickWorktimeOptions) : WorktimeLimiter() {
|
class TickWorktimeLimiter(private val plugin: ParcelsPlugin, var options: TickWorktimeOptions) : WorktimeLimiter() {
|
||||||
// Coroutine dispatcher for jobs
|
|
||||||
private val dispatcher = Executor(Runnable::run).asCoroutineDispatcher()
|
|
||||||
// The currently registered bukkit scheduler task
|
// The currently registered bukkit scheduler task
|
||||||
private var bukkitTask: BukkitTask? = null
|
private var bukkitTask: BukkitTask? = null
|
||||||
// The workers.
|
// The workers.
|
||||||
@@ -111,9 +110,9 @@ class TickWorktimeLimiter(private val plugin: Plugin, var options: TickWorktimeO
|
|||||||
override val workers: List<Worker> = _workers
|
override val workers: List<Worker> = _workers
|
||||||
|
|
||||||
override fun submit(task: TimeLimitedTask): Worker {
|
override fun submit(task: TimeLimitedTask): Worker {
|
||||||
val worker: WorkerContinuation = WorkerImpl(plugin, dispatcher, task)
|
val worker: WorkerContinuation = WorkerImpl(plugin.functionHelper, task)
|
||||||
_workers.addFirst(worker)
|
_workers.addFirst(worker)
|
||||||
if (bukkitTask == null) bukkitTask = plugin.server.scheduler.runTaskTimer(plugin, ::tickJobs, 0, options.tickInterval.toLong())
|
if (bukkitTask == null) bukkitTask = plugin.functionHelper.scheduleRepeating(0, options.tickInterval) { tickJobs() }
|
||||||
return worker
|
return worker
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,8 +145,7 @@ class TickWorktimeLimiter(private val plugin: Plugin, var options: TickWorktimeO
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class WorkerImpl(val plugin: Plugin,
|
private class WorkerImpl(val functionHelper: FunctionHelper,
|
||||||
val dispatcher: CoroutineDispatcher,
|
|
||||||
val task: TimeLimitedTask) : WorkerContinuation {
|
val task: TimeLimitedTask) : WorkerContinuation {
|
||||||
override var job: Job? = null; private set
|
override var job: Job? = null; private set
|
||||||
|
|
||||||
@@ -179,7 +177,7 @@ private class WorkerImpl(val plugin: Plugin,
|
|||||||
// report any error that occurred
|
// report any error that occurred
|
||||||
completionException = exception?.also {
|
completionException = exception?.also {
|
||||||
if (it !is CancellationException)
|
if (it !is CancellationException)
|
||||||
plugin.logger.log(Level.SEVERE, "TimeLimitedTask for plugin ${plugin.name} generated an exception", it)
|
functionHelper.plugin.logger.log(Level.SEVERE, "TimeLimitedTask for plugin ${functionHelper.plugin.name} generated an exception", it)
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert to elapsed time here
|
// convert to elapsed time here
|
||||||
@@ -236,10 +234,9 @@ private class WorkerImpl(val plugin: Plugin,
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
launch(context = dispatcher, start = CoroutineStart.UNDISPATCHED) {
|
val job = functionHelper.launchLazilyOnMainThread { task() }
|
||||||
initJob(job = kotlin.coroutines.experimental.coroutineContext[Job]!!)
|
initJob(job = job)
|
||||||
task()
|
job.start()
|
||||||
}
|
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
// do nothing: handled by job.invokeOnCompletion()
|
// do nothing: handled by job.invokeOnCompletion()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import io.dico.dicore.command.EMessageType
|
|||||||
import io.dico.dicore.command.ExecutionContext
|
import io.dico.dicore.command.ExecutionContext
|
||||||
import io.dico.dicore.command.annotation.Cmd
|
import io.dico.dicore.command.annotation.Cmd
|
||||||
import io.dico.dicore.command.annotation.Desc
|
import io.dico.dicore.command.annotation.Desc
|
||||||
|
import io.dico.dicore.command.annotation.Flag
|
||||||
import io.dico.dicore.command.annotation.RequireParameters
|
import io.dico.dicore.command.annotation.RequireParameters
|
||||||
import io.dico.parcels2.ParcelOwner
|
import io.dico.parcels2.ParcelOwner
|
||||||
import io.dico.parcels2.ParcelsPlugin
|
import io.dico.parcels2.ParcelsPlugin
|
||||||
@@ -84,12 +85,22 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
|
|||||||
|
|
||||||
@Cmd("clear")
|
@Cmd("clear")
|
||||||
@ParcelRequire(owner = true)
|
@ParcelRequire(owner = true)
|
||||||
fun ParcelScope.cmdClear(context: ExecutionContext) {
|
fun ParcelScope.cmdClear(context: ExecutionContext, @Flag sure: Boolean): Any? {
|
||||||
|
if (!sure) return "Are you sure? You cannot undo this action!\n" +
|
||||||
|
"Type ${context.rawInput} -sure if you want to go through with this."
|
||||||
|
|
||||||
world.generator.clearParcel(parcel)
|
world.generator.clearParcel(parcel)
|
||||||
.onProgressUpdate(1000, 1000) { progress, elapsedTime ->
|
.onProgressUpdate(1000, 1000) { progress, elapsedTime ->
|
||||||
context.sendMessage(EMessageType.INFORMATIVE, "Clear progress: %.02f%%, %.2fs elapsed"
|
context.sendMessage(EMessageType.INFORMATIVE, "Clear progress: %.02f%%, %.2fs elapsed"
|
||||||
.format(progress * 100, elapsedTime / 1000.0))
|
.format(progress * 100, elapsedTime / 1000.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
@Cmd("swap")
|
||||||
|
fun ParcelScope.cmdSwap(context: ExecutionContext, @Flag sure: Boolean): Any? {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Cmd("make_mess")
|
@Cmd("make_mess")
|
||||||
|
|||||||
23
src/main/kotlin/io/dico/parcels2/command/ParcelMatcher.kt
Normal file
23
src/main/kotlin/io/dico/parcels2/command/ParcelMatcher.kt
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package io.dico.parcels2.command
|
||||||
|
|
||||||
|
import io.dico.parcels2.Parcel
|
||||||
|
import io.dico.parcels2.ParcelWorld
|
||||||
|
import io.dico.parcels2.ParcelsPlugin
|
||||||
|
import kotlinx.coroutines.experimental.Deferred
|
||||||
|
|
||||||
|
interface ParcelTarget {
|
||||||
|
val world: ParcelWorld
|
||||||
|
val isByID: Boolean
|
||||||
|
val isByOwner: Boolean get() = !isByID
|
||||||
|
suspend fun ParcelsPlugin.await(): Parcel?
|
||||||
|
fun ParcelsPlugin.get(): Deferred<Parcel?> =
|
||||||
|
}
|
||||||
|
|
||||||
|
class ParcelTargetByOwner : ParcelTarget {
|
||||||
|
override val isByID get() = false
|
||||||
|
}
|
||||||
|
|
||||||
|
class ParcelTargetByID : ParcelTarget {
|
||||||
|
override val isByID get() = true
|
||||||
|
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import io.dico.dicore.command.parameter.type.ParameterConfig
|
|||||||
import io.dico.dicore.command.parameter.type.ParameterType
|
import io.dico.dicore.command.parameter.type.ParameterType
|
||||||
import io.dico.parcels2.Parcel
|
import io.dico.parcels2.Parcel
|
||||||
import io.dico.parcels2.ParcelWorld
|
import io.dico.parcels2.ParcelWorld
|
||||||
|
import io.dico.parcels2.ParcelsPlugin
|
||||||
import io.dico.parcels2.Worlds
|
import io.dico.parcels2.Worlds
|
||||||
import io.dico.parcels2.util.isValid
|
import io.dico.parcels2.util.isValid
|
||||||
import org.bukkit.Bukkit
|
import org.bukkit.Bukkit
|
||||||
|
|||||||
23
src/main/kotlin/io/dico/parcels2/listener/ListenerHelper.kt
Normal file
23
src/main/kotlin/io/dico/parcels2/listener/ListenerHelper.kt
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package io.dico.parcels2.listener
|
||||||
|
|
||||||
|
import io.dico.dicore.RegistratorListener
|
||||||
|
import io.dico.parcels2.ParcelsPlugin
|
||||||
|
import org.bukkit.event.Event
|
||||||
|
|
||||||
|
interface HasPlugin {
|
||||||
|
val plugin: ParcelsPlugin
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T: Event> HasPlugin.listener(crossinline block: suspend (T) -> Unit) = RegistratorListener<T> { event ->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -37,15 +37,17 @@ interface Backing {
|
|||||||
|
|
||||||
suspend fun setParcelOwner(parcelFor: Parcel, owner: ParcelOwner?)
|
suspend fun setParcelOwner(parcelFor: Parcel, owner: ParcelOwner?)
|
||||||
|
|
||||||
suspend fun setParcelPlayerState(parcelFor: Parcel, player: UUID, state: Boolean?)
|
suspend fun setLocalPlayerStatus(parcelFor: Parcel, player: UUID, status: AddedStatus)
|
||||||
|
|
||||||
suspend fun setParcelAllowsInteractInventory(parcel: Parcel, value: Boolean)
|
suspend fun setParcelAllowsInteractInventory(parcel: Parcel, value: Boolean)
|
||||||
|
|
||||||
suspend fun setParcelAllowsInteractInputs(parcel: Parcel, value: Boolean)
|
suspend fun setParcelAllowsInteractInputs(parcel: Parcel, value: Boolean)
|
||||||
|
|
||||||
|
|
||||||
|
suspend fun ProducerScope<Pair<ParcelOwner, MutableMap<UUID, AddedStatus>>>.produceAllGlobalAddedData()
|
||||||
|
|
||||||
suspend fun readGlobalAddedData(owner: ParcelOwner): MutableMap<UUID, AddedStatus>
|
suspend fun readGlobalAddedData(owner: ParcelOwner): MutableMap<UUID, AddedStatus>
|
||||||
|
|
||||||
suspend fun setGlobalAddedStatus(owner: ParcelOwner, player: UUID, status: AddedStatus)
|
suspend fun setGlobalPlayerStatus(owner: ParcelOwner, player: UUID, status: AddedStatus)
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,322 +0,0 @@
|
|||||||
package io.dico.parcels2.storage
|
|
||||||
|
|
||||||
import com.zaxxer.hikari.HikariDataSource
|
|
||||||
import io.dico.parcels2.*
|
|
||||||
import io.dico.parcels2.util.Vec2i
|
|
||||||
import io.dico.parcels2.util.toByteArray
|
|
||||||
import io.dico.parcels2.util.toUUID
|
|
||||||
import kotlinx.coroutines.experimental.channels.ProducerScope
|
|
||||||
import org.jetbrains.exposed.sql.*
|
|
||||||
import org.jetbrains.exposed.sql.SchemaUtils.create
|
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
|
||||||
import org.jetbrains.exposed.sql.vendors.DatabaseDialect
|
|
||||||
import org.joda.time.DateTime
|
|
||||||
import java.util.*
|
|
||||||
import javax.sql.DataSource
|
|
||||||
|
|
||||||
object WorldsT : Table("worlds") {
|
|
||||||
val id = integer("world_id").autoIncrement().primaryKey()
|
|
||||||
val name = varchar("name", 50)
|
|
||||||
val uid = binary("uid", 16)
|
|
||||||
val index_uid = uniqueIndexR("index_uid", uid)
|
|
||||||
}
|
|
||||||
|
|
||||||
object ParcelsT : Table("parcels") {
|
|
||||||
val id = integer("parcel_id").autoIncrement().primaryKey()
|
|
||||||
val px = integer("px")
|
|
||||||
val pz = integer("pz")
|
|
||||||
val world_id = integer("world_id").references(WorldsT.id)
|
|
||||||
val owner_uuid = binary("owner_uuid", 16).nullable()
|
|
||||||
val owner_name = varchar("owner_name", 16).nullable()
|
|
||||||
val claim_time = datetime("claim_time").nullable()
|
|
||||||
val index_location = uniqueIndexR("index_location", world_id, px, pz)
|
|
||||||
}
|
|
||||||
|
|
||||||
object AddedLocalT : Table("parcels_added_local") {
|
|
||||||
val parcel_id = integer("parcel_id").references(ParcelsT.id, ReferenceOption.CASCADE)
|
|
||||||
val player_uuid = binary("player_uuid", 16)
|
|
||||||
val allowed_flag = bool("allowed_flag")
|
|
||||||
val index_pair = uniqueIndexR("index_pair", parcel_id, player_uuid)
|
|
||||||
}
|
|
||||||
|
|
||||||
object AddedGlobalT : Table("parcels_added_global") {
|
|
||||||
val owner_uuid = binary("owner_uuid", 16)
|
|
||||||
val player_uuid = binary("player_uuid", 16)
|
|
||||||
val allowed_flag = bool("allowed_flag")
|
|
||||||
val index_pair = uniqueIndexR("index_pair", owner_uuid, player_uuid)
|
|
||||||
}
|
|
||||||
|
|
||||||
object ParcelOptionsT : Table("parcel_options") {
|
|
||||||
val parcel_id = integer("parcel_id").primaryKey().references(ParcelsT.id, ReferenceOption.CASCADE)
|
|
||||||
val interact_inventory = bool("interact_inventory").default(false)
|
|
||||||
val interact_inputs = bool("interact_inputs").default(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ExposedDatabaseException(message: String? = null) : Exception(message)
|
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
class ExposedBacking(private val dataSourceFactory: () -> DataSource) : Backing {
|
|
||||||
override val name get() = "Exposed"
|
|
||||||
private var dataSource: DataSource? = null
|
|
||||||
private var database: Database? = null
|
|
||||||
private var isShutdown: Boolean = false
|
|
||||||
|
|
||||||
override val isConnected get() = database != null
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
init {
|
|
||||||
Database.registerDialect("mariadb") {
|
|
||||||
Class.forName("org.jetbrains.exposed.sql.vendors.MysqlDialect").newInstance() as DatabaseDialect
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun init() {
|
|
||||||
if (isShutdown) throw IllegalStateException()
|
|
||||||
dataSource = dataSourceFactory()
|
|
||||||
database = Database.connect(dataSource!!)
|
|
||||||
transaction(database) {
|
|
||||||
create(WorldsT, ParcelsT, AddedLocalT, ParcelOptionsT)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun shutdown() {
|
|
||||||
if (isShutdown) throw IllegalStateException()
|
|
||||||
dataSource?.let {
|
|
||||||
if (it is HikariDataSource) it.close()
|
|
||||||
}
|
|
||||||
database = null
|
|
||||||
isShutdown = true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <T> transaction(statement: Transaction.() -> T) = transaction(database, statement)
|
|
||||||
|
|
||||||
private inline fun Transaction.getWorldId(binaryUid: ByteArray): Int? {
|
|
||||||
return WorldsT.select { WorldsT.uid eq binaryUid }.firstOrNull()?.let { it[WorldsT.id] }
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun Transaction.getWorldId(worldUid: UUID): Int? {
|
|
||||||
return getWorldId(worldUid.toByteArray()!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun Transaction.getOrInitWorldId(worldUid: UUID, worldName: String): Int {
|
|
||||||
val binaryUid = worldUid.toByteArray()!!
|
|
||||||
return getWorldId(binaryUid)
|
|
||||||
?: WorldsT.insert /*Ignore*/ { it[uid] = binaryUid; it[name] = worldName }.get(WorldsT.id)
|
|
||||||
?: throw ExposedDatabaseException("This should not happen - failed to insert world named $worldName and get its id")
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun Transaction.getParcelId(worldId: Int, parcelX: Int, parcelZ: Int): Int? {
|
|
||||||
return ParcelsT.select { (ParcelsT.world_id eq worldId) and (ParcelsT.px eq parcelX) and (ParcelsT.pz eq parcelZ) }
|
|
||||||
.firstOrNull()?.let { it[ParcelsT.id] }
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun Transaction.getParcelId(worldUid: UUID, parcelX: Int, parcelZ: Int): Int? {
|
|
||||||
return getWorldId(worldUid)?.let { getParcelId(it, parcelX, parcelZ) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun Transaction.getOrInitParcelId(worldUid: UUID, worldName: String, parcelX: Int, parcelZ: Int): Int {
|
|
||||||
val worldId = getOrInitWorldId(worldUid, worldName)
|
|
||||||
return getParcelId(worldId, parcelX, parcelZ)
|
|
||||||
?: ParcelsT.insert /*Ignore*/ { it[world_id] = worldId; it[px] = parcelX; it[pz] = parcelZ }.get(ParcelsT.id)
|
|
||||||
?: throw ExposedDatabaseException("This should not happen - failed to insert parcel at $worldName($parcelX, $parcelZ)")
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun Transaction.getParcelRow(id: Int): ResultRow? {
|
|
||||||
return ParcelsT.select { ParcelsT.id eq id }.firstOrNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Transaction.getWorldId(world: ParcelWorld): Int? {
|
|
||||||
return getWorldId(world.world.uid)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Transaction.getOrInitWorldId(world: ParcelWorld): Int {
|
|
||||||
return world.world.let { getOrInitWorldId(it.uid, it.name) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Transaction.getParcelId(parcel: Parcel): Int? {
|
|
||||||
return getParcelId(parcel.world.world.uid, parcel.pos.x, parcel.pos.z)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Transaction.getOrInitParcelId(parcel: Parcel): Int {
|
|
||||||
return parcel.world.world.let { getOrInitParcelId(it.uid, it.name, parcel.pos.x, parcel.pos.z) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Transaction.getParcelRow(parcel: Parcel): ResultRow? {
|
|
||||||
return getParcelId(parcel)?.let { getParcelRow(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun ProducerScope<Pair<Parcel, ParcelData?>>.produceParcelData(parcels: Sequence<Parcel>) {
|
|
||||||
for (parcel in parcels) {
|
|
||||||
val data = readParcelData(parcel)
|
|
||||||
channel.send(parcel to data)
|
|
||||||
}
|
|
||||||
channel.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun ProducerScope<Pair<SerializableParcel, ParcelData?>>.produceAllParcelData() {
|
|
||||||
ParcelsT.selectAll().forEach { row ->
|
|
||||||
val parcel = rowToSerializableParcel(row) ?: return@forEach
|
|
||||||
val data = rowToParcelData(row)
|
|
||||||
channel.send(parcel to data)
|
|
||||||
}
|
|
||||||
channel.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun readParcelData(parcelFor: Parcel): ParcelData? = transaction {
|
|
||||||
val row = getParcelRow(parcelFor) ?: return@transaction null
|
|
||||||
rowToParcelData(row)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun getOwnedParcels(user: ParcelOwner): List<SerializableParcel> = transaction {
|
|
||||||
val where: SqlExpressionBuilder.() -> Op<Boolean>
|
|
||||||
|
|
||||||
if (user.uuid != null) {
|
|
||||||
val binaryUuid = user.uuid.toByteArray()
|
|
||||||
where = { ParcelsT.owner_uuid eq binaryUuid }
|
|
||||||
} else {
|
|
||||||
val name = user.name
|
|
||||||
where = { ParcelsT.owner_name eq name }
|
|
||||||
}
|
|
||||||
|
|
||||||
ParcelsT.select(where)
|
|
||||||
.orderBy(ParcelsT.claim_time, isAsc = true)
|
|
||||||
.mapNotNull(::rowToSerializableParcel)
|
|
||||||
.toList()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override suspend fun setParcelData(parcelFor: Parcel, data: ParcelData?) {
|
|
||||||
if (data == null) {
|
|
||||||
transaction {
|
|
||||||
getParcelId(parcelFor)?.let { id ->
|
|
||||||
ParcelsT.deleteIgnoreWhere() { ParcelsT.id eq id }
|
|
||||||
|
|
||||||
// Below should cascade automatically
|
|
||||||
/*
|
|
||||||
AddedLocalT.deleteIgnoreWhere { AddedLocalT.parcel_id eq id }
|
|
||||||
ParcelOptionsT.deleteIgnoreWhere(limit = 1) { ParcelOptionsT.parcel_id eq id }
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val id = transaction {
|
|
||||||
val id = getOrInitParcelId(parcelFor)
|
|
||||||
AddedLocalT.deleteIgnoreWhere { AddedLocalT.parcel_id eq id }
|
|
||||||
id
|
|
||||||
}
|
|
||||||
|
|
||||||
setParcelOwner(parcelFor, data.owner)
|
|
||||||
|
|
||||||
for ((uuid, status) in data.added) {
|
|
||||||
val state = status.asBoolean
|
|
||||||
setParcelPlayerState(parcelFor, uuid, state)
|
|
||||||
}
|
|
||||||
|
|
||||||
setParcelAllowsInteractInputs(parcelFor, data.allowInteractInputs)
|
|
||||||
setParcelAllowsInteractInventory(parcelFor, data.allowInteractInventory)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun setParcelOwner(parcelFor: Parcel, owner: ParcelOwner?) = transaction {
|
|
||||||
val binaryUuid = owner?.uuid?.toByteArray()
|
|
||||||
val name = owner?.name
|
|
||||||
val time = owner?.let { DateTime.now() }
|
|
||||||
|
|
||||||
val id = if (owner == null)
|
|
||||||
getParcelId(parcelFor) ?: return@transaction
|
|
||||||
else
|
|
||||||
getOrInitParcelId(parcelFor)
|
|
||||||
|
|
||||||
ParcelsT.update({ ParcelsT.id eq id }) {
|
|
||||||
it[ParcelsT.owner_uuid] = binaryUuid
|
|
||||||
it[ParcelsT.owner_name] = name
|
|
||||||
it[ParcelsT.claim_time] = time
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun setParcelPlayerState(parcelFor: Parcel, player: UUID, state: Boolean?) = transaction {
|
|
||||||
val binaryUuid = player.toByteArray()!!
|
|
||||||
|
|
||||||
if (state == null) {
|
|
||||||
getParcelId(parcelFor)?.let { id ->
|
|
||||||
AddedLocalT.deleteWhere { (AddedLocalT.parcel_id eq id) and (AddedLocalT.player_uuid eq binaryUuid) }
|
|
||||||
}
|
|
||||||
return@transaction
|
|
||||||
}
|
|
||||||
|
|
||||||
val id = getOrInitParcelId(parcelFor)
|
|
||||||
AddedLocalT.upsert(AddedLocalT.parcel_id) {
|
|
||||||
it[AddedLocalT.parcel_id] = id
|
|
||||||
it[AddedLocalT.player_uuid] = binaryUuid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun setParcelAllowsInteractInventory(parcel: Parcel, value: Boolean): Unit = transaction {
|
|
||||||
val id = getOrInitParcelId(parcel)
|
|
||||||
/*ParcelOptionsT.upsert(ParcelOptionsT.parcel_id) {
|
|
||||||
it[ParcelOptionsT.parcel_id] = id
|
|
||||||
it[ParcelOptionsT.interact_inventory] = value
|
|
||||||
}*/
|
|
||||||
|
|
||||||
ParcelOptionsT.upsert(ParcelOptionsT.parcel_id) {
|
|
||||||
it[ParcelOptionsT.parcel_id] = id
|
|
||||||
it[ParcelOptionsT.interact_inventory] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun setParcelAllowsInteractInputs(parcel: Parcel, value: Boolean): Unit = transaction {
|
|
||||||
val id = getOrInitParcelId(parcel)
|
|
||||||
ParcelOptionsT.upsert(ParcelOptionsT.parcel_id) {
|
|
||||||
it[ParcelOptionsT.parcel_id] = id
|
|
||||||
it[ParcelOptionsT.interact_inputs] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun readGlobalAddedData(owner: ParcelOwner): AddedData? {
|
|
||||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun setGlobalAddedStatus(owner: ParcelOwner, player: UUID, status: Boolean?) {
|
|
||||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun rowToSerializableParcel(row: ResultRow): SerializableParcel? {
|
|
||||||
val worldId = row[ParcelsT.world_id]
|
|
||||||
val worldRow = WorldsT.select { WorldsT.id eq worldId }.firstOrNull()
|
|
||||||
?: return null
|
|
||||||
|
|
||||||
val world = SerializableWorld(worldRow[WorldsT.name], worldRow[WorldsT.uid].toUUID())
|
|
||||||
return SerializableParcel(world, Vec2i(row[ParcelsT.px], row[ParcelsT.pz]))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun rowToParcelData(row: ResultRow) = ParcelDataHolder().apply {
|
|
||||||
owner = ParcelOwner.create(
|
|
||||||
uuid = row[ParcelsT.owner_uuid]?.toUUID(),
|
|
||||||
name = row[ParcelsT.owner_name],
|
|
||||||
time = row[ParcelsT.claim_time]
|
|
||||||
)
|
|
||||||
|
|
||||||
val parcelId = row[ParcelsT.id]
|
|
||||||
AddedLocalT.select { AddedLocalT.parcel_id eq parcelId }.forEach {
|
|
||||||
val uuid = it[AddedLocalT.player_uuid].toUUID()!!
|
|
||||||
val status = if (it[AddedLocalT.allowed_flag]) AddedStatus.ALLOWED else AddedStatus.BANNED
|
|
||||||
setAddedStatus(uuid, status)
|
|
||||||
}
|
|
||||||
|
|
||||||
ParcelOptionsT.select { ParcelOptionsT.parcel_id eq parcelId }.firstOrNull()?.let {
|
|
||||||
allowInteractInputs = it[ParcelOptionsT.interact_inputs]
|
|
||||||
allowInteractInventory = it[ParcelOptionsT.interact_inventory]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -24,9 +24,9 @@ interface Storage {
|
|||||||
|
|
||||||
fun readParcelData(parcelFor: Parcel): Deferred<ParcelData?>
|
fun readParcelData(parcelFor: Parcel): Deferred<ParcelData?>
|
||||||
|
|
||||||
fun readParcelData(parcelsFor: Sequence<Parcel>, channelCapacity: Int): ReceiveChannel<Pair<Parcel, ParcelData?>>
|
fun readParcelData(parcelsFor: Sequence<Parcel>): ReceiveChannel<Pair<Parcel, ParcelData?>>
|
||||||
|
|
||||||
fun readAllParcelData(channelCapacity: Int): ReceiveChannel<Pair<SerializableParcel, ParcelData?>>
|
fun readAllParcelData(): ReceiveChannel<Pair<SerializableParcel, ParcelData?>>
|
||||||
|
|
||||||
fun getOwnedParcels(user: ParcelOwner): Deferred<List<SerializableParcel>>
|
fun getOwnedParcels(user: ParcelOwner): Deferred<List<SerializableParcel>>
|
||||||
|
|
||||||
@@ -37,13 +37,15 @@ interface Storage {
|
|||||||
|
|
||||||
fun setParcelOwner(parcelFor: Parcel, owner: ParcelOwner?): Job
|
fun setParcelOwner(parcelFor: Parcel, owner: ParcelOwner?): Job
|
||||||
|
|
||||||
fun setParcelPlayerState(parcelFor: Parcel, player: UUID, state: Boolean?): Job
|
fun setParcelPlayerStatus(parcelFor: Parcel, player: UUID, status: AddedStatus): Job
|
||||||
|
|
||||||
fun setParcelAllowsInteractInventory(parcel: Parcel, value: Boolean): Job
|
fun setParcelAllowsInteractInventory(parcel: Parcel, value: Boolean): Job
|
||||||
|
|
||||||
fun setParcelAllowsInteractInputs(parcel: Parcel, value: Boolean): Job
|
fun setParcelAllowsInteractInputs(parcel: Parcel, value: Boolean): Job
|
||||||
|
|
||||||
|
|
||||||
|
fun readAllGlobalAddedData(): ReceiveChannel<Pair<ParcelOwner, MutableMap<UUID, AddedStatus>>>
|
||||||
|
|
||||||
fun readGlobalAddedData(owner: ParcelOwner): Deferred<MutableMap<UUID, AddedStatus>?>
|
fun readGlobalAddedData(owner: ParcelOwner): Deferred<MutableMap<UUID, AddedStatus>?>
|
||||||
|
|
||||||
fun setGlobalAddedStatus(owner: ParcelOwner, player: UUID, status: AddedStatus): Job
|
fun setGlobalAddedStatus(owner: ParcelOwner, player: UUID, status: AddedStatus): Job
|
||||||
@@ -55,6 +57,7 @@ class StorageWithCoroutineBacking internal constructor(val backing: Backing) : S
|
|||||||
val poolSize: Int get() = 4
|
val poolSize: Int get() = 4
|
||||||
override val asyncDispatcher = Executors.newFixedThreadPool(poolSize) { Thread(it, "Parcels2_StorageThread") }.asCoroutineDispatcher()
|
override val asyncDispatcher = Executors.newFixedThreadPool(poolSize) { Thread(it, "Parcels2_StorageThread") }.asCoroutineDispatcher()
|
||||||
override val isConnected get() = backing.isConnected
|
override val isConnected get() = backing.isConnected
|
||||||
|
val channelCapacity = 16
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
private inline fun <T> defer(noinline block: suspend CoroutineScope.() -> T): Deferred<T> {
|
private inline fun <T> defer(noinline block: suspend CoroutineScope.() -> T): Deferred<T> {
|
||||||
@@ -73,10 +76,10 @@ class StorageWithCoroutineBacking internal constructor(val backing: Backing) : S
|
|||||||
|
|
||||||
override fun readParcelData(parcelFor: Parcel) = defer { backing.readParcelData(parcelFor) }
|
override fun readParcelData(parcelFor: Parcel) = defer { backing.readParcelData(parcelFor) }
|
||||||
|
|
||||||
override fun readParcelData(parcelsFor: Sequence<Parcel>, channelCapacity: Int) =
|
override fun readParcelData(parcelsFor: Sequence<Parcel>) =
|
||||||
produce(asyncDispatcher, capacity = channelCapacity) { with(backing) { produceParcelData(parcelsFor) } }
|
produce(asyncDispatcher, capacity = channelCapacity) { with(backing) { produceParcelData(parcelsFor) } }
|
||||||
|
|
||||||
override fun readAllParcelData(channelCapacity: Int): ReceiveChannel<Pair<SerializableParcel, ParcelData?>> =
|
override fun readAllParcelData(): ReceiveChannel<Pair<SerializableParcel, ParcelData?>> =
|
||||||
produce(asyncDispatcher, capacity = channelCapacity) { with(backing) { produceAllParcelData() } }
|
produce(asyncDispatcher, capacity = channelCapacity) { with(backing) { produceAllParcelData() } }
|
||||||
|
|
||||||
override fun getOwnedParcels(user: ParcelOwner) = defer { backing.getOwnedParcels(user) }
|
override fun getOwnedParcels(user: ParcelOwner) = defer { backing.getOwnedParcels(user) }
|
||||||
@@ -87,14 +90,17 @@ class StorageWithCoroutineBacking internal constructor(val backing: Backing) : S
|
|||||||
|
|
||||||
override fun setParcelOwner(parcelFor: Parcel, owner: ParcelOwner?) = job { backing.setParcelOwner(parcelFor, owner) }
|
override fun setParcelOwner(parcelFor: Parcel, owner: ParcelOwner?) = job { backing.setParcelOwner(parcelFor, owner) }
|
||||||
|
|
||||||
override fun setParcelPlayerState(parcelFor: Parcel, player: UUID, state: Boolean?) = job { backing.setParcelPlayerState(parcelFor, player, state) }
|
override fun setParcelPlayerStatus(parcelFor: Parcel, player: UUID, status: AddedStatus) = job { backing.setLocalPlayerStatus(parcelFor, player, status) }
|
||||||
|
|
||||||
override fun setParcelAllowsInteractInventory(parcel: Parcel, value: Boolean) = job { backing.setParcelAllowsInteractInventory(parcel, value) }
|
override fun setParcelAllowsInteractInventory(parcel: Parcel, value: Boolean) = job { backing.setParcelAllowsInteractInventory(parcel, value) }
|
||||||
|
|
||||||
override fun setParcelAllowsInteractInputs(parcel: Parcel, value: Boolean) = job { backing.setParcelAllowsInteractInputs(parcel, value) }
|
override fun setParcelAllowsInteractInputs(parcel: Parcel, value: Boolean) = job { backing.setParcelAllowsInteractInputs(parcel, value) }
|
||||||
|
|
||||||
|
|
||||||
|
override fun readAllGlobalAddedData(): ReceiveChannel<Pair<ParcelOwner, MutableMap<UUID, AddedStatus>>> =
|
||||||
|
produce(asyncDispatcher, capacity = channelCapacity) { with(backing) { produceAllGlobalAddedData() } }
|
||||||
|
|
||||||
override fun readGlobalAddedData(owner: ParcelOwner): Deferred<MutableMap<UUID, AddedStatus>?> = defer { backing.readGlobalAddedData(owner) }
|
override fun readGlobalAddedData(owner: ParcelOwner): Deferred<MutableMap<UUID, AddedStatus>?> = defer { backing.readGlobalAddedData(owner) }
|
||||||
|
|
||||||
override fun setGlobalAddedStatus(owner: ParcelOwner, player: UUID, status: AddedStatus) = job { backing.setGlobalAddedStatus(owner, player, status) }
|
override fun setGlobalAddedStatus(owner: ParcelOwner, player: UUID, status: AddedStatus) = job { backing.setGlobalPlayerStatus(owner, player, status) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package io.dico.parcels2.storage
|
|||||||
|
|
||||||
import com.zaxxer.hikari.HikariDataSource
|
import com.zaxxer.hikari.HikariDataSource
|
||||||
import io.dico.parcels2.DataConnectionOptions
|
import io.dico.parcels2.DataConnectionOptions
|
||||||
|
import io.dico.parcels2.storage.exposed.ExposedBacking
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
interface StorageFactory {
|
interface StorageFactory {
|
||||||
@@ -35,7 +36,7 @@ class ConnectionStorageFactory : StorageFactory {
|
|||||||
|
|
||||||
override fun newStorageInstance(dialect: String, options: Any): Storage {
|
override fun newStorageInstance(dialect: String, options: Any): Storage {
|
||||||
val hikariConfig = getHikariConfig(dialect, options as DataConnectionOptions)
|
val hikariConfig = getHikariConfig(dialect, options as DataConnectionOptions)
|
||||||
val dataSourceFactory = { HikariDataSource(hikariConfig) }
|
val dataSourceFactory = suspend { HikariDataSource(hikariConfig) }
|
||||||
return StorageWithCoroutineBacking(ExposedBacking(dataSourceFactory))
|
return StorageWithCoroutineBacking(ExposedBacking(dataSourceFactory))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,186 @@
|
|||||||
|
@file:Suppress("NOTHING_TO_INLINE", "PARAMETER_NAME_CHANGED_ON_OVERRIDE", "LocalVariableName")
|
||||||
|
|
||||||
|
package io.dico.parcels2.storage.exposed
|
||||||
|
|
||||||
|
import com.zaxxer.hikari.HikariDataSource
|
||||||
|
import io.dico.parcels2.*
|
||||||
|
import io.dico.parcels2.storage.*
|
||||||
|
import io.dico.parcels2.util.toUUID
|
||||||
|
import kotlinx.coroutines.experimental.channels.ProducerScope
|
||||||
|
import org.jetbrains.exposed.sql.*
|
||||||
|
import org.jetbrains.exposed.sql.SchemaUtils.create
|
||||||
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
import org.jetbrains.exposed.sql.vendors.DatabaseDialect
|
||||||
|
import org.joda.time.DateTime
|
||||||
|
import java.util.*
|
||||||
|
import javax.sql.DataSource
|
||||||
|
|
||||||
|
class ExposedDatabaseException(message: String? = null) : Exception(message)
|
||||||
|
|
||||||
|
class ExposedBacking(private val dataSourceFactory: suspend () -> DataSource) : Backing {
|
||||||
|
override val name get() = "Exposed"
|
||||||
|
private var dataSource: DataSource? = null
|
||||||
|
private var database: Database? = null
|
||||||
|
private var isShutdown: Boolean = false
|
||||||
|
|
||||||
|
override val isConnected get() = database != null
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
init {
|
||||||
|
Database.registerDialect("mariadb") {
|
||||||
|
Class.forName("org.jetbrains.exposed.sql.vendors.MysqlDialect").newInstance() as DatabaseDialect
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T> transaction(statement: Transaction.() -> T) = transaction(database!!, statement)
|
||||||
|
|
||||||
|
override suspend fun init() {
|
||||||
|
if (isShutdown) throw IllegalStateException()
|
||||||
|
dataSource = dataSourceFactory()
|
||||||
|
database = Database.connect(dataSource!!)
|
||||||
|
transaction(database) {
|
||||||
|
create(WorldsT, OwnersT, ParcelsT, ParcelOptionsT, AddedLocalT, AddedGlobalT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun shutdown() {
|
||||||
|
if (isShutdown) throw IllegalStateException()
|
||||||
|
dataSource?.let {
|
||||||
|
(it as? HikariDataSource)?.close()
|
||||||
|
}
|
||||||
|
database = null
|
||||||
|
isShutdown = true
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun ProducerScope<Pair<Parcel, ParcelData?>>.produceParcelData(parcels: Sequence<Parcel>) {
|
||||||
|
for (parcel in parcels) {
|
||||||
|
val data = readParcelData(parcel)
|
||||||
|
channel.send(parcel to data)
|
||||||
|
}
|
||||||
|
channel.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun ProducerScope<Pair<SerializableParcel, ParcelData?>>.produceAllParcelData() {
|
||||||
|
ParcelsT.selectAll().forEach { row ->
|
||||||
|
val parcel = ParcelsT.getSerializable(row) ?: return@forEach
|
||||||
|
val data = rowToParcelData(row)
|
||||||
|
channel.send(parcel to data)
|
||||||
|
}
|
||||||
|
channel.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun readParcelData(parcelFor: Parcel): ParcelData? = transaction {
|
||||||
|
val row = ParcelsT.getRow(parcelFor) ?: return@transaction null
|
||||||
|
rowToParcelData(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getOwnedParcels(user: ParcelOwner): List<SerializableParcel> = transaction {
|
||||||
|
val user_id = OwnersT.getId(user) ?: return@transaction emptyList()
|
||||||
|
ParcelsT.select { ParcelsT.owner_id eq user_id }
|
||||||
|
.orderBy(ParcelsT.claim_time, isAsc = true)
|
||||||
|
.mapNotNull(ParcelsT::getSerializable)
|
||||||
|
.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun setParcelData(parcelFor: Parcel, data: ParcelData?) {
|
||||||
|
if (data == null) {
|
||||||
|
transaction {
|
||||||
|
ParcelsT.getId(parcelFor)?.let { id ->
|
||||||
|
ParcelsT.deleteIgnoreWhere { ParcelsT.id eq id }
|
||||||
|
|
||||||
|
// Below should cascade automatically
|
||||||
|
/*
|
||||||
|
AddedLocalT.deleteIgnoreWhere { AddedLocalT.parcel_id eq id }
|
||||||
|
ParcelOptionsT.deleteIgnoreWhere(limit = 1) { ParcelOptionsT.parcel_id eq id }
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
val id = ParcelsT.getOrInitId(parcelFor)
|
||||||
|
AddedLocalT.deleteIgnoreWhere { AddedLocalT.attach_id eq id }
|
||||||
|
}
|
||||||
|
|
||||||
|
setParcelOwner(parcelFor, data.owner)
|
||||||
|
|
||||||
|
for ((uuid, status) in data.added) {
|
||||||
|
setLocalPlayerStatus(parcelFor, uuid, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
setParcelAllowsInteractInputs(parcelFor, data.allowInteractInputs)
|
||||||
|
setParcelAllowsInteractInventory(parcelFor, data.allowInteractInventory)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun setParcelOwner(parcelFor: Parcel, owner: ParcelOwner?) = transaction {
|
||||||
|
val id = if (owner == null)
|
||||||
|
ParcelsT.getId(parcelFor) ?: return@transaction
|
||||||
|
else
|
||||||
|
ParcelsT.getOrInitId(parcelFor)
|
||||||
|
|
||||||
|
val owner_id = owner?.let { OwnersT.getOrInitId(it) }
|
||||||
|
val time = owner?.let { DateTime.now() }
|
||||||
|
|
||||||
|
ParcelsT.update({ ParcelsT.id eq id }) {
|
||||||
|
it[ParcelsT.owner_id] = owner_id
|
||||||
|
it[claim_time] = time
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun setLocalPlayerStatus(parcelFor: Parcel, player: UUID, status: AddedStatus) = transaction {
|
||||||
|
AddedLocalT.setPlayerStatus(parcelFor, player, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun setParcelAllowsInteractInventory(parcel: Parcel, value: Boolean): Unit = transaction {
|
||||||
|
val id = ParcelsT.getOrInitId(parcel)
|
||||||
|
ParcelOptionsT.upsert(ParcelOptionsT.parcel_id) {
|
||||||
|
it[ParcelOptionsT.parcel_id] = id
|
||||||
|
it[ParcelOptionsT.interact_inventory] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun setParcelAllowsInteractInputs(parcel: Parcel, value: Boolean): Unit = transaction {
|
||||||
|
val id = ParcelsT.getOrInitId(parcel)
|
||||||
|
ParcelOptionsT.upsert(ParcelOptionsT.parcel_id) {
|
||||||
|
it[ParcelOptionsT.parcel_id] = id
|
||||||
|
it[ParcelOptionsT.interact_inputs] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun ProducerScope<Pair<ParcelOwner, MutableMap<UUID, AddedStatus>>>.produceAllGlobalAddedData() {
|
||||||
|
AddedGlobalT.sendAllAddedData(channel)
|
||||||
|
channel.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun readGlobalAddedData(owner: ParcelOwner): MutableMap<UUID, AddedStatus> {
|
||||||
|
return AddedGlobalT.readAddedData(OwnersT.getId(owner) ?: return hashMapOf())
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun setGlobalPlayerStatus(owner: ParcelOwner, player: UUID, status: AddedStatus) = transaction {
|
||||||
|
AddedGlobalT.setPlayerStatus(owner, player, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rowToParcelData(row: ResultRow) = ParcelDataHolder().apply {
|
||||||
|
owner = row[ParcelsT.owner_id]?.let { OwnersT.getSerializable(it) }
|
||||||
|
since = row[ParcelsT.claim_time]
|
||||||
|
|
||||||
|
val parcelId = row[ParcelsT.id]
|
||||||
|
added = AddedLocalT.readAddedData(parcelId)
|
||||||
|
|
||||||
|
AddedLocalT.select { AddedLocalT.attach_id eq parcelId }.forEach {
|
||||||
|
val uuid = it[AddedLocalT.player_uuid].toUUID()
|
||||||
|
val status = if (it[AddedLocalT.allowed_flag]) AddedStatus.ALLOWED else AddedStatus.BANNED
|
||||||
|
setAddedStatus(uuid, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
ParcelOptionsT.select { ParcelOptionsT.parcel_id eq parcelId }.firstOrNull()?.let {
|
||||||
|
allowInteractInputs = it[ParcelOptionsT.interact_inputs]
|
||||||
|
allowInteractInventory = it[ParcelOptionsT.interact_inventory]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
121
src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt
Normal file
121
src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
@file:Suppress("NOTHING_TO_INLINE", "PARAMETER_NAME_CHANGED_ON_OVERRIDE", "unused", "MemberVisibilityCanBePrivate")
|
||||||
|
|
||||||
|
package io.dico.parcels2.storage.exposed
|
||||||
|
|
||||||
|
import io.dico.parcels2.Parcel
|
||||||
|
import io.dico.parcels2.ParcelOwner
|
||||||
|
import io.dico.parcels2.ParcelWorld
|
||||||
|
import io.dico.parcels2.storage.SerializableParcel
|
||||||
|
import io.dico.parcels2.storage.SerializableWorld
|
||||||
|
import io.dico.parcels2.storage.uniqueIndexR
|
||||||
|
import io.dico.parcels2.util.Vec2i
|
||||||
|
import io.dico.parcels2.util.toByteArray
|
||||||
|
import io.dico.parcels2.util.toUUID
|
||||||
|
import org.jetbrains.exposed.sql.*
|
||||||
|
import org.jetbrains.exposed.sql.statements.InsertStatement
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
sealed class IdTransactionsTable<TableT : IdTransactionsTable<TableT, QueryObj, SerializableObj>,
|
||||||
|
QueryObj, SerializableObj>(tableName: String, columnName: String)
|
||||||
|
: Table(tableName) {
|
||||||
|
val id = integer(columnName).autoIncrement().primaryKey()
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
inline val table: TableT
|
||||||
|
get() = this as TableT
|
||||||
|
|
||||||
|
internal inline fun getId(where: SqlExpressionBuilder.(TableT) -> Op<Boolean>): Int? {
|
||||||
|
return select { where(table) }.firstOrNull()?.let { it[id] }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal inline fun insertAndGetId(objName: String, noinline body: TableT.(InsertStatement<Number>) -> Unit): Int {
|
||||||
|
return table.insert(body)[id] ?: insertError(objName)
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun insertError(obj: String): Nothing = throw ExposedDatabaseException("This should not happen - failed to insert $obj and get its id")
|
||||||
|
|
||||||
|
abstract fun getId(obj: QueryObj): Int?
|
||||||
|
abstract fun getOrInitId(obj: QueryObj): Int
|
||||||
|
fun getSerializable(id: Int): SerializableObj? = select { this@IdTransactionsTable.id eq id }.firstOrNull()?.let { getSerializable(it) }
|
||||||
|
abstract fun getSerializable(row: ResultRow): SerializableObj?
|
||||||
|
}
|
||||||
|
|
||||||
|
object WorldsT : IdTransactionsTable<WorldsT, ParcelWorld, SerializableWorld>("parcel_worlds", "world_id") {
|
||||||
|
val name = varchar("name", 50)
|
||||||
|
val uid = binary("uid", 2)
|
||||||
|
val index_uid = uniqueIndexR("index_uid", uid)
|
||||||
|
|
||||||
|
internal inline fun getId(binaryUid: ByteArray): Int? = getId { uid eq binaryUid }
|
||||||
|
internal inline fun getId(uid: UUID): Int? = getId(uid.toByteArray())
|
||||||
|
internal inline fun getOrInitId(worldUid: UUID, worldName: String): Int = worldUid.toByteArray().let { binaryUid ->
|
||||||
|
getId(binaryUid)
|
||||||
|
?: insertAndGetId("world named $worldName") { it[uid] = binaryUid; it[name] = worldName }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getId(world: ParcelWorld): Int? = getId(world.world.uid)
|
||||||
|
override fun getOrInitId(world: ParcelWorld): Int = world.world.let { getOrInitId(it.uid, it.name) }
|
||||||
|
|
||||||
|
override fun getSerializable(row: ResultRow): SerializableWorld {
|
||||||
|
return SerializableWorld(row[name], row[uid].toUUID())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object ParcelsT : IdTransactionsTable<ParcelsT, Parcel, SerializableParcel>("parcels", "parcel_id") {
|
||||||
|
val world_id = integer("world_id").references(WorldsT.id)
|
||||||
|
val px = integer("px")
|
||||||
|
val pz = integer("pz")
|
||||||
|
val owner_id = integer("owner_id").references(OwnersT.id).nullable()
|
||||||
|
val claim_time = datetime("claim_time").nullable()
|
||||||
|
val index_location = uniqueIndexR("index_location", world_id, px, pz)
|
||||||
|
|
||||||
|
private inline fun getId(worldId: Int, parcelX: Int, parcelZ: Int): Int? = getId { world_id.eq(worldId) and px.eq(parcelX) and pz.eq(parcelZ) }
|
||||||
|
private inline fun getId(worldUid: UUID, parcelX: Int, parcelZ: Int): Int? = WorldsT.getId(worldUid)?.let { getId(it, parcelX, parcelZ) }
|
||||||
|
private inline fun getOrInitId(worldUid: UUID, worldName: String, parcelX: Int, parcelZ: Int): Int {
|
||||||
|
val worldId = WorldsT.getOrInitId(worldUid, worldName)
|
||||||
|
return getId(worldId, parcelX, parcelZ)
|
||||||
|
?: insertAndGetId("parcel at $worldName($parcelX, $parcelZ)") { it[world_id] = worldId; it[px] = parcelX; it[pz] = parcelZ }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getId(parcel: Parcel): Int? = getId(parcel.world.world.uid, parcel.pos.x, parcel.pos.z)
|
||||||
|
override fun getOrInitId(parcel: Parcel): Int = parcel.world.world.let { getOrInitId(it.uid, it.name, parcel.pos.x, parcel.pos.z) }
|
||||||
|
|
||||||
|
private inline fun getRow(id: Int): ResultRow? = select { ParcelsT.id eq id }.firstOrNull()
|
||||||
|
fun getRow(parcel: Parcel): ResultRow? = getId(parcel)?.let { getRow(it) }
|
||||||
|
|
||||||
|
override fun getSerializable(row: ResultRow): SerializableParcel? {
|
||||||
|
val worldId = row[world_id]
|
||||||
|
val world = WorldsT.getSerializable(worldId) ?: return null
|
||||||
|
return SerializableParcel(world, Vec2i(row[px], row[pz]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object OwnersT : IdTransactionsTable<OwnersT, ParcelOwner, ParcelOwner>("parcel_owners", "owner_id") {
|
||||||
|
val uuid = binary("uuid", 2).nullable()
|
||||||
|
val name = varchar("name", 32).nullable()
|
||||||
|
val index_pair = uniqueIndexR("index_pair", uuid, name)
|
||||||
|
|
||||||
|
private inline fun getId(binaryUuid: ByteArray) = getId { uuid eq binaryUuid }
|
||||||
|
private inline fun getId(uuid: UUID) = getId(uuid.toByteArray())
|
||||||
|
private inline fun getId(name: String) = getId { OwnersT.name eq name }
|
||||||
|
|
||||||
|
private inline fun getOrInitId(uuid: UUID) = uuid.toByteArray().let { binaryUuid ->
|
||||||
|
getId(binaryUuid)
|
||||||
|
?: insertAndGetId("owner(uuid = $uuid)") { it[OwnersT.uuid] = binaryUuid }
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun getOrInitId(name: String) =
|
||||||
|
getId(name)
|
||||||
|
?: insertAndGetId("owner(name = $name)") { it[OwnersT.name] = name }
|
||||||
|
|
||||||
|
override fun getId(owner: ParcelOwner): Int? =
|
||||||
|
if (owner.hasUUID) getId(owner.uuid!!)
|
||||||
|
else getId(owner.name!!)
|
||||||
|
|
||||||
|
override fun getOrInitId(owner: ParcelOwner): Int =
|
||||||
|
if (owner.hasUUID) getOrInitId(owner.uuid!!)
|
||||||
|
else getOrInitId(owner.name!!)
|
||||||
|
|
||||||
|
override fun getSerializable(row: ResultRow): ParcelOwner {
|
||||||
|
return row[uuid]?.toUUID()?.let { ParcelOwner(it) } ?: ParcelOwner(row[name]!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
104
src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt
Normal file
104
src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
@file:Suppress("PropertyName", "LocalVariableName", "NOTHING_TO_INLINE")
|
||||||
|
|
||||||
|
package io.dico.parcels2.storage.exposed
|
||||||
|
|
||||||
|
import io.dico.parcels2.AddedStatus
|
||||||
|
import io.dico.parcels2.Parcel
|
||||||
|
import io.dico.parcels2.ParcelOwner
|
||||||
|
import io.dico.parcels2.storage.SerializableParcel
|
||||||
|
import io.dico.parcels2.storage.uniqueIndexR
|
||||||
|
import io.dico.parcels2.storage.upsert
|
||||||
|
import io.dico.parcels2.util.toByteArray
|
||||||
|
import io.dico.parcels2.util.toUUID
|
||||||
|
import kotlinx.coroutines.experimental.channels.SendChannel
|
||||||
|
import org.jetbrains.exposed.sql.*
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object AddedLocalT : AddedTable<Parcel, SerializableParcel>("parcels_added_local", ParcelsT)
|
||||||
|
object AddedGlobalT : AddedTable<ParcelOwner, ParcelOwner>("parcels_added_global", OwnersT)
|
||||||
|
|
||||||
|
object ParcelOptionsT : Table("parcel_options") {
|
||||||
|
val parcel_id = integer("parcel_id").primaryKey().references(ParcelsT.id, ReferenceOption.CASCADE)
|
||||||
|
val interact_inventory = bool("interact_inventory").default(false)
|
||||||
|
val interact_inputs = bool("interact_inputs").default(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
typealias AddedStatusSendChannel<AttachT> = SendChannel<Pair<AttachT, MutableMap<UUID, AddedStatus>>>
|
||||||
|
|
||||||
|
sealed class AddedTable<AttachT, SerializableT>(name: String, val idTable: IdTransactionsTable<*, AttachT, SerializableT>) : Table(name) {
|
||||||
|
val attach_id = integer("attach_id").references(idTable.id, ReferenceOption.CASCADE)
|
||||||
|
val player_uuid = binary("player_uuid", 16)
|
||||||
|
val allowed_flag = bool("allowed_flag")
|
||||||
|
val index_pair = uniqueIndexR("index_pair", attach_id, player_uuid)
|
||||||
|
|
||||||
|
fun setPlayerStatus(attachedOn: AttachT, player: UUID, status: AddedStatus) {
|
||||||
|
val binaryUuid = player.toByteArray()
|
||||||
|
|
||||||
|
if (status.isDefault) {
|
||||||
|
idTable.getId(attachedOn)?.let { id ->
|
||||||
|
deleteWhere { (attach_id eq id) and (player_uuid eq binaryUuid) }
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val id = idTable.getOrInitId(attachedOn)
|
||||||
|
upsert(conflictIndex = index_pair) {
|
||||||
|
it[attach_id] = id
|
||||||
|
it[player_uuid] = binaryUuid
|
||||||
|
it[allowed_flag] = status.isAllowed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readAddedData(id: Int): MutableMap<UUID, AddedStatus> {
|
||||||
|
return slice(player_uuid, allowed_flag).select { attach_id eq id }
|
||||||
|
.associateByTo(hashMapOf(), { it[player_uuid].toUUID() }, { it[allowed_flag].asAddedStatus() })
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun sendAllAddedData(channel: AddedStatusSendChannel<SerializableT>) {
|
||||||
|
val iterator = selectAll().orderBy(attach_id).iterator()
|
||||||
|
|
||||||
|
if (iterator.hasNext()) {
|
||||||
|
val firstRow = iterator.next()
|
||||||
|
var id: Int = firstRow[attach_id]
|
||||||
|
var attach: SerializableT? = null
|
||||||
|
var map: MutableMap<UUID, AddedStatus>? = null
|
||||||
|
|
||||||
|
fun initAttachAndMap() {
|
||||||
|
attach = idTable.getSerializable(id)
|
||||||
|
map = attach?.let { mutableMapOf() }
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun sendIfPresent() {
|
||||||
|
if (attach != null && map != null && map!!.isNotEmpty()) {
|
||||||
|
channel.send(attach!! to map!!)
|
||||||
|
}
|
||||||
|
attach = null
|
||||||
|
map = null
|
||||||
|
}
|
||||||
|
|
||||||
|
initAttachAndMap()
|
||||||
|
|
||||||
|
for (row in iterator) {
|
||||||
|
val rowId = row[attach_id]
|
||||||
|
if (rowId != id) {
|
||||||
|
sendIfPresent()
|
||||||
|
id = rowId
|
||||||
|
initAttachAndMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attach == null) {
|
||||||
|
continue // owner not found for this owner id
|
||||||
|
}
|
||||||
|
|
||||||
|
val player_uuid = row[player_uuid].toUUID()
|
||||||
|
val status = row[allowed_flag].asAddedStatus()
|
||||||
|
map!![player_uuid] = status
|
||||||
|
}
|
||||||
|
|
||||||
|
sendIfPresent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun Boolean?.asAddedStatus() = if (this == null) AddedStatus.DEFAULT else if (this) AddedStatus.ALLOWED else AddedStatus.BANNED
|
||||||
|
|
||||||
|
}
|
||||||
@@ -49,8 +49,6 @@ class AwaitTask : Runnable {
|
|||||||
onSuccess!!.invoke()
|
onSuccess!!.invoke()
|
||||||
}
|
}
|
||||||
|
|
||||||
elapsedChecks++
|
|
||||||
|
|
||||||
if (maxChecks in 1 until elapsedChecks) {
|
if (maxChecks in 1 until elapsedChecks) {
|
||||||
cancel()
|
cancel()
|
||||||
onFailure?.invoke()
|
onFailure?.invoke()
|
||||||
|
|||||||
53
src/main/kotlin/io/dico/parcels2/util/FunctionHelper.kt
Normal file
53
src/main/kotlin/io/dico/parcels2/util/FunctionHelper.kt
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package io.dico.parcels2.util
|
||||||
|
|
||||||
|
import io.dico.parcels2.ParcelsPlugin
|
||||||
|
import kotlinx.coroutines.experimental.*
|
||||||
|
import org.bukkit.scheduler.BukkitTask
|
||||||
|
import kotlin.coroutines.experimental.CoroutineContext
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
class FunctionHelper(val plugin: ParcelsPlugin) {
|
||||||
|
val mainThreadDispatcher: MainThreadDispatcher = MainThreadDispatcherImpl()
|
||||||
|
|
||||||
|
fun <T> deferLazilyOnMainThread(block: suspend CoroutineScope.() -> T): Deferred<T> {
|
||||||
|
return async(context = mainThreadDispatcher, start = CoroutineStart.LAZY, block = block)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> deferUndispatchedOnMainThread(block: suspend CoroutineScope.() -> T): Deferred<T> {
|
||||||
|
return async(context = mainThreadDispatcher, start = CoroutineStart.UNDISPATCHED, block = block)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun launchLazilyOnMainThread(block: suspend CoroutineScope.() -> Unit): Job {
|
||||||
|
return launch(context = mainThreadDispatcher, start = CoroutineStart.LAZY, block = block)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun schedule(noinline task: () -> Unit) = schedule(0, task)
|
||||||
|
|
||||||
|
fun schedule(delay: Int, task: () -> Unit): BukkitTask {
|
||||||
|
return plugin.server.scheduler.runTaskLater(plugin, task, delay.toLong())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun scheduleRepeating(delay: Int, interval: Int, task: () -> Unit): BukkitTask {
|
||||||
|
return plugin.server.scheduler.runTaskTimer(plugin, task, delay.toLong(), interval.toLong())
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class MainThreadDispatcher : CoroutineDispatcher() {
|
||||||
|
abstract val mainThread: Thread
|
||||||
|
abstract fun runOnMainThread(task: Runnable)
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class MainThreadDispatcherImpl : MainThreadDispatcher() {
|
||||||
|
override val mainThread: Thread = Thread.currentThread()
|
||||||
|
|
||||||
|
override fun dispatch(context: CoroutineContext, block: Runnable) {
|
||||||
|
runOnMainThread(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("OVERRIDE_BY_INLINE")
|
||||||
|
override inline fun runOnMainThread(task: Runnable) {
|
||||||
|
if (Thread.currentThread() === mainThread) task.run()
|
||||||
|
else plugin.server.scheduler.runTaskLater(plugin, task, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -6,21 +6,21 @@ import java.nio.ByteBuffer
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@Suppress("UsePropertyAccessSyntax")
|
@Suppress("UsePropertyAccessSyntax")
|
||||||
fun getPlayerName(uuid: UUID?, ifUnknown: String? = null): String {
|
fun getPlayerNameOrDefault(uuid: UUID?, ifUnknown: String? = null): String {
|
||||||
return uuid?.let { Bukkit.getOfflinePlayer(uuid)?.takeIf { it.isValid }?.name }
|
return uuid
|
||||||
|
?.let { getPlayerName(it) }
|
||||||
?: ifUnknown
|
?: ifUnknown
|
||||||
?: ":unknown_name:"
|
?: ":unknown_name:"
|
||||||
}
|
}
|
||||||
|
|
||||||
@Contract("null -> null; !null -> !null", pure = true)
|
fun getPlayerName(uuid: UUID): String? {
|
||||||
fun UUID?.toByteArray(): ByteArray? = this?.let {
|
return Bukkit.getOfflinePlayer(uuid)?.takeIf { it.isValid }?.name
|
||||||
|
}
|
||||||
|
|
||||||
|
fun UUID.toByteArray(): ByteArray =
|
||||||
ByteBuffer.allocate(16).apply {
|
ByteBuffer.allocate(16).apply {
|
||||||
putLong(mostSignificantBits)
|
putLong(mostSignificantBits)
|
||||||
putLong(leastSignificantBits)
|
putLong(leastSignificantBits)
|
||||||
}.array()
|
}.array()
|
||||||
}
|
|
||||||
|
|
||||||
@Contract("null -> null; !null -> !null", pure = true)
|
fun ByteArray.toUUID(): UUID = ByteBuffer.wrap(this).run { UUID(long, long) }
|
||||||
fun ByteArray?.toUUID(): UUID? = this?.let {
|
|
||||||
ByteBuffer.wrap(it).run { UUID(long, long) }
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -17,3 +17,35 @@ data class Vec3i(
|
|||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
inline operator fun World.get(vec: Vec3i): Block = getBlockAt(vec.x, vec.y, vec.z)
|
inline operator fun World.get(vec: Vec3i): Block = getBlockAt(vec.x, vec.y, vec.z)
|
||||||
|
|
||||||
|
/*
|
||||||
|
inline class IVec3i(private val data: Long) {
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val mask = 0x001F_FFFF
|
||||||
|
const val max: Int = 0x000F_FFFF // +1048575
|
||||||
|
const val min: Int = -max - 1 // -1048575 // 0xFFF0_0000
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
inline fun Int.compressIntoLong(offset: Int): Long {
|
||||||
|
if (this !in min..max) throw IllegalArgumentException()
|
||||||
|
return and(mask).toLong().shl(offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
inline fun Long.extractInt(offset: Int): Int {
|
||||||
|
return ushr(offset).toInt().and(mask)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(x: Int, y: Int, z: Int) : this(
|
||||||
|
x.compressIntoLong(42)
|
||||||
|
or y.compressIntoLong(21)
|
||||||
|
or z.compressIntoLong(0))
|
||||||
|
|
||||||
|
val x: Int get() = data.extractInt(42)
|
||||||
|
val y: Int get() = data.extractInt(21)
|
||||||
|
val z: Int get() = data.extractInt(0)
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user