summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDico <dico.karssiens@gmail.com>2018-08-05 06:46:11 +0100
committerDico <dico.karssiens@gmail.com>2018-08-05 06:46:11 +0100
commitba347a805333b84f860150cbf0b8c514472c6f9f (patch)
tree746a8f793ad96b09bffe25bf4fcd55afe969b0b7
parent3573f9ade67010e3e538151375faecbec32825c4 (diff)
Things be working
-rw-r--r--src/main/kotlin/io/dico/parcels2/AddedData.kt73
-rw-r--r--src/main/kotlin/io/dico/parcels2/Parcel.kt6
-rw-r--r--src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt7
-rw-r--r--src/main/kotlin/io/dico/parcels2/ParcelOwner.kt52
-rw-r--r--src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt1
-rw-r--r--src/main/kotlin/io/dico/parcels2/PlayerProfile.kt287
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt4
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandsAddedStatusGlobal.kt4
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt6
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt54
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt16
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalAddedDataManagerImpl.kt15
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelImpl.kt27
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt4
-rw-r--r--src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt5
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/Backing.kt41
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/Storage.kt59
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/exposed/CoroutineTransactionManager.kt118
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt145
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedExtensions.kt19
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt85
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt60
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt115
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/MiscExtensions.kt3
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/PlayerExtensions.kt2
-rw-r--r--src/main/resources/logback.xml38
26 files changed, 709 insertions, 537 deletions
diff --git a/src/main/kotlin/io/dico/parcels2/AddedData.kt b/src/main/kotlin/io/dico/parcels2/AddedData.kt
index 633fe72..9249c7f 100644
--- a/src/main/kotlin/io/dico/parcels2/AddedData.kt
+++ b/src/main/kotlin/io/dico/parcels2/AddedData.kt
@@ -1,41 +1,50 @@
package io.dico.parcels2
-import io.dico.parcels2.util.uuid
import org.bukkit.OfflinePlayer
-import java.util.UUID
-typealias MutableAddedDataMap = MutableMap<UUID, AddedStatus>
-typealias AddedDataMap = Map<UUID, AddedStatus>
+typealias StatusKey = PlayerProfile.Real
+typealias MutableAddedDataMap = MutableMap<StatusKey, AddedStatus>
+typealias AddedDataMap = Map<StatusKey, AddedStatus>
+
+@Suppress("FunctionName")
+fun MutableAddedDataMap(): MutableAddedDataMap = hashMapOf()
interface AddedData {
val addedMap: AddedDataMap
+ var addedStatusOfStar: AddedStatus
+
+ fun getAddedStatus(key: StatusKey): AddedStatus
+ fun setAddedStatus(key: StatusKey, status: AddedStatus): Boolean
+
+ fun compareAndSetAddedStatus(key: StatusKey, expect: AddedStatus, status: AddedStatus): Boolean =
+ (getAddedStatus(key) == expect).also { if (it) setAddedStatus(key, status) }
+
+ fun isAllowed(key: StatusKey) = getAddedStatus(key) == AddedStatus.ALLOWED
+ fun allow(key: StatusKey) = setAddedStatus(key, AddedStatus.ALLOWED)
+ fun disallow(key: StatusKey) = compareAndSetAddedStatus(key, AddedStatus.ALLOWED, AddedStatus.DEFAULT)
+ fun isBanned(key: StatusKey) = getAddedStatus(key) == AddedStatus.BANNED
+ fun ban(key: StatusKey) = setAddedStatus(key, AddedStatus.BANNED)
+ fun unban(key: StatusKey) = compareAndSetAddedStatus(key, AddedStatus.BANNED, AddedStatus.DEFAULT)
- 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)
+ fun isAllowed(player: OfflinePlayer) = isAllowed(player.statusKey)
+ fun allow(player: OfflinePlayer) = allow(player.statusKey)
+ fun disallow(player: OfflinePlayer) = disallow(player.statusKey)
+ fun isBanned(player: OfflinePlayer) = isBanned(player.statusKey)
+ fun ban(player: OfflinePlayer) = ban(player.statusKey)
+ fun unban(player: OfflinePlayer) = unban(player.statusKey)
}
+inline val OfflinePlayer.statusKey get() = PlayerProfile.nameless(this)
+
open class AddedDataHolder(override var addedMap: MutableAddedDataMap = mutableMapOf()) : AddedData {
- override fun getAddedStatus(uuid: UUID): AddedStatus = addedMap.getOrDefault(uuid, AddedStatus.DEFAULT)
- override fun setAddedStatus(uuid: UUID, status: AddedStatus): Boolean = status.takeIf { it != AddedStatus.DEFAULT }
- ?.let { addedMap.put(uuid, it) != it }
- ?: addedMap.remove(uuid) != null
+ override var addedStatusOfStar: AddedStatus = AddedStatus.DEFAULT
+
+ override fun getAddedStatus(key: StatusKey): AddedStatus = addedMap.getOrDefault(key, addedStatusOfStar)
+
+ override fun setAddedStatus(key: StatusKey, status: AddedStatus): Boolean {
+ return if (status.isDefault) addedMap.remove(key) != null
+ else addedMap.put(key, status) != status
+ }
}
enum class AddedStatus {
@@ -43,15 +52,15 @@ enum class AddedStatus {
ALLOWED,
BANNED;
- val isDefault get() = this == DEFAULT
- val isAllowed get() = this == ALLOWED
- val isBanned get() = this == BANNED
+ inline val isDefault get() = this == DEFAULT
+ inline val isAllowed get() = this == ALLOWED
+ inline val isBanned get() = this == BANNED
}
interface GlobalAddedData : AddedData {
- val owner: ParcelOwner
+ val owner: PlayerProfile
}
interface GlobalAddedDataManager {
- operator fun get(owner: ParcelOwner): GlobalAddedData
+ operator fun get(owner: PlayerProfile): GlobalAddedData
}
diff --git a/src/main/kotlin/io/dico/parcels2/Parcel.kt b/src/main/kotlin/io/dico/parcels2/Parcel.kt
index 6505a49..e7c5209 100644
--- a/src/main/kotlin/io/dico/parcels2/Parcel.kt
+++ b/src/main/kotlin/io/dico/parcels2/Parcel.kt
@@ -33,7 +33,7 @@ interface Parcel : ParcelData {
}
interface ParcelData : AddedData {
- var owner: ParcelOwner?
+ var owner: PlayerProfile?
val since: DateTime?
fun canBuild(player: OfflinePlayer, checkAdmin: Boolean = true, checkGlobal: Boolean = true): Boolean
@@ -48,9 +48,9 @@ interface ParcelData : AddedData {
class ParcelDataHolder(addedMap: MutableAddedDataMap = mutableMapOf()) : AddedDataHolder(addedMap), ParcelData {
- override var owner: ParcelOwner? = null
+ override var owner: PlayerProfile? = 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.statusKey)
|| owner.let { it != null && it.matches(player, allowNameMatch = false) }
|| (checkAdmin && player is Player && player.hasBuildAnywhere)
diff --git a/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt b/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt
index 6f504d0..b9d2e3d 100644
--- a/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt
+++ b/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt
@@ -3,8 +3,6 @@ package io.dico.parcels2
import io.dico.parcels2.blockvisitor.RegionTraversal
import io.dico.parcels2.blockvisitor.Worker
import io.dico.parcels2.blockvisitor.WorktimeLimiter
-import io.dico.parcels2.defaultimpl.DefaultParcelGenerator
-import io.dico.parcels2.options.GeneratorOptions
import io.dico.parcels2.util.Vec2i
import org.bukkit.Chunk
import org.bukkit.Location
@@ -14,9 +12,7 @@ import org.bukkit.block.Block
import org.bukkit.entity.Entity
import org.bukkit.generator.BlockPopulator
import org.bukkit.generator.ChunkGenerator
-import java.util.HashMap
import java.util.Random
-import kotlin.reflect.KClass
abstract class ParcelGenerator : ChunkGenerator() {
abstract val world: World
@@ -40,6 +36,7 @@ abstract class ParcelGenerator : ChunkGenerator() {
abstract fun makeParcelLocator(container: ParcelContainer): ParcelLocator
}
+@Suppress("DeprecatedCallableAddReplaceWith")
interface ParcelBlockManager {
val world: World
val worktimeLimiter: WorktimeLimiter
@@ -48,7 +45,7 @@ interface ParcelBlockManager {
fun getHomeLocation(parcel: ParcelId): Location
- fun setOwnerBlock(parcel: ParcelId, owner: ParcelOwner?)
+ fun setOwnerBlock(parcel: ParcelId, owner: PlayerProfile?)
@Deprecated("")
fun getEntities(parcel: ParcelId): Collection<Entity> = TODO()
diff --git a/src/main/kotlin/io/dico/parcels2/ParcelOwner.kt b/src/main/kotlin/io/dico/parcels2/ParcelOwner.kt
deleted file mode 100644
index 5a36cac..0000000
--- a/src/main/kotlin/io/dico/parcels2/ParcelOwner.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-@file:Suppress("unused", "UsePropertyAccessSyntax", "DEPRECATION")
-
-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.UUID
-
-class ParcelOwner(val uuid: UUID?,
- val name: String?) {
- val notNullName: String by lazy { name ?: getPlayerNameOrDefault(uuid!!) }
-
- 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.getPlayerExact(name) }
- val offlinePlayer: OfflinePlayer? get() = uuid?.let { Bukkit.getOfflinePlayer(it).takeIf { it.isValid } }
- val offlinePlayerAllowingNameMatch: OfflinePlayer?
- get() = offlinePlayer ?: 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()
- }
-
-} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt b/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt
index f0b5fbc..e9c70f6 100644
--- a/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt
+++ b/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt
@@ -42,6 +42,7 @@ class ParcelsPlugin : JavaPlugin() {
override fun onEnable() {
plogger.info("Debug enabled: ${plogger.isDebugEnabled}")
+ plogger.debug(System.getProperty("user.dir"))
if (!init()) {
Bukkit.getPluginManager().disablePlugin(this)
}
diff --git a/src/main/kotlin/io/dico/parcels2/PlayerProfile.kt b/src/main/kotlin/io/dico/parcels2/PlayerProfile.kt
new file mode 100644
index 0000000..c5403cd
--- /dev/null
+++ b/src/main/kotlin/io/dico/parcels2/PlayerProfile.kt
@@ -0,0 +1,287 @@
+@file:Suppress("unused", "UsePropertyAccessSyntax", "DEPRECATION")
+
+package io.dico.parcels2
+
+import io.dico.parcels2.storage.Storage
+import io.dico.parcels2.util.getPlayerNameOrDefault
+import io.dico.parcels2.util.isValid
+import io.dico.parcels2.util.uuid
+import kotlinx.coroutines.experimental.Deferred
+import kotlinx.coroutines.experimental.Unconfined
+import kotlinx.coroutines.experimental.async
+import org.bukkit.Bukkit
+import org.bukkit.OfflinePlayer
+import java.util.UUID
+
+interface PlayerProfile {
+ val uuid: UUID? get() = null
+ val name: String?
+ val notNullName: String
+ val isStar: Boolean get() = false
+
+ fun matches(player: OfflinePlayer, allowNameMatch: Boolean = false): Boolean
+
+ fun equals(other: PlayerProfile): Boolean
+
+ override fun equals(other: Any?): Boolean
+ override fun hashCode(): Int
+
+ val isFake: Boolean get() = this is Fake
+ val isReal: Boolean get() = this is Real
+
+ companion object {
+ fun safe(uuid: UUID?, name: String?): PlayerProfile? {
+ if (uuid != null) return Real(uuid, name)
+ if (name != null) return invoke(name)
+ return null
+ }
+
+ operator fun invoke(uuid: UUID?, name: String?): PlayerProfile {
+ return safe(uuid, name) ?: throw IllegalArgumentException("One of uuid and name must not be null")
+ }
+
+ operator fun invoke(uuid: UUID): Real {
+ if (uuid == Star.uuid) return Star
+ return RealImpl(uuid, null)
+ }
+
+ operator fun invoke(name: String): PlayerProfile {
+ if (name == Star.name) return Star
+ return Fake(name)
+ }
+
+ operator fun invoke(player: OfflinePlayer): PlayerProfile {
+ return if (player.isValid) Real(player.uuid, player.name) else Fake(player.name)
+ }
+
+ fun nameless(player: OfflinePlayer): Real {
+ if (!player.isValid) throw IllegalArgumentException("The given OfflinePlayer is not valid")
+ return RealImpl(player.uuid, null)
+ }
+
+ fun byName(input: String, allowReal: Boolean = true, allowFake: Boolean = false): PlayerProfile {
+ if (!allowReal) {
+ if (!allowFake) throw IllegalArgumentException("at least one of allowReal and allowFake must be true")
+ return Fake(input)
+ }
+
+ if (input == Star.name) return Star
+
+ return Bukkit.getOfflinePlayer(input).takeIf { it.isValid }?.let { PlayerProfile(it) }
+ ?: Unresolved(input)
+ }
+ }
+
+ interface Real : PlayerProfile {
+ override val uuid: UUID
+ override val notNullName: String
+ get() = name ?: getPlayerNameOrDefault(uuid)
+
+ val player: OfflinePlayer? get() = Bukkit.getOfflinePlayer(uuid).takeIf { it.isValid }
+ val playerUnchecked: OfflinePlayer get() = Bukkit.getOfflinePlayer(uuid)
+
+ override fun matches(player: OfflinePlayer, allowNameMatch: Boolean): Boolean {
+ return uuid == player.uuid || (allowNameMatch && name?.let { it == player.name } == true)
+ }
+
+ override fun equals(other: PlayerProfile): Boolean {
+ return other is Real && uuid == other.uuid
+ }
+
+ companion object {
+ fun byName(name: String): PlayerProfile {
+ if (name == Star.name) return Star
+ return Unresolved(name)
+ }
+
+ operator fun invoke(uuid: UUID, name: String?): Real {
+ if (name == Star.name || uuid == Star.uuid) return Star
+ return RealImpl(uuid, name)
+ }
+
+ fun safe(uuid: UUID?, name: String?): Real? {
+ if (name == Star.name || uuid == Star.uuid) return Star
+ if (uuid == null) return null
+ return RealImpl(uuid, name)
+ }
+
+ }
+ }
+
+ object Star : BaseImpl(), Real {
+ override val name: String = "*"
+ override val uuid: UUID = UUID.fromString("7d09c4c6-117d-4f36-9778-c4d24618cee1")
+ override val isStar: Boolean get() = true
+
+ override fun matches(player: OfflinePlayer, allowNameMatch: Boolean): Boolean {
+ return true
+ }
+ }
+
+ abstract class NameOnly(override val name: String) : BaseImpl() {
+ override val notNullName get() = name
+
+ override fun matches(player: OfflinePlayer, allowNameMatch: Boolean): Boolean {
+ return allowNameMatch && player.name == name
+ }
+ }
+
+ class Fake(name: String) : NameOnly(name) {
+ override fun equals(other: PlayerProfile): Boolean {
+ return other is Fake && other.name == name
+ }
+ }
+
+ class Unresolved(name: String) : NameOnly(name) {
+ override fun equals(other: PlayerProfile): Boolean {
+ return other is Unresolved && name == other.name
+ }
+
+ fun tryResolve(storage: Storage): Deferred<Real?> {
+ return async(Unconfined) { tryResolveSuspendedly(storage) }
+ }
+
+ suspend fun tryResolveSuspendedly(storage: Storage): Real? {
+ return storage.getPlayerUuidForName(name).await()?.let { RealImpl(it, name) }
+ }
+
+ fun resolve(uuid: UUID): Real {
+ return RealImpl(uuid, name)
+ }
+
+ fun throwException(): Nothing {
+ throw IllegalArgumentException("A UUID for the player $name can not be found")
+ }
+ }
+
+ abstract class BaseImpl : PlayerProfile {
+ override fun equals(other: Any?): Boolean {
+ return this === other || (other is PlayerProfile && equals(other))
+ }
+
+ override fun hashCode(): Int {
+ return uuid?.hashCode() ?: name!!.hashCode()
+ }
+ }
+
+ private class RealImpl(override val uuid: UUID, override val name: String?) : BaseImpl(), Real
+
+}
+
+
+/*
+
+
+/**
+ * This class can represent:
+ *
+ * An existing player
+ * A fake player (with only a name)
+ * An existing player who must have its uuid resolved from the database (after checking against Bukkit OfflinePlayer)
+ * STAR profile, which matches everyone. This profile is considered a REAL player, because it can have an added status.
+ */
+class PlayerProfile2 private constructor(uuid: UUID?,
+ val name: String?,
+ val isReal: Boolean = uuid != null) {
+ private var _uuid: UUID? = uuid
+ val notNullName: String get() = name ?: getPlayerNameOrDefault(uuid!!)
+
+ val uuid: UUID? get() = _uuid ?: if (isReal) throw IllegalStateException("This PlayerProfile must be resolved first") else null
+
+ companion object {
+ // below uuid is just a randomly generated one (version 4). Hopefully no minecraft player will ever have it :)
+ val star = PlayerProfile(UUID.fromString("7d09c4c6-117d-4f36-9778-c4d24618cee1"), "*", true)
+
+ fun nameless(player: OfflinePlayer): PlayerProfile {
+ if (!player.isValid) throw IllegalArgumentException("The given OfflinePlayer is not valid")
+ return PlayerProfile(player.uuid)
+ }
+
+ fun fromNameAndUuid(name: String?, uuid: UUID?): PlayerProfile? {
+ if (name == null && uuid == null) return null
+ if (star.name == name && star._uuid == uuid) return star
+ return PlayerProfile(uuid, name)
+ }
+
+ fun realPlayerByName(name: String): PlayerProfile {
+ return fromString(name, allowReal = true, allowFake = false)
+ }
+
+ fun fromString(input: String, allowReal: Boolean = true, allowFake: Boolean = false): PlayerProfile {
+ if (!allowReal) {
+ if (!allowFake) throw IllegalArgumentException("at least one of allowReal and allowFake must be true")
+ return PlayerProfile(input)
+ }
+
+ if (input == star.name) return star
+
+ return Bukkit.getOfflinePlayer(input).takeIf { it.isValid }?.let { PlayerProfile(it) }
+ ?: PlayerProfile(null, input, !allowFake)
+ }
+
+ operator fun invoke(name: String): PlayerProfile {
+ if (name == star.name) return star
+ return PlayerProfile(null, name)
+ }
+
+ operator fun invoke(uuid: UUID): PlayerProfile {
+ if (uuid == star.uuid) return star
+ return PlayerProfile(uuid, null)
+ }
+
+ operator fun invoke(player: OfflinePlayer): PlayerProfile {
+ // avoid UUID comparison against STAR
+ return if (player.isValid) PlayerProfile(player.uuid, player.name) else invoke(player.name)
+ }
+ }
+
+ val isStar: Boolean get() = this === star || (name == star.name && _uuid == star._uuid)
+ val hasUUID: Boolean get() = _uuid != null
+ val mustBeResolved: Boolean get() = isReal && _uuid == null
+
+ val onlinePlayer: Player? get() = uuid?.let { Bukkit.getPlayer(uuid) }
+
+ val onlinePlayerAllowingNameMatch: Player? get() = onlinePlayer ?: name?.let { Bukkit.getPlayerExact(name) }
+ val offlinePlayer: OfflinePlayer? get() = uuid?.let { Bukkit.getOfflinePlayer(it).takeIf { it.isValid } }
+ val offlinePlayerAllowingNameMatch: OfflinePlayer?
+ get() = offlinePlayer ?: Bukkit.getOfflinePlayer(name).takeIf { it.isValid }
+
+ fun matches(player: OfflinePlayer, allowNameMatch: Boolean = false): Boolean {
+ if (isStar) return true
+ return uuid?.let { it == player.uniqueId } ?: false
+ || (allowNameMatch && name?.let { it == player.name } ?: false)
+ }
+
+ fun equals(other: PlayerProfile): Boolean {
+ return if (_uuid != null) _uuid == other._uuid
+ else other._uuid == null && isReal == other.isReal && name == other.name
+ }
+
+ override fun equals(other: Any?): Boolean {
+ return other is PlayerProfile && equals(other)
+ }
+
+ override fun hashCode(): Int {
+ return _uuid?.hashCode() ?: name!!.hashCode()
+ }
+
+ /**
+ * resolve the uuid of this player profile if [mustBeResolved], using specified [storage].
+ * returns true if the PlayerProfile has a uuid after this call.
+ */
+ suspend fun resolve(storage: Storage): Boolean {
+ if (mustBeResolved) {
+ val uuid = storage.getPlayerUuidForName(name!!).await()
+ _uuid = uuid
+ return uuid != null
+ }
+ return _uuid != null
+ }
+
+ fun resolve(uuid: UUID) {
+ if (isReal && _uuid == null) {
+ _uuid = uuid
+ }
+ }
+}
+*/
diff --git a/src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt b/src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt
index 2fceb5b..31a551a 100644
--- a/src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt
@@ -3,7 +3,7 @@ package io.dico.parcels2.command
import io.dico.dicore.command.CommandException
import io.dico.dicore.command.ExecutionContext
import io.dico.dicore.command.ICommandReceiver
-import io.dico.parcels2.ParcelOwner
+import io.dico.parcels2.PlayerProfile
import io.dico.parcels2.ParcelWorld
import io.dico.parcels2.ParcelsPlugin
import io.dico.parcels2.util.hasAdminManage
@@ -31,7 +31,7 @@ abstract class AbstractParcelCommands(val plugin: ParcelsPlugin) : ICommandRecei
protected suspend fun checkParcelLimit(player: Player, world: ParcelWorld) {
if (player.hasAdminManage) return
- val numOwnedParcels = plugin.storage.getOwnedParcels(ParcelOwner(player)).await()
+ val numOwnedParcels = plugin.storage.getOwnedParcels(PlayerProfile(player)).await()
.filter { it.worldId.equals(world.id) }.size
val limit = player.parcelLimit
diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsAddedStatusGlobal.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsAddedStatusGlobal.kt
index d483126..eae6da2 100644
--- a/src/main/kotlin/io/dico/parcels2/command/CommandsAddedStatusGlobal.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/CommandsAddedStatusGlobal.kt
@@ -5,7 +5,7 @@ import io.dico.dicore.command.annotation.Cmd
import io.dico.dicore.command.annotation.Desc
import io.dico.parcels2.GlobalAddedData
import io.dico.parcels2.GlobalAddedDataManager
-import io.dico.parcels2.ParcelOwner
+import io.dico.parcels2.PlayerProfile
import io.dico.parcels2.ParcelsPlugin
import org.bukkit.OfflinePlayer
import org.bukkit.entity.Player
@@ -13,7 +13,7 @@ import org.bukkit.entity.Player
class CommandsAddedStatusGlobal(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
private inline val data get() = plugin.globalAddedData
@Suppress("NOTHING_TO_INLINE")
- private inline operator fun GlobalAddedDataManager.get(player: OfflinePlayer): GlobalAddedData = data[ParcelOwner(player)]
+ private inline operator fun GlobalAddedDataManager.get(player: OfflinePlayer): GlobalAddedData = data[PlayerProfile(player)]
@Cmd("allow", aliases = ["add", "permit"])
@Desc("Globally allows a player to build on all",
diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt
index 5395b1c..7442019 100644
--- a/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt
@@ -6,7 +6,7 @@ import io.dico.dicore.command.annotation.Cmd
import io.dico.dicore.command.annotation.Desc
import io.dico.dicore.command.annotation.Flag
import io.dico.dicore.command.annotation.RequireParameters
-import io.dico.parcels2.ParcelOwner
+import io.dico.parcels2.PlayerProfile
import io.dico.parcels2.ParcelsPlugin
import io.dico.parcels2.util.hasAdminManage
import io.dico.parcels2.util.hasParcelHomeOthers
@@ -25,7 +25,7 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
val parcel = world.nextEmptyParcel()
?: error("This world is full, please ask an admin to upsize it")
- parcel.owner = ParcelOwner(uuid = player.uuid)
+ parcel.owner = PlayerProfile(uuid = player.uuid)
player.teleport(parcel.world.getHomeLocation(parcel.id))
return "Enjoy your new parcel!"
}
@@ -72,7 +72,7 @@ class CommandsGeneral(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
}
checkParcelLimit(player, world)
- parcel.owner = ParcelOwner(player)
+ parcel.owner = PlayerProfile(player)
return "Enjoy your new parcel!"
}
diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt
index 5504e6b..cbab80a 100644
--- a/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt
@@ -7,14 +7,14 @@ import io.dico.dicore.command.parameter.type.ParameterType
import io.dico.parcels2.*
import io.dico.parcels2.util.Vec2i
import io.dico.parcels2.util.floor
-import io.dico.parcels2.util.isValid
import kotlinx.coroutines.experimental.Deferred
-import org.bukkit.Bukkit
import org.bukkit.command.CommandSender
import org.bukkit.entity.Player
sealed class ParcelTarget(val world: ParcelWorld, val isDefault: Boolean) {
+
abstract suspend fun ParcelsPlugin.getParcelSuspend(): Parcel?
+
fun ParcelsPlugin.getParcelDeferred(): Deferred<Parcel?> = functionHelper.deferUndispatchedOnMainThread { getParcelSuspend() }
class ByID(world: ParcelWorld, val id: Vec2i?, isDefault: Boolean) : ParcelTarget(world, isDefault) {
@@ -23,16 +23,35 @@ sealed class ParcelTarget(val world: ParcelWorld, val isDefault: Boolean) {
val isPath: Boolean get() = id == null
}
- class ByOwner(world: ParcelWorld, val owner: ParcelOwner, val index: Int, isDefault: Boolean) : ParcelTarget(world, isDefault) {
+ class ByOwner(world: ParcelWorld,
+ owner: PlayerProfile,
+ val index: Int,
+ isDefault: Boolean,
+ val onResolveFailure: (() -> Unit)? = null) : ParcelTarget(world, isDefault) {
init {
if (index < 0) throw IllegalArgumentException("Invalid parcel home index: $index")
}
+ var owner = owner; private set
+
override suspend fun ParcelsPlugin.getParcelSuspend(): Parcel? {
+ onResolveFailure?.let { onFail ->
+ val owner = owner
+ if (owner is PlayerProfile.Unresolved) {
+ val new = owner.tryResolveSuspendedly(storage)
+ if (new == null) {
+ onFail()
+ return@let
+ }
+ this@ByOwner.owner = new
+ }
+ }
+
val ownedParcelsSerialized = storage.getOwnedParcels(owner).await()
val ownedParcels = ownedParcelsSerialized
.map { parcelProvider.getParcelById(it) }
.filter { it != null && world == it.world && owner == it.owner }
+
return ownedParcels.getOrNull(index)
}
}
@@ -80,8 +99,8 @@ sealed class ParcelTarget(val world: ParcelWorld, val isDefault: Boolean) {
}
if (kind and OWNER == 0) invalidInput(parameter, "You must specify a parcel by ID, that is, the x and z component separated by a comma")
- val (owner, index) = getHomeIndex(parameter, sender, input)
- return ByOwner(world, owner, index, false)
+ val (owner, index) = getHomeIndex(parameter, kind, sender, input)
+ return ByOwner(world, owner, index, false, onResolveFailure = { invalidInput(parameter, "The player $input does not exist") })
}
private fun getId(parameter: Parameter<*, *>, input: String): Vec2i {
@@ -94,7 +113,7 @@ sealed class ParcelTarget(val world: ParcelWorld, val isDefault: Boolean) {
return Vec2i(x, z)
}
- private fun getHomeIndex(parameter: Parameter<*, Int>, sender: CommandSender, input: String): Pair<ParcelOwner, Int> {
+ private fun getHomeIndex(parameter: Parameter<*, *>, kind: Int, sender: CommandSender, input: String): Pair<PlayerProfile, Int> {
val splitIdx = input.indexOf(':')
val ownerString: String
val indexString: String
@@ -109,9 +128,9 @@ sealed class ParcelTarget(val world: ParcelWorld, val isDefault: Boolean) {
}
val owner = if (ownerString.isEmpty())
- ParcelOwner(requirePlayer(sender, parameter, "the player"))
+ PlayerProfile(requirePlayer(sender, parameter, "the player"))
else
- inputAsOwner(parameter, ownerString)
+ PlayerProfile.byName(ownerString, allowReal = kind and OWNER_REAL != 0, allowFake = kind and OWNER_FAKE != 0)
val index = if (indexString.isEmpty()) 0 else indexString.toIntOrNull()
?: invalidInput(parameter, "The home index must be an integer, $indexString is not an integer")
@@ -124,22 +143,6 @@ sealed class ParcelTarget(val world: ParcelWorld, val isDefault: Boolean) {
return sender
}
- @Suppress("DEPRECATION")
- private fun inputAsOwner(parameter: Parameter<*, Int>, input: String): ParcelOwner {
- val kind = parameter.paramInfo ?: DEFAULT_KIND
- if (kind and OWNER_REAL == 0) {
- return ParcelOwner(input)
- }
-
- val player = Bukkit.getOfflinePlayer(input).takeIf { it.isValid }
- if (player == null) {
- if (kind and OWNER_FAKE == 0) invalidInput(parameter, "The player $input does not exist")
- return ParcelOwner(input)
- }
-
- return ParcelOwner(player)
- }
-
override fun getDefaultValue(parameter: Parameter<ParcelTarget, Int>, sender: CommandSender, buffer: ArgumentBuffer): ParcelTarget? {
val kind = parameter.paramInfo ?: DEFAULT_KIND
val useLocation = when {
@@ -156,7 +159,8 @@ sealed class ParcelTarget(val world: ParcelWorld, val isDefault: Boolean) {
return ByID(world, id, true)
}
- return ByOwner(world, ParcelOwner(player), 0, true)
+ return ByOwner(world, PlayerProfile(player), 0, true)
}
}
+
}
diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt
index 3b6bfb5..7e75f52 100644
--- a/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt
+++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt
@@ -119,7 +119,7 @@ class DefaultParcelGenerator(val name: String, private val o: DefaultGeneratorOp
val modX = absX umod sectionSize
val modZ = absZ umod sectionSize
if (modX in 0 until parcelSize && modZ in 0 until parcelSize) {
- return mapper((absX - modX) / sectionSize, (absZ - modZ) / sectionSize)
+ return mapper((absX - modX) / sectionSize + 1, (absZ - modZ) / sectionSize + 1)
}
return null
}
@@ -140,16 +140,18 @@ class DefaultParcelGenerator(val name: String, private val o: DefaultGeneratorOp
override val world: World = this@DefaultParcelGenerator.world
override fun getBottomBlock(parcel: ParcelId): Vec2i = Vec2i(
- sectionSize * parcel.pos.x + pathOffset + o.offsetX,
- sectionSize * parcel.pos.z + pathOffset + o.offsetZ
+ sectionSize * (parcel.x - 1) + pathOffset + o.offsetX,
+ sectionSize * (parcel.z - 1) + pathOffset + o.offsetZ
)
override fun getHomeLocation(parcel: ParcelId): Location {
val bottom = getBottomBlock(parcel)
- return Location(world, bottom.x.toDouble() + 0.5, o.floorHeight + 1.0, bottom.z + 0.5 + (o.parcelSize - 1) / 2.0, -90F, 0F)
+ val x = bottom.x + (o.parcelSize - 1) / 2.0
+ val z = bottom.z - 2
+ return Location(world, x + 0.5, o.floorHeight + 1.0, z + 0.5, 0F, 0F)
}
- override fun setOwnerBlock(parcel: ParcelId, owner: ParcelOwner?) {
+ override fun setOwnerBlock(parcel: ParcelId, owner: PlayerProfile?) {
val b = getBottomBlock(parcel)
val wallBlock = world.getBlockAt(b.x - 1, o.floorHeight + 1, b.z - 1)
@@ -178,8 +180,8 @@ class DefaultParcelGenerator(val name: String, private val o: DefaultGeneratorOp
skullBlock.type = Material.PLAYER_HEAD
val skull = skullBlock.state as Skull
- if (owner.uuid != null) {
- skull.owningPlayer = owner.offlinePlayer
+ if (owner is PlayerProfile.Real) {
+ skull.owningPlayer = owner.playerUnchecked
} else {
skull.owner = owner.name
}
diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalAddedDataManagerImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalAddedDataManagerImpl.kt
index 1ac053f..ba54375 100644
--- a/src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalAddedDataManagerImpl.kt
+++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalAddedDataManagerImpl.kt
@@ -3,33 +3,32 @@
package io.dico.parcels2.defaultimpl
import io.dico.parcels2.*
+import io.dico.parcels2.util.alsoIfTrue
import java.util.Collections
-import java.util.UUID
class GlobalAddedDataManagerImpl(val plugin: ParcelsPlugin) : GlobalAddedDataManager {
- private val map = mutableMapOf<ParcelOwner, GlobalAddedData>()
+ private val map = mutableMapOf<PlayerProfile, GlobalAddedData>()
- override fun get(owner: ParcelOwner): GlobalAddedData {
+ override fun get(owner: PlayerProfile): GlobalAddedData {
return map[owner] ?: GlobalAddedDataImpl(owner).also { map[owner] = it }
}
- private inner class GlobalAddedDataImpl(override val owner: ParcelOwner,
+ private inner class GlobalAddedDataImpl(override val owner: PlayerProfile,
data: MutableAddedDataMap = emptyData)
: AddedDataHolder(data), GlobalAddedData {
private inline var data get() = addedMap; set(value) = run { addedMap = value }
private inline val isEmpty get() = data === emptyData
- override fun setAddedStatus(uuid: UUID, status: AddedStatus): Boolean {
+ override fun setAddedStatus(key: StatusKey, status: AddedStatus): Boolean {
if (isEmpty) {
if (status == AddedStatus.DEFAULT) return false
data = mutableMapOf()
}
- return super.setAddedStatus(uuid, status).also {
- if (it) plugin.storage.setGlobalAddedStatus(owner, uuid, status)
+ return super.setAddedStatus(key, status).alsoIfTrue {
+ plugin.storage.setGlobalAddedStatus(owner, key, status)
}
}
-
}
private companion object {
diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelImpl.kt
index c3d7c22..128705f 100644
--- a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelImpl.kt
+++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelImpl.kt
@@ -3,6 +3,7 @@ package io.dico.parcels2.defaultimpl
import io.dico.dicore.Formatting
import io.dico.parcels2.*
import io.dico.parcels2.util.Vec2i
+import io.dico.parcels2.util.alsoIfTrue
import io.dico.parcels2.util.getPlayerName
import org.bukkit.OfflinePlayer
import org.joda.time.DateTime
@@ -33,20 +34,24 @@ class ParcelImpl(override val world: ParcelWorld,
world.storage.setParcelData(this, null)
}
- override val addedMap: Map<UUID, AddedStatus> get() = data.addedMap
- override fun getAddedStatus(uuid: UUID) = data.getAddedStatus(uuid)
- override fun isBanned(uuid: UUID) = data.isBanned(uuid)
- override fun isAllowed(uuid: UUID) = data.isAllowed(uuid)
+ override val addedMap: AddedDataMap get() = data.addedMap
+ override fun getAddedStatus(key: StatusKey) = data.getAddedStatus(key)
+ override fun isBanned(key: StatusKey) = data.isBanned(key)
+ override fun isAllowed(key: StatusKey) = data.isAllowed(key)
override fun canBuild(player: OfflinePlayer, checkAdmin: Boolean, checkGlobal: Boolean): Boolean {
return (data.canBuild(player, checkAdmin, false))
|| checkGlobal && world.globalAddedData[owner ?: return false].isAllowed(player)
}
- val globalAddedMap: Map<UUID, AddedStatus>? get() = owner?.let { world.globalAddedData[it].addedMap }
+ override var addedStatusOfStar: AddedStatus
+ get() = data.addedStatusOfStar
+ set(value) = run { setAddedStatus(PlayerProfile.Star, value) }
+
+ val globalAddedMap: AddedDataMap? get() = owner?.let { world.globalAddedData[it].addedMap }
override val since: DateTime? get() = data.since
- override var owner: ParcelOwner?
+ override var owner: PlayerProfile?
get() = data.owner
set(value) {
if (data.owner != value) {
@@ -55,9 +60,9 @@ class ParcelImpl(override val world: ParcelWorld,
}
}
- override fun setAddedStatus(uuid: UUID, status: AddedStatus): Boolean {
- return data.setAddedStatus(uuid, status).also {
- if (it) world.storage.setParcelPlayerStatus(this, uuid, status)
+ override fun setAddedStatus(key: StatusKey, status: AddedStatus): Boolean {
+ return data.setAddedStatus(key, status).alsoIfTrue {
+ world.storage.setParcelPlayerStatus(this, key, status)
}
}
@@ -102,10 +107,10 @@ private object ParcelInfoStringComputer {
append(' ')
}
- private fun StringBuilder.appendAddedList(local: Map<UUID, AddedStatus>, global: Map<UUID, AddedStatus>, status: AddedStatus, fieldName: String) {
+ private fun StringBuilder.appendAddedList(local: AddedDataMap, global: AddedDataMap, status: AddedStatus, fieldName: String) {
val globalSet = global.filterValues { it == status }.keys
val localList = local.filterValues { it == status }.keys.filter { it !in globalSet }
- val stringList = globalSet.map(::getPlayerName).map { "(G)$it" } + localList.map(::getPlayerName)
+ val stringList = globalSet.map(StatusKey::notNullName).map { "(G)$it" } + localList.map(StatusKey::notNullName)
if (stringList.isEmpty()) return
appendField({
diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt
index f7abccd..70d428d 100644
--- a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt
+++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt
@@ -75,7 +75,7 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
}
logger.info("Loading all parcel data...")
- val channel = plugin.storage.readAllParcelData()
+ val channel = plugin.storage.transmitAllParcelData()
do {
val pair = channel.receiveOrNull() ?: break
val parcel = getParcelById(pair.first) ?: continue
@@ -119,7 +119,7 @@ class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
}
}
- val channel = plugin.storage.readAllParcelData()
+ val channel = plugin.storage.transmitAllParcelData()
val job = plugin.functionHelper.launchLazilyOnMainThread {
do {
val pair = channel.receiveOrNull() ?: break
diff --git a/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt b/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt
index b2637f5..fa5ccb4 100644
--- a/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt
+++ b/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt
@@ -6,6 +6,7 @@ import io.dico.dicore.RegistratorListener
import io.dico.parcels2.Parcel
import io.dico.parcels2.ParcelProvider
import io.dico.parcels2.ParcelWorld
+import io.dico.parcels2.statusKey
import io.dico.parcels2.util.*
import org.bukkit.Material.*
import org.bukkit.World
@@ -51,7 +52,7 @@ class ParcelListeners(val parcelProvider: ParcelProvider, val entityTracker: Par
val user = event.player
if (user.hasBanBypass) return@l
val parcel = parcelProvider.getParcelAt(event.to) ?: return@l
- if (parcel.isBanned(user.uuid)) {
+ if (parcel.isBanned(user.statusKey)) {
parcelProvider.getParcelAt(event.from)?.also {
user.teleport(it.world.getHomeLocation(it.id))
user.sendParcelMessage(nopermit = true, message = "You are banned from this parcel")
@@ -178,7 +179,7 @@ class ParcelListeners(val parcelProvider: ParcelProvider, val entityTracker: Par
val clickedBlock = event.clickedBlock
val parcel = clickedBlock?.let { world.getParcelAt(it) }
- if (!user.hasBuildAnywhere && parcel.isPresentAnd { isBanned(user.uuid) }) {
+ if (!user.hasBuildAnywhere && parcel.isPresentAnd { isBanned(user.statusKey) }) {
user.sendParcelMessage(nopermit = true, message = "You cannot interact with parcels you're banned from")
event.isCancelled = true; return@l
}
diff --git a/src/main/kotlin/io/dico/parcels2/storage/Backing.kt b/src/main/kotlin/io/dico/parcels2/storage/Backing.kt
index bb4cf33..b658d10 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/Backing.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/Backing.kt
@@ -2,10 +2,8 @@ package io.dico.parcels2.storage
import io.dico.parcels2.*
import kotlinx.coroutines.experimental.CoroutineDispatcher
-import kotlinx.coroutines.experimental.CoroutineScope
import kotlinx.coroutines.experimental.Deferred
import kotlinx.coroutines.experimental.Job
-import kotlinx.coroutines.experimental.channels.ProducerScope
import kotlinx.coroutines.experimental.channels.ReceiveChannel
import kotlinx.coroutines.experimental.channels.SendChannel
import java.util.UUID
@@ -16,58 +14,49 @@ interface Backing {
val isConnected: Boolean
+ val dispatcher: CoroutineDispatcher
+
fun launchJob(job: Backing.() -> Unit): Job
fun <T> launchFuture(future: Backing.() -> T): Deferred<T>
fun <T> openChannel(future: Backing.(SendChannel<T>) -> Unit): ReceiveChannel<T>
+ fun <T> openChannelForWriting(future: Backing.(T) -> Unit): SendChannel<T>
+
fun init()
fun shutdown()
- /**
- * This producer function is capable of constantly reading parcels from a potentially infinite sequence,
- * and provide parcel data for it as read from the database.
- */
- fun produceParcelData(channel: SendChannel<DataPair>, parcels: Sequence<ParcelId>)
+ fun getPlayerUuidForName(name: String): UUID?
- fun produceAllParcelData(channel: SendChannel<DataPair>)
+ fun transmitParcelData(channel: SendChannel<DataPair>, parcels: Sequence<ParcelId>)
+
+ fun transmitAllParcelData(channel: SendChannel<DataPair>)
fun readParcelData(parcel: ParcelId): ParcelData?
- fun getOwnedParcels(user: ParcelOwner): List<ParcelId>
+ fun getOwnedParcels(user: PlayerProfile): List<ParcelId>
- fun getNumParcels(user: ParcelOwner): Int = getOwnedParcels(user).size
+ fun getNumParcels(user: PlayerProfile): Int = getOwnedParcels(user).size
fun setParcelData(parcel: ParcelId, data: ParcelData?)
- fun setParcelOwner(parcel: ParcelId, owner: ParcelOwner?)
+ fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?)
- fun setLocalPlayerStatus(parcel: ParcelId, player: UUID, status: AddedStatus)
+ fun setLocalPlayerStatus(parcel: ParcelId, player: PlayerProfile, status: AddedStatus)
fun setParcelAllowsInteractInventory(parcel: ParcelId, value: Boolean)
fun setParcelAllowsInteractInputs(parcel: ParcelId, value: Boolean)
- fun produceAllGlobalAddedData(channel: SendChannel<AddedDataPair<ParcelOwner>>)
-
- fun readGlobalAddedData(owner: ParcelOwner): MutableAddedDataMap
-
- fun setGlobalPlayerStatus(owner: ParcelOwner, player: UUID, status: AddedStatus)
-
-}
+ fun transmitAllGlobalAddedData(channel: SendChannel<AddedDataPair<PlayerProfile>>)
-abstract class AbstractBacking(val dispatcher: CoroutineDispatcher) {
-
- fun launchJob(job: Backing.() -> Unit): Job
-
- fun <T> launchFuture(future: Backing.() -> T): Deferred<T>
-
- fun <T> openChannel(future: Backing.(SendChannel<T>) -> Unit): ReceiveChannel<T>
+ fun readGlobalAddedData(owner: PlayerProfile): MutableAddedDataMap
+ fun setGlobalPlayerStatus(owner: PlayerProfile, player: PlayerProfile, status: AddedStatus)
}
diff --git a/src/main/kotlin/io/dico/parcels2/storage/Storage.kt b/src/main/kotlin/io/dico/parcels2/storage/Storage.kt
index 6770d99..2116b46 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/Storage.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/Storage.kt
@@ -6,6 +6,8 @@ import io.dico.parcels2.*
import kotlinx.coroutines.experimental.Deferred
import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.channels.ReceiveChannel
+import kotlinx.coroutines.experimental.channels.SendChannel
+import kotlinx.coroutines.experimental.launch
import java.util.UUID
typealias DataPair = Pair<ParcelId, ParcelData?>
@@ -20,68 +22,77 @@ interface Storage {
fun shutdown(): Job
+ fun getPlayerUuidForName(name: String): Deferred<UUID?>
+
fun readParcelData(parcel: ParcelId): Deferred<ParcelData?>
- fun readParcelData(parcels: Sequence<ParcelId>): ReceiveChannel<DataPair>
+ fun transmitParcelData(parcels: Sequence<ParcelId>): ReceiveChannel<DataPair>
- fun readAllParcelData(): ReceiveChannel<DataPair>
+ fun transmitAllParcelData(): ReceiveChannel<DataPair>
- fun getOwnedParcels(user: ParcelOwner): Deferred<List<ParcelId>>
+ fun getOwnedParcels(user: PlayerProfile): Deferred<List<ParcelId>>
- fun getNumParcels(user: ParcelOwner): Deferred<Int>
+ fun getNumParcels(user: PlayerProfile): Deferred<Int>
fun setParcelData(parcel: ParcelId, data: ParcelData?): Job
- fun setParcelOwner(parcel: ParcelId, owner: ParcelOwner?): Job
+ fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?): Job
- fun setParcelPlayerStatus(parcel: ParcelId, player: UUID, status: AddedStatus): Job
+ fun setParcelPlayerStatus(parcel: ParcelId, player: PlayerProfile, status: AddedStatus): Job
fun setParcelAllowsInteractInventory(parcel: ParcelId, value: Boolean): Job
fun setParcelAllowsInteractInputs(parcel: ParcelId, value: Boolean): Job
- fun readAllGlobalAddedData(): ReceiveChannel<AddedDataPair<ParcelOwner>>
+ fun transmitAllGlobalAddedData(): ReceiveChannel<AddedDataPair<PlayerProfile>>
+
+ fun readGlobalAddedData(owner: PlayerProfile): Deferred<MutableAddedDataMap?>
+
+ fun setGlobalAddedStatus(owner: PlayerProfile, player: PlayerProfile, status: AddedStatus): Job
- fun readGlobalAddedData(owner: ParcelOwner): Deferred<MutableAddedDataMap?>
- fun setGlobalAddedStatus(owner: ParcelOwner, player: UUID, status: AddedStatus): Job
+ fun getChannelToUpdateParcelData(): SendChannel<Pair<ParcelId, ParcelData>>
}
class BackedStorage internal constructor(val b: Backing) : Storage {
override val name get() = b.name
override val isConnected get() = b.isConnected
- override fun init() = b.launchJob { init() }
+ override fun init() = launch(b.dispatcher) { b.init() }
+
+ override fun shutdown() = launch(b.dispatcher) { b.shutdown() }
+
- override fun shutdown() = b.launchJob { shutdown() }
+ override fun getPlayerUuidForName(name: String): Deferred<UUID?> = b.launchFuture { b.getPlayerUuidForName(name) }
+ override fun readParcelData(parcel: ParcelId) = b.launchFuture { b.readParcelData(parcel) }
- override fun readParcelData(parcel: ParcelId) = b.launchFuture { readParcelData(parcel) }
+ override fun transmitParcelData(parcels: Sequence<ParcelId>) = b.openChannel<DataPair> { b.transmitParcelData(it, parcels) }
- override fun readParcelData(parcels: Sequence<ParcelId>) = b.openChannel<DataPair> { produceParcelData(it, parcels) }
+ override fun transmitAllParcelData() = b.openChannel<DataPair> { b.transmitAllParcelData(it) }
- override fun readAllParcelData() = b.openChannel<DataPair> { produceAllParcelData(it) }
+ override fun getOwnedParcels(user: PlayerProfile) = b.launchFuture { b.getOwnedParcels(user) }
- override fun getOwnedParcels(user: ParcelOwner) = b.launchFuture { getOwnedParcels(user) }
+ override fun getNumParcels(user: PlayerProfile) = b.launchFuture { b.getNumParcels(user) }
- override fun getNumParcels(user: ParcelOwner) = b.launchFuture { getNumParcels(user) }
+ override fun setParcelData(parcel: ParcelId, data: ParcelData?) = b.launchJob { b.setParcelData(parcel, data) }
- override fun setParcelData(parcel: ParcelId, data: ParcelData?) = b.launchJob { setParcelData(parcel, data) }
+ override fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?) = b.launchJob { b.setParcelOwner(parcel, owner) }
- override fun setParcelOwner(parcel: ParcelId, owner: ParcelOwner?) = b.launchJob { setParcelOwner(parcel, owner) }
+ override fun setParcelPlayerStatus(parcel: ParcelId, player: PlayerProfile, status: AddedStatus) = b.launchJob { b.setLocalPlayerStatus(parcel, player, status) }
- override fun setParcelPlayerStatus(parcel: ParcelId, player: UUID, status: AddedStatus) = b.launchJob { setLocalPlayerStatus(parcel, player, status) }
+ override fun setParcelAllowsInteractInventory(parcel: ParcelId, value: Boolean) = b.launchJob { b.setParcelAllowsInteractInventory(parcel, value) }
- override fun setParcelAllowsInteractInventory(parcel: ParcelId, value: Boolean) = b.launchJob { setParcelAllowsInteractInventory(parcel, value) }
+ override fun setParcelAllowsInteractInputs(parcel: ParcelId, value: Boolean) = b.launchJob { b.setParcelAllowsInteractInputs(parcel, value) }
- override fun setParcelAllowsInteractInputs(parcel: ParcelId, value: Boolean) = b.launchJob { setParcelAllowsInteractInputs(parcel, value) }
+ override fun transmitAllGlobalAddedData(): ReceiveChannel<AddedDataPair<PlayerProfile>> = b.openChannel { b.transmitAllGlobalAddedData(it) }
- override fun readAllGlobalAddedData(): ReceiveChannel<AddedDataPair<ParcelOwner>> = b.openChannel { produceAllGlobalAddedData(it) }
+ override fun readGlobalAddedData(owner: PlayerProfile): Deferred<MutableAddedDataMap?> = b.launchFuture { b.readGlobalAddedData(owner) }
- override fun readGlobalAddedData(owner: ParcelOwner): Deferred<MutableAddedDataMap?> = b.launchFuture { readGlobalAddedData(owner) }
+ override fun setGlobalAddedStatus(owner: PlayerProfile, player: PlayerProfile, status: AddedStatus) = b.launchJob { b.setGlobalPlayerStatus(owner, player, status) }
- override fun setGlobalAddedStatus(owner: ParcelOwner, player: UUID, status: AddedStatus) = b.launchJob { setGlobalPlayerStatus(owner, player, status) }
+ override fun getChannelToUpdateParcelData(): SendChannel<Pair<ParcelId, ParcelData>> = b.openChannelForWriting { b.setParcelData(it.first, it.second) }
}
diff --git a/src/main/kotlin/io/dico/parcels2/storage/exposed/CoroutineTransactionManager.kt b/src/main/kotlin/io/dico/parcels2/storage/exposed/CoroutineTransactionManager.kt
deleted file mode 100644
index ab707af..0000000
--- a/src/main/kotlin/io/dico/parcels2/storage/exposed/CoroutineTransactionManager.kt
+++ /dev/null
@@ -1,118 +0,0 @@
-package io.dico.parcels2.storage.exposed
-
-import kotlinx.coroutines.experimental.*
-import org.jetbrains.exposed.sql.*
-import org.jetbrains.exposed.sql.statements.StatementContext
-import org.jetbrains.exposed.sql.statements.StatementInterceptor
-import org.jetbrains.exposed.sql.statements.expandArgs
-import org.jetbrains.exposed.sql.transactions.*
-import org.slf4j.LoggerFactory
-import java.sql.Connection
-import kotlin.coroutines.experimental.CoroutineContext
-
-fun <T> ctransaction(db: Database? = null, statement: suspend Transaction.() -> T): T {
- return ctransaction(TransactionManager.manager.defaultIsolationLevel, 3, db, statement)
-}
-
-fun <T> ctransaction(transactionIsolation: Int, repetitionAttempts: Int, db: Database? = null, statement: suspend Transaction.() -> T): T {
- return transaction(transactionIsolation, repetitionAttempts, db) {
- if (this !is CoroutineTransaction) throw IllegalStateException("ctransaction requires CoroutineTransactionManager.")
-
- val job = async(context = manager.context, start = CoroutineStart.UNDISPATCHED) {
- this@transaction.statement()
- }
-
- if (job.isActive) {
- runBlocking(context = Unconfined) {
- job.join()
- }
- }
-
- job.getCompleted()
- }
-}
-
-class CoroutineTransactionManager(private val db: Database,
- dispatcher: CoroutineDispatcher,
- override var defaultIsolationLevel: Int = DEFAULT_ISOLATION_LEVEL) : TransactionManager {
- val context: CoroutineDispatcher = TransactionCoroutineDispatcher(dispatcher)
- private val transaction = ThreadLocal<CoroutineTransaction?>()
-
- override fun currentOrNull(): Transaction? {
-
-
- return transaction.get()
- ?: null
- }
-
- override fun newTransaction(isolation: Int): Transaction {
- return CoroutineTransaction(this, CoroutineTransactionInterface(db, isolation, transaction)).also { transaction.set(it) }
- }
-
- private inner class TransactionCoroutineDispatcher(val delegate: CoroutineDispatcher) : CoroutineDispatcher() {
-
- // When the thread changes, move the transaction to the new thread
- override fun dispatch(context: CoroutineContext, block: Runnable) {
- val existing = transaction.get()
-
- val newContext: CoroutineContext
- if (existing != null) {
- transaction.set(null)
- newContext = context // + existing
- } else {
- newContext = context
- }
-
- delegate.dispatch(newContext, Runnable {
- if (existing != null) {
- transaction.set(existing)
- }
-
- block.run()
- })
- }
-
- }
-
-}
-
-private class CoroutineTransaction(val manager: CoroutineTransactionManager,
- itf: CoroutineTransactionInterface) : Transaction(itf), CoroutineContext.Element {
- companion object Key : CoroutineContext.Key<CoroutineTransaction>
-
- override val key: CoroutineContext.Key<CoroutineTransaction> = Key
-}
-
-private class CoroutineTransactionInterface(override val db: Database, isolation: Int, val threadLocal: ThreadLocal<CoroutineTransaction?>) : TransactionInterface {
- private val connectionLazy = lazy(LazyThreadSafetyMode.NONE) {
- db.connector().apply {
- autoCommit = false
- transactionIsolation = isolation
- }
- }
- override val connection: Connection
- get() = connectionLazy.value
-
- override val outerTransaction: CoroutineTransaction? = threadLocal.get()
-
- override fun commit() {
- if (connectionLazy.isInitialized())
- connection.commit()
- }
-
- override fun rollback() {
- if (connectionLazy.isInitialized() && !connection.isClosed) {
- connection.rollback()
- }
- }
-
- override fun close() {
- try {
- if (connectionLazy.isInitialized()) connection.close()
- } finally {
- threadLocal.set(outerTransaction)
- }
- }
-
-}
-
diff --git a/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt b/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt
index 01afd94..8ea6653 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt
@@ -4,10 +4,13 @@ package io.dico.parcels2.storage.exposed
import com.zaxxer.hikari.HikariDataSource
import io.dico.parcels2.*
+import io.dico.parcels2.storage.AddedDataPair
import io.dico.parcels2.storage.Backing
import io.dico.parcels2.storage.DataPair
+import io.dico.parcels2.util.synchronized
import io.dico.parcels2.util.toUUID
import kotlinx.coroutines.experimental.*
+import kotlinx.coroutines.experimental.channels.ArrayChannel
import kotlinx.coroutines.experimental.channels.LinkedListChannel
import kotlinx.coroutines.experimental.channels.ReceiveChannel
import kotlinx.coroutines.experimental.channels.SendChannel
@@ -21,10 +24,9 @@ import javax.sql.DataSource
class ExposedDatabaseException(message: String? = null) : Exception(message)
-class ExposedBacking(private val dataSourceFactory: () -> DataSource,
- private val poolSize: Int) : Backing {
+class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSize: Int) : Backing {
override val name get() = "Exposed"
- val dispatcher: CoroutineDispatcher = newFixedThreadPoolContext(4, "Parcels StorageThread")
+ override val dispatcher: ThreadPoolDispatcher = newFixedThreadPoolContext(poolSize, "Parcels StorageThread")
private var dataSource: DataSource? = null
private var database: Database? = null
@@ -40,6 +42,24 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource,
return channel
}
+ override fun <T> openChannelForWriting(action: Backing.(T) -> Unit): SendChannel<T> {
+ val channel = ArrayChannel<T>(poolSize * 2)
+
+ repeat(poolSize) {
+ launch(dispatcher) {
+ try {
+ while (true) {
+ action(channel.receive())
+ }
+ } catch (ex: Exception) {
+ // channel closed
+ }
+ }
+ }
+
+ return channel
+ }
+
private fun <T> transaction(statement: Transaction.() -> T) = transaction(database!!, statement)
companion object {
@@ -51,25 +71,54 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource,
}
override fun init() {
- if (isShutdown || isConnected) throw IllegalStateException()
-
- dataSource = dataSourceFactory()
- database = Database.connect(dataSource!!)
- transaction(database!!) {
- create(WorldsT, OwnersT, ParcelsT, ParcelOptionsT, AddedLocalT, AddedGlobalT)
+ synchronized {
+ if (isShutdown || isConnected) throw IllegalStateException()
+ dataSource = dataSourceFactory()
+ database = Database.connect(dataSource!!)
+ transaction(database!!) {
+ create(WorldsT, ProfilesT, ParcelsT, ParcelOptionsT, AddedLocalT, AddedGlobalT)
+ }
}
}
override fun shutdown() {
- if (isShutdown) throw IllegalStateException()
- dataSource?.let {
- (it as? HikariDataSource)?.close()
+ synchronized {
+ if (isShutdown) throw IllegalStateException()
+ dataSource?.let {
+ (it as? HikariDataSource)?.close()
+ }
+ database = null
+ isShutdown = true
}
- database = null
- isShutdown = true
}
- override fun produceParcelData(channel: SendChannel<DataPair>, parcels: Sequence<ParcelId>) {
+ private fun PlayerProfile.toOwnerProfile(): PlayerProfile {
+ if (this is PlayerProfile.Star) return PlayerProfile.Fake(PlayerProfile.Star.name)
+ return this
+ }
+
+ private fun PlayerProfile.Unresolved.toResolvedProfile(): PlayerProfile.Real {
+ return resolve(getPlayerUuidForName(name) ?: throwException())
+ }
+
+ private fun PlayerProfile.toResolvedProfile(): PlayerProfile {
+ if (this is PlayerProfile.Unresolved) return toResolvedProfile()
+ return this
+ }
+
+ private fun PlayerProfile.toRealProfile(): PlayerProfile.Real = when (this) {
+ is PlayerProfile.Real -> this
+ is PlayerProfile.Fake -> throw IllegalArgumentException("Fake profiles are not accepted")
+ is PlayerProfile.Unresolved -> toResolvedProfile()
+ else -> throw InternalError("Case should not be reached")
+ }
+
+ override fun getPlayerUuidForName(name: String): UUID? {
+ return ProfilesT.slice(ProfilesT.uuid).select { ProfilesT.name.upperCase() eq name.toUpperCase() }
+ .firstOrNull()?.let { it[ProfilesT.uuid]?.toUUID() }
+ }
+
+ override fun transmitParcelData(channel: SendChannel<DataPair>, parcels: Sequence<ParcelId>) {
for (parcel in parcels) {
val data = readParcelData(parcel)
channel.offer(parcel to data)
@@ -77,25 +126,25 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource,
channel.close()
}
- override fun produceAllParcelData(channel: SendChannel<Pair<ParcelId, ParcelData?>>) = ctransaction<Unit> {
+ override fun transmitAllParcelData(channel: SendChannel<DataPair>) {
ParcelsT.selectAll().forEach { row ->
- val parcel = ParcelsT.getId(row) ?: return@forEach
+ val parcel = ParcelsT.getItem(row) ?: return@forEach
val data = rowToParcelData(row)
- channel.send(parcel to data)
+ channel.offer(parcel to data)
}
channel.close()
}
- override fun readParcelData(parcel: ParcelId): ParcelData? = transaction {
- val row = ParcelsT.getRow(parcel) ?: return@transaction null
- rowToParcelData(row)
+ override fun readParcelData(parcel: ParcelId): ParcelData? {
+ val row = ParcelsT.getRow(parcel) ?: return null
+ return rowToParcelData(row)
}
- override fun getOwnedParcels(user: ParcelOwner): List<ParcelId> = transaction {
- val user_id = OwnersT.getId(user) ?: return@transaction emptyList()
- ParcelsT.select { ParcelsT.owner_id eq user_id }
+ override fun getOwnedParcels(user: PlayerProfile): List<ParcelId> {
+ val user_id = ProfilesT.getId(user.toOwnerProfile()) ?: return emptyList()
+ return ParcelsT.select { ParcelsT.owner_id eq user_id }
.orderBy(ParcelsT.claim_time, isAsc = true)
- .mapNotNull(ParcelsT::getId)
+ .mapNotNull(ParcelsT::getItem)
.toList()
}
@@ -123,21 +172,21 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource,
setParcelOwner(parcel, data.owner)
- for ((uuid, status) in data.addedMap) {
- setLocalPlayerStatus(parcel, uuid, status)
+ for ((profile, status) in data.addedMap) {
+ AddedLocalT.setPlayerStatus(parcel, profile, status)
}
setParcelAllowsInteractInputs(parcel, data.allowInteractInputs)
setParcelAllowsInteractInventory(parcel, data.allowInteractInventory)
}
- override fun setParcelOwner(parcel: ParcelId, owner: ParcelOwner?) = transaction {
+ override fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?) {
val id = if (owner == null)
- ParcelsT.getId(parcel) ?: return@transaction
+ ParcelsT.getId(parcel) ?: return
else
ParcelsT.getOrInitId(parcel)
- val owner_id = owner?.let { OwnersT.getOrInitId(it) }
+ val owner_id = owner?.let { ProfilesT.getOrInitId(it.toOwnerProfile()) }
val time = owner?.let { DateTime.now() }
ParcelsT.update({ ParcelsT.id eq id }) {
@@ -146,11 +195,11 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource,
}
}
- override fun setLocalPlayerStatus(parcel: ParcelId, player: UUID, status: AddedStatus) = transaction {
- AddedLocalT.setPlayerStatus(parcel, player, status)
+ override fun setLocalPlayerStatus(parcel: ParcelId, player: PlayerProfile, status: AddedStatus) {
+ AddedLocalT.setPlayerStatus(parcel, player.toRealProfile(), status)
}
- override fun setParcelAllowsInteractInventory(parcel: ParcelId, value: Boolean): Unit = transaction {
+ override fun setParcelAllowsInteractInventory(parcel: ParcelId, value: Boolean) {
val id = ParcelsT.getOrInitId(parcel)
ParcelOptionsT.upsert(ParcelOptionsT.parcel_id) {
it[ParcelOptionsT.parcel_id] = id
@@ -158,7 +207,7 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource,
}
}
- override fun setParcelAllowsInteractInputs(parcel: ParcelId, value: Boolean): Unit = transaction {
+ override fun setParcelAllowsInteractInputs(parcel: ParcelId, value: Boolean) {
val id = ParcelsT.getOrInitId(parcel)
ParcelOptionsT.upsert(ParcelOptionsT.parcel_id) {
it[ParcelOptionsT.parcel_id] = id
@@ -166,36 +215,30 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource,
}
}
- override fun produceAllGlobalAddedData(channel: SendChannel<Pair<ParcelOwner, MutableMap<UUID, AddedStatus>>>) = ctransaction<Unit> {
+ override fun transmitAllGlobalAddedData(channel: SendChannel<AddedDataPair<PlayerProfile>>) {
AddedGlobalT.sendAllAddedData(channel)
channel.close()
}
- override fun readGlobalAddedData(owner: ParcelOwner): MutableMap<UUID, AddedStatus> = transaction {
- return@transaction AddedGlobalT.readAddedData(OwnersT.getId(owner) ?: return@transaction hashMapOf())
+ override fun readGlobalAddedData(owner: PlayerProfile): MutableAddedDataMap {
+ return AddedGlobalT.readAddedData(ProfilesT.getId(owner.toOwnerProfile()) ?: return hashMapOf())
}
- override fun setGlobalPlayerStatus(owner: ParcelOwner, player: UUID, status: AddedStatus) = transaction {
- AddedGlobalT.setPlayerStatus(owner, player, status)
+ override fun setGlobalPlayerStatus(owner: PlayerProfile, player: PlayerProfile, status: AddedStatus) {
+ AddedGlobalT.setPlayerStatus(owner, player.toRealProfile(), status)
}
private fun rowToParcelData(row: ResultRow) = ParcelDataHolder().apply {
- owner = row[ParcelsT.owner_id]?.let { OwnersT.getId(it) }
+ owner = row[ParcelsT.owner_id]?.let { ProfilesT.getItem(it) }
since = row[ParcelsT.claim_time]
- val parcelId = row[ParcelsT.id]
- addedMap = 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)
+ val id = row[ParcelsT.id]
+ ParcelOptionsT.select { ParcelOptionsT.parcel_id eq id }.firstOrNull()?.let { optrow ->
+ allowInteractInputs = optrow[ParcelOptionsT.interact_inputs]
+ allowInteractInventory = optrow[ParcelOptionsT.interact_inventory]
}
- ParcelOptionsT.select { ParcelOptionsT.parcel_id eq parcelId }.firstOrNull()?.let {
- allowInteractInputs = it[ParcelOptionsT.interact_inputs]
- allowInteractInventory = it[ParcelOptionsT.interact_inventory]
- }
+ addedMap = AddedLocalT.readAddedData(id)
}
}
diff --git a/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedExtensions.kt b/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedExtensions.kt
index ce66644..6ebac6d 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedExtensions.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedExtensions.kt
@@ -1,12 +1,9 @@
package io.dico.parcels2.storage.exposed
-import org.jetbrains.exposed.sql.Column
-import org.jetbrains.exposed.sql.Index
-import org.jetbrains.exposed.sql.Table
-import org.jetbrains.exposed.sql.Transaction
+import org.jetbrains.exposed.sql.*
+import org.jetbrains.exposed.sql.Function
import org.jetbrains.exposed.sql.statements.InsertStatement
import org.jetbrains.exposed.sql.transactions.TransactionManager
-import org.jetbrains.exposed.sql.transactions.transaction
class UpsertStatement<Key : Any>(table: Table, conflictColumn: Column<*>? = null, conflictIndex: Index? = null)
: InsertStatement<Key>(table, false) {
@@ -63,3 +60,15 @@ fun Table.indexR(customIndexName: String? = null, isUnique: Boolean = false, var
fun Table.uniqueIndexR(customIndexName: String? = null, vararg columns: Column<*>): Index = indexR(customIndexName, true, *columns)
+fun <T : Int?> ExpressionWithColumnType<T>.abs(): Function<T> = Abs(this)
+
+class Abs<T : Int?>(val expr: Expression<T>) : Function<T>(IntegerColumnType()) {
+ override fun toSQL(queryBuilder: QueryBuilder): String = "ABS(${expr.toSQL(queryBuilder)})"
+}
+
+fun <T : Comparable<T>> SqlExpressionBuilder.greater(col1: ExpressionWithColumnType<T>, col2: ExpressionWithColumnType<T>): Expression<T> {
+ return case(col1)
+ .When(col1.greater(col2), col1)
+ .Else(col2)
+}
+
diff --git a/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt b/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt
index ac6e431..33314aa 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt
@@ -3,12 +3,12 @@
package io.dico.parcels2.storage.exposed
import io.dico.parcels2.ParcelId
-import io.dico.parcels2.ParcelOwner
import io.dico.parcels2.ParcelWorldId
+import io.dico.parcels2.PlayerProfile
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 org.jetbrains.exposed.sql.statements.UpdateBuilder
import java.util.UUID
sealed class IdTransactionsTable<TableT : IdTransactionsTable<TableT, QueryObj>, QueryObj>(tableName: String, columnName: String)
@@ -23,16 +23,16 @@ sealed class IdTransactionsTable<TableT : IdTransactionsTable<TableT, QueryObj>,
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)
+ internal inline fun getOrInitId(getId: () -> Int?, noinline body: TableT.(UpdateBuilder<*>) -> Unit, objName: () -> String): Int {
+ return getId() ?: table.insertIgnore(body)[id] ?: getId() ?: throw ExposedDatabaseException("This should not happen - failed to insert ${objName()} and get its id")
}
- private inline fun insertError(obj: String): Nothing = throw ExposedDatabaseException("This should not happen - failed to insert $obj and getParcelDeferred its id")
-
abstract fun getId(obj: QueryObj): Int?
abstract fun getOrInitId(obj: QueryObj): Int
- fun getId(id: Int): QueryObj? = select { this@IdTransactionsTable.id eq id }.firstOrNull()?.let { getId(it) }
- abstract fun getId(row: ResultRow): QueryObj?
+ fun getItem(id: Int): QueryObj? = select { this@IdTransactionsTable.id eq id }.firstOrNull()?.let { getItem(it) }
+ abstract fun getItem(row: ResultRow): QueryObj?
+
+ fun getId(obj: QueryObj, init: Boolean): Int? = if (init) getOrInitId(obj) else getId(obj)
}
object WorldsT : IdTransactionsTable<WorldsT, ParcelWorldId>("parcel_worlds", "world_id") {
@@ -44,14 +44,16 @@ object WorldsT : IdTransactionsTable<WorldsT, ParcelWorldId>("parcel_worlds", "w
internal inline fun getId(worldName: String, binaryUid: ByteArray?): Int? = getId { (name eq worldName).let { if (binaryUid == null) it else it or (uid eq binaryUid) } }
internal inline fun getId(worldName: String, uid: UUID?): Int? = getId(worldName, uid?.toByteArray())
internal inline fun getOrInitId(worldName: String, worldUid: UUID?): Int = worldUid?.toByteArray().let { binaryUid ->
- getId(worldName, binaryUid)
- ?: insertAndGetId("world named $worldName") { it[name] = worldName; binaryUid?.let { buid -> it[uid] = buid } }
+ return getOrInitId(
+ { getId(worldName, binaryUid) },
+ { it[name] = worldName; it[uid] = binaryUid },
+ { "world named $worldName" })
}
override fun getId(world: ParcelWorldId): Int? = getId(world.name, world.uid)
override fun getOrInitId(world: ParcelWorldId): Int = getOrInitId(world.name, world.uid)
- override fun getId(row: ResultRow): ParcelWorldId {
+ override fun getItem(row: ResultRow): ParcelWorldId {
return ParcelWorldId(row[name], row[uid]?.toUUID())
}
}
@@ -60,7 +62,7 @@ object ParcelsT : IdTransactionsTable<ParcelsT, ParcelId>("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 owner_id = integer("owner_id").references(ProfilesT.id).nullable()
val claim_time = datetime("claim_time").nullable()
val index_location = uniqueIndexR("index_location", world_id, px, pz)
@@ -68,8 +70,10 @@ object ParcelsT : IdTransactionsTable<ParcelsT, ParcelId>("parcels", "parcel_id"
private inline fun getId(worldName: String, worldUid: UUID?, parcelX: Int, parcelZ: Int): Int? = WorldsT.getId(worldName, worldUid)?.let { getId(it, parcelX, parcelZ) }
private inline fun getOrInitId(worldName: String, worldUid: UUID?, parcelX: Int, parcelZ: Int): Int {
val worldId = WorldsT.getOrInitId(worldName, worldUid)
- return getId(worldId, parcelX, parcelZ)
- ?: insertAndGetId("parcel at $worldName($parcelX, $parcelZ)") { it[world_id] = worldId; it[px] = parcelX; it[pz] = parcelZ }
+ return getOrInitId(
+ { getId(worldId, parcelX, parcelZ) },
+ { it[world_id] = worldId; it[px] = parcelX; it[pz] = parcelZ },
+ { "parcel at $worldName($parcelX, $parcelZ)" })
}
override fun getId(parcel: ParcelId): Int? = getId(parcel.worldId.name, parcel.worldId.uid, parcel.x, parcel.z)
@@ -78,41 +82,56 @@ object ParcelsT : IdTransactionsTable<ParcelsT, ParcelId>("parcels", "parcel_id"
private inline fun getRow(id: Int): ResultRow? = select { ParcelsT.id eq id }.firstOrNull()
fun getRow(parcel: ParcelId): ResultRow? = getId(parcel)?.let { getRow(it) }
- override fun getId(row: ResultRow): ParcelId? {
+ override fun getItem(row: ResultRow): ParcelId? {
val worldId = row[world_id]
- val world = WorldsT.getId(worldId) ?: return null
+ val world = WorldsT.getItem(worldId) ?: return null
return ParcelId(world, row[px], row[pz])
}
}
-object OwnersT : IdTransactionsTable<OwnersT, ParcelOwner>("parcel_owners", "owner_id") {
+object ProfilesT : IdTransactionsTable<ProfilesT, PlayerProfile>("parcel_profiles", "owner_id") {
val uuid = binary("uuid", 16).nullable()
val name = varchar("name", 32)
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(nameIn: String) = getId { uuid.isNull() and (name eq nameIn) }
+ private inline fun getId(nameIn: String) = getId { uuid.isNull() and (name.lowerCase() eq nameIn.toLowerCase()) }
+ private inline fun getRealId(nameIn: String) = getId { uuid.isNotNull() and (name.lowerCase() eq nameIn.toLowerCase()) }
- private inline fun getOrInitId(uuid: UUID, name: String) = uuid.toByteArray().let { binaryUuid ->
- getId(binaryUuid) ?: insertAndGetId("owner(uuid = $uuid)") {
- it[this@OwnersT.uuid] = binaryUuid
- it[this@OwnersT.name] = name
- }
+ private inline fun getOrInitId(uuid: UUID, name: String) = uuid.toByteArray().let { binaryUuid -> getOrInitId(
+ { getId(binaryUuid) },
+ { it[this@ProfilesT.uuid] = binaryUuid; it[this@ProfilesT.name] = name },
+ { "profile(uuid = $uuid, name = $name)" })
}
- private inline fun getOrInitId(name: String) =
- getId(name) ?: insertAndGetId("owner(name = $name)") { it[OwnersT.name] = name }
+ private inline fun getOrInitId(name: String) = getOrInitId(
+ { getId(name) },
+ { it[ProfilesT.name] = name },
+ { "owner(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!!, owner.notNullName)
- else getOrInitId(owner.name!!)
+ override fun getId(profile: PlayerProfile): Int? = when (profile) {
+ is PlayerProfile.Real -> getId(profile.uuid)
+ is PlayerProfile.Fake -> getId(profile.name)
+ is PlayerProfile.Unresolved -> getRealId(profile.name)
+ else -> throw IllegalArgumentException()
+ }
+
+ override fun getOrInitId(profile: PlayerProfile): Int = when (profile) {
+ is PlayerProfile.Real -> getOrInitId(profile.uuid, profile.notNullName)
+ is PlayerProfile.Fake -> getOrInitId(profile.name)
+ else -> throw IllegalArgumentException()
+ }
- override fun getId(row: ResultRow): ParcelOwner {
- return row[uuid]?.toUUID()?.let { ParcelOwner(it) } ?: ParcelOwner(row[name])
+ override fun getItem(row: ResultRow): PlayerProfile {
+ return PlayerProfile(row[uuid]?.toUUID(), row[name])
}
+
+ fun getRealItem(id: Int): PlayerProfile.Real? {
+ return getItem(id) as? PlayerProfile.Real
+ }
+
}
+
+// val ParcelsWithOptionsT = ParcelsT.join(ParcelOptionsT, JoinType.INNER, onColumn = ParcelsT.id, otherColumn = ParcelOptionsT.parcel_id) \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt b/src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt
index 20b36b1..bbf6872 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt
@@ -2,17 +2,13 @@
package io.dico.parcels2.storage.exposed
-import io.dico.parcels2.AddedStatus
-import io.dico.parcels2.ParcelId
-import io.dico.parcels2.ParcelOwner
-import io.dico.parcels2.util.toByteArray
-import io.dico.parcels2.util.toUUID
+import io.dico.parcels2.*
import kotlinx.coroutines.experimental.channels.SendChannel
import org.jetbrains.exposed.sql.*
import java.util.UUID
object AddedLocalT : AddedTable<ParcelId>("parcels_added_local", ParcelsT)
-object AddedGlobalT : AddedTable<ParcelOwner>("parcels_added_global", OwnersT)
+object AddedGlobalT : AddedTable<PlayerProfile>("parcels_added_global", ProfilesT)
object ParcelOptionsT : Table("parcel_options") {
val parcel_id = integer("parcel_id").primaryKey().references(ParcelsT.id, ReferenceOption.CASCADE)
@@ -20,55 +16,59 @@ object ParcelOptionsT : Table("parcel_options") {
val interact_inputs = bool("interact_inputs").default(true)
}
-typealias AddedStatusSendChannel<AttachT> = SendChannel<Pair<AttachT, MutableMap<UUID, AddedStatus>>>
+typealias AddedStatusSendChannel<AttachT> = SendChannel<Pair<AttachT, MutableAddedDataMap>>
sealed class AddedTable<AttachT>(name: String, val idTable: IdTransactionsTable<*, AttachT>) : Table(name) {
val attach_id = integer("attach_id").references(idTable.id, ReferenceOption.CASCADE)
- val player_uuid = binary("player_uuid", 16)
+ val profile_id = integer("profile_id").references(ProfilesT.id, ReferenceOption.CASCADE)
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()
+ val index_pair = uniqueIndexR("index_pair", attach_id, profile_id)
+ fun setPlayerStatus(attachedOn: AttachT, player: PlayerProfile.Real, status: AddedStatus) {
if (status.isDefault) {
- idTable.getId(attachedOn)?.let { id ->
- deleteWhere { (attach_id eq id) and (player_uuid eq binaryUuid) }
+ val player_id = ProfilesT.getId(player) ?: return
+ idTable.getId(attachedOn)?.let { holder ->
+ deleteWhere { (attach_id eq holder) and (profile_id eq player_id) }
}
return
}
- val id = idTable.getOrInitId(attachedOn)
+ val holder = idTable.getOrInitId(attachedOn)
+ val player_id = ProfilesT.getOrInitId(player)
upsert(conflictIndex = index_pair) {
- it[attach_id] = id
- it[player_uuid] = binaryUuid
+ it[attach_id] = holder
+ it[profile_id] = player_id
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() })
+ fun readAddedData(id: Int): MutableAddedDataMap {
+ val list = slice(profile_id, allowed_flag).select { attach_id eq id }
+ val result = MutableAddedDataMap()
+ for (row in list) {
+ val profile = ProfilesT.getRealItem(row[profile_id]) ?: continue
+ result[profile] = row[allowed_flag].asAddedStatus()
+ }
+ return result
}
- suspend fun sendAllAddedData(channel: AddedStatusSendChannel<AttachT>) {
- /*
+ fun sendAllAddedData(channel: AddedStatusSendChannel<AttachT>) {
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
+ var attach: AttachT? = null
+ var map: MutableAddedDataMap? = null
fun initAttachAndMap() {
- attach = idTable.getId(id)
+ attach = idTable.getItem(id)
map = attach?.let { mutableMapOf() }
}
- suspend fun sendIfPresent() {
+ fun sendIfPresent() {
if (attach != null && map != null && map!!.isNotEmpty()) {
- channel.send(attach!! to map!!)
+ channel.offer(attach!! to map!!)
}
attach = null
map = null
@@ -88,13 +88,13 @@ sealed class AddedTable<AttachT>(name: String, val idTable: IdTransactionsTable<
continue // owner not found for this owner id
}
- val player_uuid = row[player_uuid].toUUID()
+ val profile = ProfilesT.getRealItem(row[profile_id]) ?: continue
val status = row[allowed_flag].asAddedStatus()
- map!![player_uuid] = status
+ map!![profile] = status
}
sendIfPresent()
- }*/
+ }
}
private inline fun Boolean?.asAddedStatus() = if (this == null) AddedStatus.DEFAULT else if (this) AddedStatus.ALLOWED else AddedStatus.BANNED
diff --git a/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt b/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt
index 1f6e49c..f0c0cd8 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt
@@ -6,45 +6,43 @@ import com.zaxxer.hikari.HikariDataSource
import io.dico.parcels2.*
import io.dico.parcels2.options.PlotmeMigrationOptions
import io.dico.parcels2.storage.Storage
+import io.dico.parcels2.storage.exposed.abs
+import io.dico.parcels2.storage.exposed.greater
import io.dico.parcels2.storage.migration.Migration
-import io.dico.parcels2.util.Vec2i
-import io.dico.parcels2.util.isValid
import io.dico.parcels2.util.toUUID
-import io.dico.parcels2.util.uuid
-import kotlinx.coroutines.experimental.*
-import org.bukkit.Bukkit
+import kotlinx.coroutines.experimental.Job
+import kotlinx.coroutines.experimental.launch
+import kotlinx.coroutines.experimental.newFixedThreadPoolContext
import org.jetbrains.exposed.sql.*
import org.slf4j.LoggerFactory
-import java.io.ByteArrayOutputStream
import java.sql.Blob
import java.util.UUID
-import java.util.concurrent.ConcurrentHashMap
import javax.sql.DataSource
-import kotlin.coroutines.experimental.coroutineContext
class PlotmeMigration(val options: PlotmeMigrationOptions) : Migration {
private var dataSource: DataSource? = null
private var database: Database? = null
private var isShutdown: Boolean = false
private val mlogger = LoggerFactory.getLogger("PlotMe Migrator")
+ val dispatcher = newFixedThreadPoolContext(1, "PlotMe Migration Thread")
private fun <T> transaction(statement: Transaction.() -> T) = org.jetbrains.exposed.sql.transactions.transaction(database!!, statement)
override fun migrateTo(storage: Storage): Job {
- return launch(context = storage.asyncDispatcher) {
+ return launch(dispatcher) {
init()
- transaction { launch(context = Unconfined, start = CoroutineStart.UNDISPATCHED) { doWork(storage) } }
+ doWork(storage)
shutdown()
}
}
- suspend fun init() {
- if (isShutdown) throw IllegalStateException()
+ fun init() {
+ if (isShutdown || database != null) throw IllegalStateException()
dataSource = options.storage.getDataSourceFactory()!!()
database = Database.connect(dataSource!!)
}
- suspend fun shutdown() {
+ fun shutdown() {
if (isShutdown) throw IllegalStateException()
dataSource?.let {
(it as? HikariDataSource)?.close()
@@ -53,74 +51,61 @@ class PlotmeMigration(val options: PlotmeMigrationOptions) : Migration {
isShutdown = true
}
- private val parcelsCache = hashMapOf<String, MutableMap<Vec2i, ParcelData>>()
-
- private fun getMap(worldName: String): MutableMap<Vec2i, ParcelData>? {
- val mapped = options.worldsFromTo[worldName] ?: return null
- return parcelsCache.computeIfAbsent(mapped) { mutableMapOf() }
- }
-
- private fun getData(worldName: String, position: Vec2i): ParcelData? {
- return getMap(worldName)?.computeIfAbsent(position) { ParcelDataHolder(addedMap = ConcurrentHashMap()) }
- }
-
- suspend fun doWork(target: Storage): Unit {
- if (!PlotmePlotsT.exists()) {
- mlogger.warn("Plotme tables don't appear to exist. Exiting.")
- return
+ suspend fun doWork(target: Storage) {
+ val exit = transaction {
+ (!PlotmePlotsT.exists()).also {
+ if (it) mlogger.warn("Plotme tables don't appear to exist. Exiting.")
+ }
}
+ if (exit) return
- parcelsCache.clear()
+ val worldCache = options.worldsFromTo.mapValues { ParcelWorldId(it.value) }
- iterPlotmeTable(PlotmePlotsT) { data, row ->
- // in practice, owner_uuid is not null for any plot currently. It will convert well.
- data.owner = ParcelOwner(row[owner_uuid]?.toUUID(), row[owner_name])
+ fun getParcelId(table: PlotmeTable, row: ResultRow): ParcelId? {
+ val world = worldCache[row[table.world_name]] ?: return null
+ return ParcelId(world, row[table.px], row[table.pz])
}
- launch(context = target.asyncDispatcher) {
- iterPlotmeTable(PlotmeAllowedT) { data, row ->
- val uuid = row[player_uuid]?.toUUID()
- ?: Bukkit.getOfflinePlayer(row[player_name]).takeIf { it.isValid }?.uuid
- ?: return@iterPlotmeTable
-
- data.setAddedStatus(uuid, AddedStatus.ALLOWED)
+ fun PlotmePlotPlayerMap.transmitPlotmeAddedTable(kind: AddedStatus) {
+ selectAll().forEach { row ->
+ val parcel = getParcelId(this, row) ?: return@forEach
+ val profile = StatusKey.safe(row[player_uuid]?.toUUID(), row[player_name]) ?: return@forEach
+ target.setParcelPlayerStatus(parcel, profile, kind)
}
}
- launch(context = target.asyncDispatcher) {
- iterPlotmeTable(PlotmeDeniedT) { data, row ->
- val uuid = row[player_uuid]?.toUUID()
- ?: Bukkit.getOfflinePlayer(row[player_name]).takeIf { it.isValid }?.uuid
- ?: return@iterPlotmeTable
-
- data.setAddedStatus(uuid, AddedStatus.BANNED)
- }
+ mlogger.info("Transmitting data from plotmeplots table")
+ transaction {
+ PlotmePlotsT.selectAll()
+ .orderBy(PlotmePlotsT.world_name)
+ .orderBy(with(SqlExpressionBuilder) { greater(PlotmePlotsT.px.abs(), PlotmePlotsT.pz.abs()) })
+ .forEach { row ->
+ val parcel = getParcelId(PlotmePlotsT, row) ?: return@forEach
+ val owner = PlayerProfile.safe(row[PlotmePlotsT.owner_uuid]?.toUUID(), row[PlotmePlotsT.owner_name])
+ target.setParcelOwner(parcel, owner)
+ }
}
- println(coroutineContext[Job]!!.children)
- coroutineContext[Job]!!.joinChildren()
+ mlogger.info("Transmitting data from plotmeallowed table")
+ transaction {
+ PlotmeAllowedT.transmitPlotmeAddedTable(AddedStatus.ALLOWED)
+ }
- for ((worldName, map) in parcelsCache) {
- val world = ParcelWorldId(worldName)
- for ((pos, data) in map) {
- val parcel = ParcelId(world, pos)
- target.setParcelData(parcel, data)
- }
+ mlogger.info("Transmitting data from plotmedenied table")
+ transaction {
+ PlotmeDeniedT.transmitPlotmeAddedTable(AddedStatus.BANNED)
}
+ mlogger.warn("Data has been **transmitted**.")
+ mlogger.warn("Loading parcel data might take a while as enqueued transactions from this migration are completed.")
}
- private fun Blob.toUUID(): UUID {
- val out = ByteArrayOutputStream(16)
- binaryStream.copyTo(out, bufferSize = 16)
- return out.toByteArray().toUUID()
+ private fun Blob.toUUID(): UUID? {
+ val ba = ByteArray(16)
+ val count = binaryStream.read(ba, 0, 16)
+ if (count < 16) return null
+ return ba.toUUID()
}
- private inline fun <T : PlotmeTable> iterPlotmeTable(table: T, block: T.(ParcelData, ResultRow) -> Unit) {
- table.selectAll().forEach { row ->
- val data = getData(row[table.world_name], Vec2i(row[table.px], row[table.pz])) ?: return@forEach
- table.block(data, row)
- }
- }
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/util/MiscExtensions.kt b/src/main/kotlin/io/dico/parcels2/util/MiscExtensions.kt
index a4ab58d..877d1cc 100644
--- a/src/main/kotlin/io/dico/parcels2/util/MiscExtensions.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/MiscExtensions.kt
@@ -15,6 +15,9 @@ fun File.tryCreate(): Boolean {
return true
}
+inline fun Boolean.alsoIfTrue(block: () -> Unit): Boolean = also { if (it) block() }
+inline fun Boolean.alsoIfFalse(block: () -> Unit): Boolean = also { if (!it) block() }
+
inline fun <R> Any.synchronized(block: () -> R): R = synchronized(this, block)
inline fun <T> T?.isNullOr(condition: T.() -> Boolean): Boolean = this == null || condition()
diff --git a/src/main/kotlin/io/dico/parcels2/util/PlayerExtensions.kt b/src/main/kotlin/io/dico/parcels2/util/PlayerExtensions.kt
index 8713da7..9604365 100644
--- a/src/main/kotlin/io/dico/parcels2/util/PlayerExtensions.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/PlayerExtensions.kt
@@ -2,12 +2,14 @@ package io.dico.parcels2.util
import io.dico.dicore.Formatting
import io.dico.parcels2.ParcelsPlugin
+import io.dico.parcels2.PlayerProfile
import io.dico.parcels2.logger
import org.bukkit.OfflinePlayer
import org.bukkit.entity.Player
import org.bukkit.plugin.java.JavaPlugin
inline val OfflinePlayer.uuid get() = uniqueId
+
@Suppress("UsePropertyAccessSyntax")
inline val OfflinePlayer.isValid
get() = isOnline() || hasPlayedBefore()
diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml
index c2c9ddb..eff524c 100644
--- a/src/main/resources/logback.xml
+++ b/src/main/resources/logback.xml
@@ -1,41 +1,17 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
- <!-- old pattern <pattern>%d{HH:mm:ss.SSS} %magenta(%-8.-8(%thread)) %highlight(%-5level) %boldCyan(%8.-32logger{32}) - %msg</pattern>-->
- <pattern>%magenta(%-8.-8(%thread)) %highlight(%-5level) %boldCyan(%6.-32logger{32}) - %msg</pattern>
+ <!-- old pattern <pattern>%d{HH:mm:ss.SSS} %magenta(%-8.-8(%thread)) %highlight(%-5level) %boldCyan(%8.-32logger{32}) - %msg\n</pattern>-->
+ <pattern>%magenta(%-16.-16(%thread)) %highlight(%-5level) %boldCyan(%6.-32logger{32}) - %msg</pattern>
</encoder>
</appender>
- <root level="info">
+ <root level="debug">
<appender-ref ref="STDOUT" />
</root>
- <appender name="SQLLOG" class="ch.qos.logback.core.RollingFileAppender">
- <append>true</append>
- <file>C:/Parcels/sql.log</file>
-
- <encoder>
- <!-- old pattern <pattern>%d{HH:mm:ss.SSS} %magenta(%-8.-8(%thread)) %highlight(%-5level) %boldCyan(%8.-32logger{32}) - %msg</pattern>-->
- <pattern>%magenta(%-8.-8(%thread)) %highlight(%-5level) %boldCyan(%6.-32logger{32}) - %msg</pattern>
- </encoder>
-
- <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
- <fileNamePattern>C:/Parcels/sql%i.log</fileNamePattern>
- <minIndex>1</minIndex>
- <maxIndex>3</maxIndex>
- </rollingPolicy>
-
- <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
- <maxFileSize>1MB</maxFileSize>
- </triggeringPolicy>
- </appender>
-
- <appender name="ASYNC_SQLLOG" class="ch.qos.logback.classic.AsyncAppender">
- <appender-ref ref="SQLLOG"/>
- <neverBlock>true</neverBlock>
- </appender>
-
- <logger name="Exposed" level="DEBUG">
- <appender-ref ref="ASYNC_SQLLOG"/>
- </logger>
+ <logger name="com.zaxxer.hikari.pool.HikariPool" level="info"/>
+ <logger name="com.zaxxer.hikari.pool.PoolBase" level="info"/>
+ <logger name="com.zaxxer.hikari.HikariConfig" level="info"/>
+ <logger name="Exposed" level="info"/>
</configuration> \ No newline at end of file