diff --git a/src/main/kotlin/net/trueog/diamondbankog/DiamondBankAPI.kt b/src/main/kotlin/net/trueog/diamondbankog/DiamondBankAPIJava.kt similarity index 99% rename from src/main/kotlin/net/trueog/diamondbankog/DiamondBankAPI.kt rename to src/main/kotlin/net/trueog/diamondbankog/DiamondBankAPIJava.kt index 3f4c773..1f7e1e0 100644 --- a/src/main/kotlin/net/trueog/diamondbankog/DiamondBankAPI.kt +++ b/src/main/kotlin/net/trueog/diamondbankog/DiamondBankAPIJava.kt @@ -11,7 +11,7 @@ import java.util.* import java.util.concurrent.CompletableFuture @OptIn(DelicateCoroutinesApi::class) -class DiamondBankAPI(private var postgreSQL: PostgreSQL) { +class DiamondBankAPIJava(private var postgreSQL: PostgreSQL) { sealed class DiamondBankException(message: String) : Exception(message) { object TransactionsLockedException : DiamondBankException("Transactions for player are locked") { @Suppress("unused") @@ -399,7 +399,12 @@ class DiamondBankAPI(private var postgreSQL: PostgreSQL) { DiamondBankException.OtherException::class ) @Suppress("unused") - fun withdrawFromPlayer(uuid: UUID, shards: Int, transactionReason: String, notes: String?): CompletableFuture { + fun withdrawFromPlayer( + uuid: UUID, + shards: Int, + transactionReason: String, + notes: String? + ): CompletableFuture { if (DiamondBankOG.economyDisabled) throw DiamondBankException.EconomyDisabledException return DiamondBankOG.scope.future { diff --git a/src/main/kotlin/net/trueog/diamondbankog/DiamondBankAPIKotlin.kt b/src/main/kotlin/net/trueog/diamondbankog/DiamondBankAPIKotlin.kt new file mode 100644 index 0000000..294872a --- /dev/null +++ b/src/main/kotlin/net/trueog/diamondbankog/DiamondBankAPIKotlin.kt @@ -0,0 +1,531 @@ +package net.trueog.diamondbankog + +import kotlinx.coroutines.DelicateCoroutinesApi +import net.trueog.diamondbankog.ErrorHandler.handleError +import net.trueog.diamondbankog.PostgreSQL.PlayerShards +import net.trueog.diamondbankog.PostgreSQL.ShardType +import net.trueog.diamondbankog.TransactionLock.LockResult +import org.bukkit.Bukkit +import java.util.* + +@OptIn(DelicateCoroutinesApi::class) +class DiamondBankAPIKotlin(private var postgreSQL: PostgreSQL) { + sealed class DiamondBankException(message: String) : Exception(message) { + object TransactionsLockedException : DiamondBankException("Transactions for player are locked") { + @Suppress("unused") + private fun readResolve(): Any = TransactionsLockedException + } + + object EconomyDisabledException : DiamondBankException("Economy is disabled") { + @Suppress("unused") + private fun readResolve(): Any = EconomyDisabledException + } + + object InvalidPlayerException : DiamondBankException("Invalid player") { + @Suppress("unused") + private fun readResolve(): Any = InvalidPlayerException + } + + object PayerNotOnlineException : DiamondBankException("Payer is not online") { + @Suppress("unused") + private fun readResolve(): Any = PayerNotOnlineException + } + + object PlayerNotOnlineException : DiamondBankException("Player is not online") { + @Suppress("unused") + private fun readResolve(): Any = PlayerNotOnlineException + } + + object OtherException : DiamondBankException("Other exception") { + @Suppress("unused") + private fun readResolve(): Any = OtherException + } + } + + /** + * WARNING: blocking, if the player has a transaction lock applied this function will wait until its released + * @param transactionReason the reason for this transaction for in the transaction log + * @param notes any specifics for this transaction that may be nice to know for in the transaction log + */ + @Suppress("unused") + suspend fun blockingAddToPlayerBankShards( + uuid: UUID, + shards: Int, + transactionReason: String, + notes: String? + ): Result { + if (DiamondBankOG.economyDisabled) return Result.failure(DiamondBankException.EconomyDisabledException) + return DiamondBankOG.transactionLock.withLockSuspend(uuid) { + val error = postgreSQL.addToPlayerShards(uuid, shards, ShardType.BANK) + if (error) { + return@withLockSuspend Result.failure(DiamondBankException.OtherException) + } + + val transactionLogError = DiamondBankOG.postgreSQL.insertTransactionLog( + uuid, + shards, + null, + transactionReason, + notes + ) + if (transactionLogError) { + handleError( + uuid, + shards, + null, + null, + true + ) + } + + Result.success(Unit) + } + } + + /** + * @param transactionReason the reason for this transaction for in the transaction log + * @param notes any specifics for this transaction that may be nice to know for in the transaction log + */ + @Suppress("unused") + suspend fun addToPlayerBankShards( + uuid: UUID, + shards: Int, + transactionReason: String, + notes: String? + ): Result { + if (DiamondBankOG.economyDisabled) return Result.failure(DiamondBankException.EconomyDisabledException) + + return when (val result = DiamondBankOG.transactionLock.tryWithLockSuspend(uuid) { + val error = postgreSQL.addToPlayerShards(uuid, shards, ShardType.BANK) + if (error) { + return@tryWithLockSuspend Result.failure(DiamondBankException.OtherException) + } + + val transactionLogError = DiamondBankOG.postgreSQL.insertTransactionLog( + uuid, + shards, + null, + transactionReason, + notes + ) + if (transactionLogError) { + handleError( + uuid, + shards, + null, + null, + true + ) + } + + Result.success(Unit) + }) { + is LockResult.Acquired -> { + result.result + } + + LockResult.Failed -> { + return Result.failure(DiamondBankException.TransactionsLockedException) + } + } + } + + /** + * WARNING: blocking, if the player has a transaction lock applied this function will wait until its released + * @param transactionReason the reason for this transaction for in the transaction log + * @param notes any specifics for this transaction that may be nice to know for in the transaction log + */ + @Suppress("unused") + suspend fun blockingSubtractFromPlayerBankShards( + uuid: UUID, + shards: Int, + transactionReason: String, + notes: String? + ): Result { + if (DiamondBankOG.economyDisabled) return Result.failure(DiamondBankException.EconomyDisabledException) + + return DiamondBankOG.transactionLock.withLockSuspend(uuid) { + val error = postgreSQL.subtractFromPlayerShards( + uuid, + shards, + ShardType.BANK + ) + if (error) { + return@withLockSuspend Result.failure(DiamondBankException.OtherException) + } + + val transactionLogError = DiamondBankOG.postgreSQL.insertTransactionLog( + uuid, + shards, + null, + transactionReason, + notes + ) + if (transactionLogError) { + handleError( + uuid, + shards, + null, + null, + true + ) + } + + Result.success(Unit) + } + } + + /** + * @param transactionReason the reason for this transaction for in the transaction log + * @param notes any specifics for this transaction that may be nice to know for in the transaction log + */ + @Suppress("unused") + suspend fun subtractFromPlayerBankShards( + uuid: UUID, + shards: Int, + transactionReason: String, + notes: String? + ): Result { + if (DiamondBankOG.economyDisabled) return Result.failure(DiamondBankException.EconomyDisabledException) + + return when (val result = DiamondBankOG.transactionLock.tryWithLockSuspend(uuid) { + val error = postgreSQL.subtractFromPlayerShards( + uuid, + shards, + ShardType.BANK + ) + if (error) { + return@tryWithLockSuspend Result.failure(DiamondBankException.OtherException) + } + + val transactionLogError = DiamondBankOG.postgreSQL.insertTransactionLog( + uuid, + shards, + null, + transactionReason, + notes + ) + if (transactionLogError) { + handleError( + uuid, + shards, + null, + null, + true + ) + } + + Result.success(Unit) + }) { + is LockResult.Acquired -> { + result.result + } + + LockResult.Failed -> { + Result.failure(DiamondBankException.TransactionsLockedException) + } + } + } + + /** + * WARNING: blocking, if the player has a transaction lock applied this function will wait until its released + */ + @Suppress("unused") + suspend fun blockingGetPlayerShards(uuid: UUID, type: ShardType): Result { + if (DiamondBankOG.economyDisabled) return Result.failure(DiamondBankException.EconomyDisabledException) + + return DiamondBankOG.transactionLock.withLockSuspend(uuid) { + val playerShards = postgreSQL.getPlayerShards(uuid, type) + if (playerShards.isNeededShardTypeNull(type)) { + return@withLockSuspend Result.failure(DiamondBankException.OtherException) + } + + Result.success(playerShards) + } + } + + @Suppress("unused") + suspend fun getPlayerShards(uuid: UUID, type: ShardType): Result { + if (DiamondBankOG.economyDisabled) return Result.failure(DiamondBankException.EconomyDisabledException) + + return when (val result = DiamondBankOG.transactionLock.tryWithLockSuspend(uuid) { + val playerShards = postgreSQL.getPlayerShards(uuid, type) + if (playerShards.isNeededShardTypeNull(type)) { + return@tryWithLockSuspend Result.failure(DiamondBankException.OtherException) + } + Result.success(playerShards) + }) { + is LockResult.Acquired -> { + result.result + } + + LockResult.Failed -> { + return Result.failure(DiamondBankException.TransactionsLockedException) + } + } + } + + @Suppress("unused") + suspend fun getBaltop(offset: Int): Result> { + if (DiamondBankOG.economyDisabled) return Result.failure(DiamondBankException.EconomyDisabledException) + + val baltop = postgreSQL.getBaltop(offset) + if (baltop == null) { + return Result.failure(DiamondBankException.OtherException) + } + return Result.success(baltop) + } + + /** + * WARNING: blocking, if the player has a transaction lock applied this function will wait until its released + * @param transactionReason the reason for this transaction for in the transaction log + * @param notes any specifics for this transaction that may be nice to know for in the transaction log + */ + @Suppress("unused") + suspend fun blockingWithdrawFromPlayer( + uuid: UUID, + shards: Int, + transactionReason: String, + notes: String? + ): Result { + if (DiamondBankOG.economyDisabled) return Result.failure(DiamondBankException.EconomyDisabledException) + + return DiamondBankOG.transactionLock.withLockSuspend(uuid) { + val player = Bukkit.getPlayer(uuid) ?: Bukkit.getOfflinePlayer(uuid) + if (!player.hasPlayedBefore()) return@withLockSuspend Result.failure(DiamondBankException.InvalidPlayerException) + if (!player.isOnline) return@withLockSuspend Result.failure(DiamondBankException.PlayerNotOnlineException) + val playerPlayer = + player.player ?: return@withLockSuspend Result.failure(DiamondBankException.InvalidPlayerException) + + val notRemoved = WithdrawHelper.withdrawFromPlayer(playerPlayer, shards) + if (notRemoved != 0) { + handleError( + uuid, + shards, + null + ) + return@withLockSuspend Result.failure(DiamondBankException.OtherException) + } + + val transactionLogError = DiamondBankOG.postgreSQL.insertTransactionLog( + uuid, + shards, + null, + transactionReason, + notes + ) + if (transactionLogError) { + handleError( + uuid, + shards, + null, + null, + true + ) + } + + Result.success(Unit) + } + } + + /** + * @param transactionReason the reason for this transaction for in the transaction log + * @param notes any specifics for this transaction that may be nice to know for in the transaction log + */ + @Suppress("unused") + suspend fun withdrawFromPlayer(uuid: UUID, shards: Int, transactionReason: String, notes: String?): Result { + if (DiamondBankOG.economyDisabled) return Result.failure(DiamondBankException.EconomyDisabledException) + + return when (val result = DiamondBankOG.transactionLock.tryWithLockSuspend(uuid) { + val player = Bukkit.getPlayer(uuid) ?: Bukkit.getOfflinePlayer(uuid) + if (!player.hasPlayedBefore()) return@tryWithLockSuspend Result.failure(DiamondBankException.InvalidPlayerException) + if (!player.isOnline) return@tryWithLockSuspend Result.failure(DiamondBankException.PlayerNotOnlineException) + val playerPlayer = + player.player ?: return@tryWithLockSuspend Result.failure(DiamondBankException.InvalidPlayerException) + + val notRemoved = WithdrawHelper.withdrawFromPlayer(playerPlayer, shards) + if (notRemoved != 0) { + handleError( + uuid, + shards, + null + ) + return@tryWithLockSuspend Result.failure(DiamondBankException.OtherException) + } + + val transactionLogError = DiamondBankOG.postgreSQL.insertTransactionLog( + uuid, + shards, + null, + transactionReason, + notes + ) + if (transactionLogError) { + handleError( + uuid, + shards, + null, + null, + true + ) + } + + Result.success(Unit) + }) { + is LockResult.Acquired -> { + result.result + } + + LockResult.Failed -> { + Result.failure(DiamondBankException.TransactionsLockedException) + } + } + } + + /** + * WARNING: blocking, if the player has a transaction lock applied this function will wait until its released + * @param transactionReason the reason for this transaction for in the transaction log + * @param notes any specifics for this transaction that may be nice to know for in the transaction log + */ + @Suppress("unused") + suspend fun blockingPlayerPayPlayer( + payerUuid: UUID, + receiverUuid: UUID, + shards: Int, + transactionReason: String, + notes: String? + ): Result { + if (DiamondBankOG.economyDisabled) return Result.failure(DiamondBankException.EconomyDisabledException) + + return DiamondBankOG.transactionLock.withLockSuspend(payerUuid) { + val sender = Bukkit.getPlayer(payerUuid) ?: Bukkit.getOfflinePlayer(payerUuid) + if (!sender.hasPlayedBefore()) return@withLockSuspend Result.failure(DiamondBankException.InvalidPlayerException) + if (!sender.isOnline) return@withLockSuspend Result.failure(DiamondBankException.PayerNotOnlineException) + val senderPlayer = + sender.player ?: return@withLockSuspend Result.failure(DiamondBankException.InvalidPlayerException) + + val receiver = Bukkit.getPlayer(receiverUuid) ?: Bukkit.getOfflinePlayer(receiverUuid) + if (!receiver.hasPlayedBefore()) return@withLockSuspend Result.failure(DiamondBankException.InvalidPlayerException) + + val notRemoved = WithdrawHelper.withdrawFromPlayer(senderPlayer, shards) + if (notRemoved != 0) { + handleError( + payerUuid, + shards, + null + ) + return@withLockSuspend Result.failure(DiamondBankException.OtherException) + } + + val error = postgreSQL.addToPlayerShards( + receiver.uniqueId, + shards, + ShardType.BANK + ) + if (error) { + handleError( + sender.uniqueId, + shards, + null + ) + return@withLockSuspend Result.failure(DiamondBankException.OtherException) + } + + val transactionLogError = DiamondBankOG.postgreSQL.insertTransactionLog( + payerUuid, + shards, + receiverUuid, + transactionReason, + notes + ) + if (transactionLogError) { + handleError( + payerUuid, + shards, + null, + receiverUuid, + true + ) + } + + Result.success(Unit) + } + } + + /** + * @param transactionReason the reason for this transaction for in the transaction log + * @param notes any specifics for this transaction that may be nice to know for in the transaction log + */ + @Suppress("unused") + suspend fun playerPayPlayer( + payerUuid: UUID, + receiverUuid: UUID, + shards: Int, + transactionReason: String, + notes: String? + ): Result { + if (DiamondBankOG.economyDisabled) return Result.failure(DiamondBankException.EconomyDisabledException) + + return when (val result = DiamondBankOG.transactionLock.tryWithLockSuspend(payerUuid) { + val sender = Bukkit.getPlayer(payerUuid) ?: Bukkit.getOfflinePlayer(payerUuid) + if (!sender.hasPlayedBefore()) return@tryWithLockSuspend Result.failure(DiamondBankException.InvalidPlayerException) + if (!sender.isOnline) return@tryWithLockSuspend Result.failure(DiamondBankException.PayerNotOnlineException) + val senderPlayer = + sender.player ?: return@tryWithLockSuspend Result.failure(DiamondBankException.InvalidPlayerException) + + val receiver = Bukkit.getPlayer(receiverUuid) ?: Bukkit.getOfflinePlayer(receiverUuid) + if (!receiver.hasPlayedBefore()) return@tryWithLockSuspend Result.failure(DiamondBankException.InvalidPlayerException) + + val notRemoved = WithdrawHelper.withdrawFromPlayer(senderPlayer, shards) + if (notRemoved != 0) { + handleError( + payerUuid, + shards, + null + ) + return@tryWithLockSuspend Result.failure(DiamondBankException.OtherException) + } + + val error = postgreSQL.addToPlayerShards( + receiver.uniqueId, + shards, + ShardType.BANK + ) + if (error) { + handleError( + sender.uniqueId, + shards, + null + ) + return@tryWithLockSuspend Result.failure(DiamondBankException.OtherException) + } + + val transactionLogError = DiamondBankOG.postgreSQL.insertTransactionLog( + payerUuid, + shards, + receiverUuid, + transactionReason, + notes + ) + if (transactionLogError) { + handleError( + payerUuid, + shards, + null, + receiverUuid, + true + ) + } + + Result.success(Unit) + }) { + is LockResult.Acquired -> { + result.result + } + + LockResult.Failed -> { + Result.failure(DiamondBankException.TransactionsLockedException) + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/net/trueog/diamondbankog/DiamondBankOG.kt b/src/main/kotlin/net/trueog/diamondbankog/DiamondBankOG.kt index 2eb7f58..97e9210 100644 --- a/src/main/kotlin/net/trueog/diamondbankog/DiamondBankOG.kt +++ b/src/main/kotlin/net/trueog/diamondbankog/DiamondBankOG.kt @@ -92,9 +92,15 @@ class DiamondBankOG : JavaPlugin() { Shard.createCraftingRecipes() - val diamondBankAPI = DiamondBankAPI(postgreSQL) + val diamondBankAPIJava = DiamondBankAPIJava(postgreSQL) this.server.servicesManager.register( - DiamondBankAPI::class.java, diamondBankAPI, this, + DiamondBankAPIJava::class.java, diamondBankAPIJava, this, + ServicePriority.Normal + ) + + val diamondBankAPIKotlin = DiamondBankAPIKotlin(postgreSQL) + this.server.servicesManager.register( + DiamondBankAPIKotlin::class.java, diamondBankAPIKotlin, this, ServicePriority.Normal ) } diff --git a/src/main/kotlin/net/trueog/diamondbankog/ErrorHandler.kt b/src/main/kotlin/net/trueog/diamondbankog/ErrorHandler.kt index 55de02b..0e4195f 100644 --- a/src/main/kotlin/net/trueog/diamondbankog/ErrorHandler.kt +++ b/src/main/kotlin/net/trueog/diamondbankog/ErrorHandler.kt @@ -4,7 +4,7 @@ import net.trueog.diamondbankog.PostgreSQL.PlayerShards import java.util.* object ErrorHandler { - class EconomyException(message: String) : Exception(message) + class EconomyException(message: String) : RuntimeException(message) /** * Handles the error by throwing, disables the economy unless you specify it shouldn't