diff options
Diffstat (limited to 'src/main/kotlin/io/dico/parcels2/storage')
6 files changed, 75 insertions, 73 deletions
diff --git a/src/main/kotlin/io/dico/parcels2/storage/Backing.kt b/src/main/kotlin/io/dico/parcels2/storage/Backing.kt index 6c91714..69c5b62 100644 --- a/src/main/kotlin/io/dico/parcels2/storage/Backing.kt +++ b/src/main/kotlin/io/dico/parcels2/storage/Backing.kt @@ -1,13 +1,13 @@ package io.dico.parcels2.storage import io.dico.parcels2.* -import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Deferred import kotlinx.coroutines.Job import kotlinx.coroutines.channels.ReceiveChannel import kotlinx.coroutines.channels.SendChannel import org.joda.time.DateTime import java.util.UUID +import kotlin.coroutines.CoroutineContext interface Backing { @@ -15,7 +15,7 @@ interface Backing { val isConnected: Boolean - val dispatcher: CoroutineDispatcher + val coroutineContext: CoroutineContext fun launchJob(job: Backing.() -> Unit): Job @@ -56,9 +56,9 @@ interface Backing { fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean) - fun setLocalPlayerStatus(parcel: ParcelId, player: PlayerProfile, status: AddedStatus) + fun setLocalPrivilege(parcel: ParcelId, player: PlayerProfile, status: Privilege) - fun setParcelOptionsInteractBitmask(parcel: ParcelId, bitmask: IntArray?) + fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration) /* fun setParcelAllowsInteractInventory(parcel: ParcelId, value: Boolean) @@ -67,7 +67,7 @@ interface Backing { fun transmitAllGlobalAddedData(channel: SendChannel<AddedDataPair<PlayerProfile>>) - fun readGlobalAddedData(owner: PlayerProfile): MutableAddedDataMap + fun readGlobalPrivileges(owner: PlayerProfile): MutablePrivilegeMap - fun setGlobalPlayerStatus(owner: PlayerProfile, player: PlayerProfile, status: AddedStatus) + fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, status: Privilege) } diff --git a/src/main/kotlin/io/dico/parcels2/storage/Storage.kt b/src/main/kotlin/io/dico/parcels2/storage/Storage.kt index c9c0d4a..fa63496 100644 --- a/src/main/kotlin/io/dico/parcels2/storage/Storage.kt +++ b/src/main/kotlin/io/dico/parcels2/storage/Storage.kt @@ -3,6 +3,7 @@ package io.dico.parcels2.storage import io.dico.parcels2.* +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred import kotlinx.coroutines.Job import kotlinx.coroutines.channels.ReceiveChannel @@ -10,9 +11,10 @@ import kotlinx.coroutines.channels.SendChannel import kotlinx.coroutines.launch import org.joda.time.DateTime import java.util.UUID +import kotlin.coroutines.CoroutineContext typealias DataPair = Pair<ParcelId, ParcelData?> -typealias AddedDataPair<TAttach> = Pair<TAttach, MutableAddedDataMap> +typealias AddedDataPair<TAttach> = Pair<TAttach, MutablePrivilegeMap> interface Storage { val name: String @@ -48,28 +50,29 @@ interface Storage { fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean): Job - fun setParcelPlayerStatus(parcel: ParcelId, player: PlayerProfile, status: AddedStatus): Job + fun setLocalPrivilege(parcel: ParcelId, player: PlayerProfile, privilege: Privilege): Job - fun setParcelOptionsInteractBitmask(parcel: ParcelId, bitmask: IntArray): Job + fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration): Job fun transmitAllGlobalAddedData(): ReceiveChannel<AddedDataPair<PlayerProfile>> - fun readGlobalAddedData(owner: PlayerProfile): Deferred<MutableAddedDataMap?> + fun readGlobalPrivileges(owner: PlayerProfile): Deferred<MutablePrivilegeMap?> - fun setGlobalAddedStatus(owner: PlayerProfile, player: PlayerProfile, status: AddedStatus): Job + fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, status: Privilege): Job fun getChannelToUpdateParcelData(): SendChannel<Pair<ParcelId, ParcelData>> } -class BackedStorage internal constructor(val b: Backing) : Storage { +class BackedStorage internal constructor(val b: Backing) : Storage, CoroutineScope { override val name get() = b.name override val isConnected get() = b.isConnected + override val coroutineContext: CoroutineContext get() = b.coroutineContext - override fun init() = launch(b.dispatcher) { b.init() } + override fun init() = launch { b.init() } - override fun shutdown() = launch(b.dispatcher) { b.shutdown() } + override fun shutdown() = launch { b.shutdown() } override fun getWorldCreationTime(worldId: ParcelWorldId): Deferred<DateTime?> = b.launchFuture { b.getWorldCreationTime(worldId) } @@ -96,16 +99,16 @@ class BackedStorage internal constructor(val b: Backing) : Storage { override fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean): Job = b.launchJob { b.setParcelOwnerSignOutdated(parcel, outdated) } - override fun setParcelPlayerStatus(parcel: ParcelId, player: PlayerProfile, status: AddedStatus) = b.launchJob { b.setLocalPlayerStatus(parcel, player, status) } + override fun setLocalPrivilege(parcel: ParcelId, player: PlayerProfile, privilege: Privilege) = b.launchJob { b.setLocalPrivilege(parcel, player, privilege) } - override fun setParcelOptionsInteractBitmask(parcel: ParcelId, bitmask: IntArray) = b.launchJob { b.setParcelOptionsInteractBitmask(parcel, bitmask) } + override fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration) = b.launchJob { b.setParcelOptionsInteractConfig(parcel, config) } override fun transmitAllGlobalAddedData(): ReceiveChannel<AddedDataPair<PlayerProfile>> = b.openChannel { b.transmitAllGlobalAddedData(it) } - override fun readGlobalAddedData(owner: PlayerProfile): Deferred<MutableAddedDataMap?> = b.launchFuture { b.readGlobalAddedData(owner) } + override fun readGlobalPrivileges(owner: PlayerProfile): Deferred<MutablePrivilegeMap?> = b.launchFuture { b.readGlobalPrivileges(owner) } - override fun setGlobalAddedStatus(owner: PlayerProfile, player: PlayerProfile, status: AddedStatus) = b.launchJob { b.setGlobalPlayerStatus(owner, player, status) } + override fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, status: Privilege) = b.launchJob { b.setGlobalPrivilege(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/ExposedBacking.kt b/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt index b7f9f82..4c88c11 100644 --- a/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt +++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt @@ -23,17 +23,16 @@ import javax.sql.DataSource class ExposedDatabaseException(message: String? = null) : Exception(message) -class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSize: Int) : Backing { +class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSize: Int) : Backing, CoroutineScope { override val name get() = "Exposed" - override val dispatcher: ThreadPoolDispatcher = newFixedThreadPoolContext(poolSize, "Parcels StorageThread") - + override val coroutineContext = Job() + newFixedThreadPoolContext(poolSize, "Parcels StorageThread") private var dataSource: DataSource? = null private var database: Database? = null private var isShutdown: Boolean = false override val isConnected get() = database != null - override fun launchJob(job: Backing.() -> Unit): Job = launch(dispatcher) { transaction { job() } } - override fun <T> launchFuture(future: Backing.() -> T): Deferred<T> = async(dispatcher) { transaction { future() } } + override fun launchJob(job: Backing.() -> Unit): Job = launch { transaction { job() } } + override fun <T> launchFuture(future: Backing.() -> T): Deferred<T> = async { transaction { future() } } override fun <T> openChannel(future: Backing.(SendChannel<T>) -> Unit): ReceiveChannel<T> { val channel = LinkedListChannel<T>() @@ -44,8 +43,8 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi override fun <T> openChannelForWriting(action: Backing.(T) -> Unit): SendChannel<T> { val channel = ArrayChannel<T>(poolSize * 2) - repeat(poolSize) { - launch(dispatcher) { + repeat(poolSize.clampMax(3)) { + launch { try { while (true) { action(channel.receive()) @@ -75,7 +74,7 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi dataSource = dataSourceFactory() database = Database.connect(dataSource!!) transaction(database!!) { - create(WorldsT, ProfilesT, ParcelsT, ParcelOptionsT, AddedLocalT, AddedGlobalT) + create(WorldsT, ProfilesT, ParcelsT, ParcelOptionsT, PrivilegesLocalT, PrivilegesGlobalT) } } } @@ -83,11 +82,12 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi override fun shutdown() { synchronized { if (isShutdown) throw IllegalStateException() + isShutdown = true + coroutineContext[Job]!!.cancel(CancellationException("ExposedBacking shutdown")) dataSource?.let { (it as? HikariDataSource)?.close() } database = null - isShutdown = true } } @@ -173,7 +173,7 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi // Below should cascade automatically /* - AddedLocalT.deleteIgnoreWhere { AddedLocalT.parcel_id eq id } + PrivilegesLocalT.deleteIgnoreWhere { PrivilegesLocalT.parcel_id eq id } ParcelOptionsT.deleteIgnoreWhere(limit = 1) { ParcelOptionsT.parcel_id eq id } */ } @@ -184,18 +184,16 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi transaction { val id = ParcelsT.getOrInitId(parcel) - AddedLocalT.deleteIgnoreWhere { AddedLocalT.attach_id eq id } + PrivilegesLocalT.deleteIgnoreWhere { PrivilegesLocalT.attach_id eq id } } setParcelOwner(parcel, data.owner) - for ((profile, status) in data.addedMap) { - AddedLocalT.setPlayerStatus(parcel, profile, status) + for ((profile, privilege) in data.map) { + PrivilegesLocalT.setPrivilege(parcel, profile, privilege) } - val bitmaskArray = (data.interactableConfig as? BitmaskInteractableConfiguration ?: return).bitmaskArray - val isAllZero = bitmaskArray.fold(false) { cur, elem -> cur || elem != 0 } - setParcelOptionsInteractBitmask(parcel, if (isAllZero) null else bitmaskArray) + setParcelOptionsInteractConfig(parcel, data.interactableConfig) } override fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?) { @@ -221,19 +219,22 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi } } - override fun setLocalPlayerStatus(parcel: ParcelId, player: PlayerProfile, status: AddedStatus) { - AddedLocalT.setPlayerStatus(parcel, player.toRealProfile(), status) + override fun setLocalPrivilege(parcel: ParcelId, player: PlayerProfile, privilege: Privilege) { + PrivilegesLocalT.setPrivilege(parcel, player.toRealProfile(), privilege) } - override fun setParcelOptionsInteractBitmask(parcel: ParcelId, bitmask: IntArray?) { - if (bitmask == null) { + override fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration) { + val bitmaskArray = (config as? BitmaskInteractableConfiguration ?: return).bitmaskArray + val isAllZero = !bitmaskArray.fold(false) { cur, elem -> cur || elem != 0 } + + if (isAllZero) { val id = ParcelsT.getId(parcel) ?: return ParcelOptionsT.deleteWhere { ParcelOptionsT.parcel_id eq id } return } - if (bitmask.size != 1) throw IllegalArgumentException() - val array = bitmask.toByteArray() + if (bitmaskArray.size != 1) throw IllegalArgumentException() + val array = bitmaskArray.toByteArray() val id = ParcelsT.getOrInitId(parcel) ParcelOptionsT.upsert(ParcelOptionsT.parcel_id) { it[parcel_id] = id @@ -242,16 +243,16 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi } override fun transmitAllGlobalAddedData(channel: SendChannel<AddedDataPair<PlayerProfile>>) { - AddedGlobalT.sendAllAddedData(channel) + PrivilegesGlobalT.sendAllAddedData(channel) channel.close() } - override fun readGlobalAddedData(owner: PlayerProfile): MutableAddedDataMap { - return AddedGlobalT.readAddedData(ProfilesT.getId(owner.toOwnerProfile()) ?: return hashMapOf()) + override fun readGlobalPrivileges(owner: PlayerProfile): MutablePrivilegeMap { + return PrivilegesGlobalT.readPrivileges(ProfilesT.getId(owner.toOwnerProfile()) ?: return hashMapOf()) } - override fun setGlobalPlayerStatus(owner: PlayerProfile, player: PlayerProfile, status: AddedStatus) { - AddedGlobalT.setPlayerStatus(owner, player.toRealProfile(), status) + override fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege) { + PrivilegesGlobalT.setPrivilege(owner, player.toRealProfile(), privilege) } private fun rowToParcelData(row: ResultRow) = ParcelDataHolder().apply { @@ -266,7 +267,7 @@ class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSi System.arraycopy(source, 0, target, 0, source.size.clampMax(target.size)) } - addedMap = AddedLocalT.readAddedData(id) + map = PrivilegesLocalT.readPrivileges(id) } } 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 696b84c..6f6ad6b 100644 --- a/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt +++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt @@ -26,7 +26,7 @@ abstract class IdTransactionsTable<TableT : IdTransactionsTable<TableT, QueryObj 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") + ?: throw ExposedDatabaseException("This should not happen - failed to insert ${objName()} and get its number") } abstract fun getId(obj: QueryObj): Int? 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 f41d545..600255e 100644 --- a/src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt +++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt @@ -3,30 +3,30 @@ package io.dico.parcels2.storage.exposed import io.dico.parcels2.* -import io.dico.parcels2.AddedStatus.ALLOWED -import io.dico.parcels2.AddedStatus.DEFAULT +import io.dico.parcels2.Privilege.DEFAULT import kotlinx.coroutines.channels.SendChannel import org.jetbrains.exposed.sql.* -import java.util.UUID -object AddedLocalT : AddedTable<ParcelId>("parcels_added_local", ParcelsT) -object AddedGlobalT : AddedTable<PlayerProfile>("parcels_added_global", ProfilesT) +object PrivilegesLocalT : PrivilegesTable<ParcelId>("parcels_added_local", ParcelsT) +object PrivilegesGlobalT : PrivilegesTable<PlayerProfile>("parcels_added_global", ProfilesT) object ParcelOptionsT : Table("parcel_options") { val parcel_id = integer("parcel_id").primaryKey().references(ParcelsT.id, ReferenceOption.CASCADE) - val interact_bitmask = binary("interact_bitmask", 4).default(ByteArray(4) { 0 }) // all zero by default + val interact_bitmask = binary("interact_bitmask", 4) } -typealias AddedStatusSendChannel<AttachT> = SendChannel<Pair<AttachT, MutableAddedDataMap>> +typealias PrivilegesSendChannel<AttachT> = SendChannel<Pair<AttachT, MutablePrivilegeMap>> -sealed class AddedTable<AttachT>(name: String, val idTable: IdTransactionsTable<*, AttachT>) : Table(name) { +sealed class PrivilegesTable<AttachT>(name: String, val idTable: IdTransactionsTable<*, AttachT>) : Table(name) { val attach_id = integer("attach_id").references(idTable.id, ReferenceOption.CASCADE) val profile_id = integer("profile_id").references(ProfilesT.id, ReferenceOption.CASCADE) - val allowed_flag = bool("allowed_flag") + val privilege = integer("privilege") val index_pair = uniqueIndexR("index_pair", attach_id, profile_id) - fun setPlayerStatus(attachedOn: AttachT, player: PlayerProfile.Real, status: AddedStatus) { - if (status == DEFAULT) { + fun setPrivilege(attachedOn: AttachT, player: PlayerProfile.Real, privilege: Privilege) { + privilege.requireNonTransient() + + if (privilege == DEFAULT) { val player_id = ProfilesT.getId(player) ?: return idTable.getId(attachedOn)?.let { holder -> deleteWhere { (attach_id eq holder) and (profile_id eq player_id) } @@ -39,28 +39,28 @@ sealed class AddedTable<AttachT>(name: String, val idTable: IdTransactionsTable< upsert(conflictIndex = index_pair) { it[attach_id] = holder it[profile_id] = player_id - it[allowed_flag] = status == ALLOWED + it[this.privilege] = privilege.number } } - fun readAddedData(id: Int): MutableAddedDataMap { - val list = slice(profile_id, allowed_flag).select { attach_id eq id } - val result = MutableAddedDataMap() + fun readPrivileges(id: Int): MutablePrivilegeMap { + val list = slice(profile_id, privilege).select { attach_id eq id } + val result = MutablePrivilegeMap() for (row in list) { val profile = ProfilesT.getRealItem(row[profile_id]) ?: continue - result[profile] = row[allowed_flag].asAddedStatus() + result[profile] = Privilege.safeGetByNumber(row[privilege]) ?: continue } return result } - fun sendAllAddedData(channel: AddedStatusSendChannel<AttachT>) { + fun sendAllAddedData(channel: PrivilegesSendChannel<AttachT>) { val iterator = selectAll().orderBy(attach_id).iterator() if (iterator.hasNext()) { val firstRow = iterator.next() var id: Int = firstRow[attach_id] var attach: AttachT? = null - var map: MutableAddedDataMap? = null + var map: MutablePrivilegeMap? = null fun initAttachAndMap() { attach = idTable.getItem(id) @@ -90,14 +90,12 @@ sealed class AddedTable<AttachT>(name: String, val idTable: IdTransactionsTable< } val profile = ProfilesT.getRealItem(row[profile_id]) ?: continue - val status = row[allowed_flag].asAddedStatus() - map!![profile] = status + val privilege = Privilege.safeGetByNumber(row[privilege]) ?: continue + map!![profile] = privilege } sendIfPresent() } } - private inline fun Boolean?.asAddedStatus() = if (this == null) AddedStatus.DEFAULT else if (this) 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 0dcf36d..b514f87 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 @@ -66,11 +66,11 @@ class PlotmeMigration(val options: PlotmeMigrationOptions) : Migration { return ParcelId(world, row[table.px], row[table.pz]) } - fun PlotmePlotPlayerMap.transmitPlotmeAddedTable(kind: AddedStatus) { + fun PlotmePlotPlayerMap.transmitPlotmeAddedTable(kind: Privilege) { 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) + target.setLocalPrivilege(parcel, profile, kind) } } @@ -89,12 +89,12 @@ class PlotmeMigration(val options: PlotmeMigrationOptions) : Migration { mlogger.info("Transmitting data from plotmeallowed table") transaction { - PlotmeAllowedT.transmitPlotmeAddedTable(AddedStatus.ALLOWED) + PlotmeAllowedT.transmitPlotmeAddedTable(Privilege.CAN_BUILD) } mlogger.info("Transmitting data from plotmedenied table") transaction { - PlotmeDeniedT.transmitPlotmeAddedTable(AddedStatus.BANNED) + PlotmeDeniedT.transmitPlotmeAddedTable(Privilege.BANNED) } mlogger.warn("Data has been **transmitted**.") |