Skip to content

Commit

Permalink
Fix towny integration (#87)
Browse files Browse the repository at this point in the history
* fix vault provider

* fix plugin

* fix tests

* fix database recreation

* up version
  • Loading branch information
makeevrserg authored Sep 13, 2024
1 parent 4418fd6 commit 2c7339a
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 70 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ makeevrserg.java.ktarget=21
# Project
makeevrserg.project.name=AspeKt
makeevrserg.project.group=ru.astrainteractive.aspekt
makeevrserg.project.version.string=2.28.1
makeevrserg.project.version.string=2.28.2
makeevrserg.project.description=Essentials plugin for EmpireProjekt
makeevrserg.project.developers=makeevrserg|Makeev Roman|[email protected]
makeevrserg.project.url=https://empireprojekt.ru
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ package ru.astrainteractive.aspekt.module.economy.database.dao.impl

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.JoinType
import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.SortOrder
import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.andWhere
import org.jetbrains.exposed.sql.deleteWhere
import org.jetbrains.exposed.sql.insert
Expand All @@ -27,8 +30,16 @@ internal class EconomyDaoImpl(
private val databaseFlow: Flow<Database>
) : EconomyDao,
Logger by JUtiltLogger("EconomyDao") {
private val mutex = Mutex()

private suspend fun <T> withMutex(block: suspend () -> T): T {
return mutex.withLock { block.invoke() }
}

private suspend fun currentDatabase(): Database {
return databaseFlow.first()
}

private suspend fun currentDatabase() = databaseFlow.first()
private fun ResultRow.toCurrency() = CurrencyModel(
id = this[CurrencyTable.id].value,
name = this[CurrencyTable.name],
Expand All @@ -55,7 +66,7 @@ internal class EconomyDaoImpl(
}
}

override suspend fun updateCurrencies(currencies: List<CurrencyModel>) {
override suspend fun updateCurrencies(currencies: List<CurrencyModel>) = withMutex {
val existingCurrencies = getAllCurrencies()
val nonExistingCurrencies = existingCurrencies
.map(CurrencyModel::id)
Expand Down Expand Up @@ -156,16 +167,29 @@ internal class EconomyDaoImpl(
it[PlayerCurrencyTable.currencyId] = currency.currencyModel.id
}
} else {
PlayerCurrencyTable.update(where = { PlayerCurrencyTable.currencyId eq currency.currencyModel.id }) {
it[PlayerCurrencyTable.amount] = currency.balance
it[PlayerCurrencyTable.lastUsername] = currency.playerModel.name
}
PlayerCurrencyTable.update(
where = {
PlayerCurrencyTable.currencyId.eq(currency.currencyModel.id).and {
PlayerCurrencyTable.uuid.eq(currency.playerModel.uuid)
}
},
body = {
it[PlayerCurrencyTable.amount] = currency.balance
it[PlayerCurrencyTable.lastUsername] = currency.playerModel.name
}
)
}
}

override suspend fun transfer(from: PlayerModel, to: PlayerModel, amount: Double, currencyId: String): Boolean {
return newSuspendedTransaction(db = currentDatabase()) {
val fromPlayerCurrency = findPlayerCurrency(from.uuid, currencyId) ?: return@newSuspendedTransaction false
override suspend fun transfer(
from: PlayerModel,
to: PlayerModel,
amount: Double,
currencyId: String
): Boolean = withMutex {
newSuspendedTransaction(db = currentDatabase()) {
val fromPlayerCurrency =
findPlayerCurrency(from.uuid, currencyId) ?: return@newSuspendedTransaction false
if (fromPlayerCurrency.balance - amount < 0) return@newSuspendedTransaction false
val toPlayerCurrency = PlayerCurrency(
playerModel = PlayerModel(
Expand All @@ -183,8 +207,8 @@ internal class EconomyDaoImpl(
}
}

override suspend fun updatePlayerCurrency(currency: PlayerCurrency) {
return newSuspendedTransaction(db = currentDatabase()) {
override suspend fun updatePlayerCurrency(currency: PlayerCurrency) = withMutex {
newSuspendedTransaction(db = currentDatabase()) {
updatePlayerCurrencyWithoutTransaction(currency)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package ru.astrainteractive.aspekt.module.economy.database.di

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.shareIn
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.SchemaUtils
import org.jetbrains.exposed.sql.Slf4jSqlDebugLogger
Expand All @@ -17,13 +19,16 @@ import ru.astrainteractive.aspekt.module.economy.database.table.PlayerCurrencyTa
import ru.astrainteractive.astralibs.exposed.factory.DatabaseFactory
import ru.astrainteractive.astralibs.exposed.model.DatabaseConfiguration
import ru.astrainteractive.astralibs.lifecycle.Lifecycle
import ru.astrainteractive.astralibs.logging.JUtiltLogger
import ru.astrainteractive.astralibs.logging.Logger
import ru.astrainteractive.astralibs.util.FlowExt.mapCached
import ru.astrainteractive.klibs.kstorage.api.flow.StateFlowKrate
import java.io.File
import kotlin.coroutines.CoroutineContext

internal interface EconomyDatabaseModule {
val lifecycle: Lifecycle
val databaseFlow: Flow<Database>
val economyDao: EconomyDao
val cachedDao: CachedDao

Expand All @@ -32,20 +37,23 @@ internal interface EconomyDatabaseModule {
dataFolder: File,
coroutineScope: CoroutineScope,
ioDispatcher: CoroutineContext
) : EconomyDatabaseModule {
private val databaseFlow: Flow<Database> = dbConfig.cachedStateFlow.mapCached { dbConfig, previous ->
previous?.connector?.invoke()?.close()
val database = DatabaseFactory(dataFolder).create(dbConfig)
TransactionManager.manager.defaultIsolationLevel = java.sql.Connection.TRANSACTION_SERIALIZABLE
transaction(database) {
addLogger(Slf4jSqlDebugLogger)
SchemaUtils.create(
CurrencyTable,
PlayerCurrencyTable
)
}
database
}
) : EconomyDatabaseModule, Logger by JUtiltLogger("EconomyDatabaseModule") {

override val databaseFlow: Flow<Database> = dbConfig.cachedStateFlow
.mapCached<DatabaseConfiguration, Database> { dbConfig, previous ->
previous?.connector?.invoke()?.close()
previous?.run(TransactionManager::closeAndUnregister)
val database = DatabaseFactory(dataFolder).create(dbConfig)
TransactionManager.manager.defaultIsolationLevel = java.sql.Connection.TRANSACTION_SERIALIZABLE
transaction(database) {
addLogger(Slf4jSqlDebugLogger)
SchemaUtils.create(
CurrencyTable,
PlayerCurrencyTable
)
}
database
}.shareIn(coroutineScope, SharingStarted.Eagerly, 1)

override val economyDao: EconomyDao = EconomyDaoImpl(databaseFlow)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ interface EconomyModule {
override val lifecycle: Lifecycle = Lifecycle.Lambda(
onEnable = { lifecycles.forEach(Lifecycle::onEnable) },
onReload = {
error { "#onReload - reload of economy module is not supported! Consider full server reload." }
lifecycles.forEach(Lifecycle::onReload)
error { "#onReload - reload of economy module may break everything! Consider full server reload." }
},
onDisable = { lifecycles.forEach(Lifecycle::onDisable) }
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,23 @@ import ru.astrainteractive.aspekt.module.economy.database.dao.EconomyDao
import ru.astrainteractive.aspekt.module.economy.model.CurrencyModel
import ru.astrainteractive.aspekt.module.economy.model.PlayerCurrency
import ru.astrainteractive.aspekt.module.economy.model.PlayerModel
import ru.astrainteractive.astralibs.logging.JUtiltLogger
import ru.astrainteractive.astralibs.logging.Logger
import java.text.DecimalFormat
import kotlin.time.Duration.Companion.seconds

@Suppress("TooManyFunctions")
internal class VaultEconomyProvider(
private val primaryCurrencyModel: CurrencyModel,
private val dao: EconomyDao
) : Economy {
) : Economy, Logger by JUtiltLogger("Economy-${primaryCurrencyModel.id}") {
/**
* Run blocking function but try not to block main thread
*/
private fun <T> runIoBlocking(block: suspend CoroutineScope.() -> T): T {
return if (Bukkit.isPrimaryThread()) {
runBlocking(Dispatchers.IO) {
flow<T> { block.invoke(this@runBlocking) }
flow<T> { emit(block.invoke(this@runBlocking)) }
.timeout(timeout = 15.seconds)
.first()
}
Expand All @@ -37,6 +39,16 @@ internal class VaultEconomyProvider(
}
}

private fun notImplemented(message: String): EconomyResponse {
return EconomyResponse(0.0, 0.0, EconomyResponse.ResponseType.NOT_IMPLEMENTED, message)
}

private fun requireOfflinePlayer(name: String?): OfflinePlayer {
name ?: error("The requested name is null")
val offlinePlayer = Bukkit.getOfflinePlayer(name)
return offlinePlayer
}

override fun isEnabled(): Boolean = true

override fun getName(): String = "AspeKt Economy of ${primaryCurrencyModel.id} currency"
Expand All @@ -53,38 +65,48 @@ internal class VaultEconomyProvider(

override fun currencyNameSingular(): String = primaryCurrencyModel.name

override fun hasAccount(p0: String?): Boolean = error("Method with player name is not supported")
override fun hasAccount(playerName: String?): Boolean = hasAccount(requireOfflinePlayer(playerName))

override fun hasAccount(player: OfflinePlayer?): Boolean = runIoBlocking {
player ?: return@runIoBlocking false
dao.findPlayerCurrency(player.uniqueId.toString(), currencyId = primaryCurrencyModel.id) != null
}

override fun hasAccount(p0: String?, p1: String?): Boolean = error("Method with player name is not supported")
override fun hasAccount(playerName: String?, worldName: String?): Boolean {
return hasAccount(requireOfflinePlayer(playerName), worldName)
}

override fun hasAccount(player: OfflinePlayer?, p1: String?): Boolean = hasAccount(player)

override fun getBalance(p0: String?): Double = error("Method with player name is not supported")
override fun getBalance(playerName: String?): Double = getBalance(requireOfflinePlayer(playerName))

override fun getBalance(player: OfflinePlayer?): Double = runIoBlocking {
player ?: return@runIoBlocking 0.0
dao.findPlayerCurrency(player.uniqueId.toString(), currencyId = primaryCurrencyModel.id)?.balance ?: 0.0
}

override fun getBalance(p0: String?, p1: String?): Double = error("Method with player name is not supported")
override fun getBalance(playerName: String?, world: String?): Double {
return getBalance(requireOfflinePlayer(playerName), world)
}

override fun getBalance(player: OfflinePlayer?, p1: String?): Double = getBalance(player)

override fun has(p0: String?, p1: Double): Boolean = error("Method with player name is not supported")
override fun has(playerName: String?, amount: Double): Boolean {
return has(requireOfflinePlayer(playerName), amount)
}

override fun has(p0: OfflinePlayer?, amount: Double): Boolean = getBalance(p0) >= amount

override fun has(p0: String?, p1: String?, p2: Double): Boolean = error("Method with player name is not supported")
override fun has(playerName: String?, worldName: String?, amount: Double): Boolean {
return has(requireOfflinePlayer(playerName), worldName, amount)
}

override fun has(p0: OfflinePlayer?, p1: String?, amount: Double): Boolean = getBalance(p0) >= amount
override fun has(p0: OfflinePlayer?, p1: String?, amount: Double): Boolean {
return getBalance(p0) >= amount
}

override fun withdrawPlayer(p0: String?, p1: Double): EconomyResponse {
error("Method with player name is not supported")
override fun withdrawPlayer(playerName: String?, amount: Double): EconomyResponse {
return withdrawPlayer(requireOfflinePlayer(playerName), amount)
}

override fun withdrawPlayer(player: OfflinePlayer?, amount: Double): EconomyResponse {
Expand Down Expand Up @@ -115,14 +137,14 @@ internal class VaultEconomyProvider(
)
}

override fun withdrawPlayer(p0: String?, p1: String?, p2: Double): EconomyResponse {
error("Method with player name is not supported")
override fun withdrawPlayer(playerName: String?, worldName: String?, amount: Double): EconomyResponse {
return withdrawPlayer(requireOfflinePlayer(playerName), worldName, amount)
}

override fun withdrawPlayer(p0: OfflinePlayer?, p1: String?, p2: Double): EconomyResponse = withdrawPlayer(p0, p2)

override fun depositPlayer(p0: String?, p1: Double): EconomyResponse {
error("Method with player name is not supported")
override fun depositPlayer(playerName: String?, amount: Double): EconomyResponse {
return depositPlayer(requireOfflinePlayer(playerName), amount)
}

override fun depositPlayer(player: OfflinePlayer?, amount: Double): EconomyResponse {
Expand All @@ -149,44 +171,55 @@ internal class VaultEconomyProvider(
)
}

override fun depositPlayer(p0: String?, p1: String?, p2: Double): EconomyResponse {
error("Method with player name is not supported")
override fun depositPlayer(playerName: String?, worldName: String?, amount: Double): EconomyResponse {
return depositPlayer(requireOfflinePlayer(playerName), worldName, amount)
}

override fun depositPlayer(player: OfflinePlayer?, p1: String?, amount: Double): EconomyResponse =
depositPlayer(player, amount)
override fun depositPlayer(player: OfflinePlayer?, p1: String?, amount: Double): EconomyResponse {
return depositPlayer(player, amount)
}

override fun createBank(p0: String?, p1: String?): EconomyResponse = error("Banks not implemented")
override fun createBank(p0: String?, p1: String?): EconomyResponse = notImplemented("Banks not implemented")

override fun createBank(p0: String?, p1: OfflinePlayer?): EconomyResponse = error("Banks not implemented")
override fun createBank(p0: String?, p1: OfflinePlayer?): EconomyResponse = notImplemented("Banks not implemented")

override fun deleteBank(p0: String?): EconomyResponse = error("Banks not implemented")
override fun deleteBank(p0: String?): EconomyResponse = notImplemented("Banks not implemented")

override fun bankBalance(p0: String?): EconomyResponse = error("Banks not implemented")
override fun bankBalance(p0: String?): EconomyResponse = notImplemented("Banks not implemented")

override fun bankHas(p0: String?, p1: Double): EconomyResponse = error("Banks not implemented")
override fun bankHas(p0: String?, p1: Double): EconomyResponse = notImplemented("Banks not implemented")

override fun bankWithdraw(p0: String?, p1: Double): EconomyResponse = error("Banks not implemented")
override fun bankWithdraw(p0: String?, p1: Double): EconomyResponse = notImplemented("Banks not implemented")

override fun bankDeposit(p0: String?, p1: Double): EconomyResponse = error("Banks not implemented")
override fun bankDeposit(p0: String?, p1: Double): EconomyResponse = notImplemented("Banks not implemented")

override fun isBankOwner(p0: String?, p1: String?): EconomyResponse = error("Banks not implemented")
override fun isBankOwner(p0: String?, p1: String?): EconomyResponse = notImplemented("Banks not implemented")

override fun isBankOwner(p0: String?, p1: OfflinePlayer?): EconomyResponse = error("Banks not implemented")
override fun isBankOwner(p0: String?, p1: OfflinePlayer?): EconomyResponse = notImplemented("Banks not implemented")

override fun isBankMember(p0: String?, p1: String?): EconomyResponse = error("Banks not implemented")
override fun isBankMember(p0: String?, p1: String?): EconomyResponse = notImplemented("Banks not implemented")

override fun isBankMember(p0: String?, p1: OfflinePlayer?): EconomyResponse = error("Banks not implemented")
override fun isBankMember(p0: String?, p1: OfflinePlayer?): EconomyResponse {
return notImplemented("Banks not implemented")
}

override fun getBanks(): MutableList<String> = error("Banks not implemented")
override fun getBanks(): MutableList<String> = mutableListOf()

override fun createPlayerAccount(p0: String?): Boolean = error("Creating accounts not implemented")
override fun createPlayerAccount(playerName: String?): Boolean {
return createPlayerAccount(requireOfflinePlayer(playerName))
}

override fun createPlayerAccount(p0: OfflinePlayer?): Boolean = error("Creating accounts not implemented")
override fun createPlayerAccount(player: OfflinePlayer?): Boolean {
player ?: return false
if (hasAccount(player)) return false
return depositPlayer(player, 0.0).transactionSuccess()
}

override fun createPlayerAccount(p0: String?, p1: String?): Boolean = error("Creating accounts not implemented")
override fun createPlayerAccount(playerName: String?, worldName: String?): Boolean {
return createPlayerAccount(requireOfflinePlayer(playerName), worldName)
}

override fun createPlayerAccount(p0: OfflinePlayer?, p1: String?): Boolean {
error("Creating accounts not implemented")
override fun createPlayerAccount(player: OfflinePlayer?, worldName: String?): Boolean {
return createPlayerAccount(player)
}
}
Loading

0 comments on commit 2c7339a

Please sign in to comment.