diff --git a/.gitignore b/.gitignore index 8a4e0a2..d35fe74 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ target/**/* out/ build/ .gradle/ - +output/ ### IntelliJ IDEA ### /.idea/* !/.idea/runConfigurations diff --git a/build.gradle.kts b/build.gradle.kts index b9415c4..7ef1f96 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,3 @@ -import dev.slne.surf.surfapi.gradle.util.slnePublic -import dev.slne.surf.surfapi.gradle.util.slneReleases - buildscript { repositories { gradlePluginPortal() @@ -11,23 +8,7 @@ buildscript { } } -plugins { - java -} - - allprojects { group = "dev.slne.surf" version = findProperty("version") as String -} - -subprojects { - apply(plugin = "java") - repositories { - slnePublic() - slneReleases() - } - dependencies { - implementation(platform("dev.slne.surf.cloud:surf-cloud-bom:1.21.7+")) - } } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml deleted file mode 100644 index ad6aa21..0000000 --- a/gradle/libs.versions.toml +++ /dev/null @@ -1,5 +0,0 @@ -[versions] -surf-database = "2.0.4-SNAPSHOT" - -[libraries] -surf-database = { module = "dev.slne.surf:surf-database", version.ref = "surf-database" } \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 9766507..9990fe3 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -4,17 +4,9 @@ plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" } -val projects: List> = listOf( - "surf-transaction-api" to "SurfTransactionApi", - "surf-transaction-paper" to "SurfTransactionBukkit", - "surf-transaction-velocity" to "SurfTransactionVelocity", -// "surf-transaction-fallback" to "SurfTransactionFallback" -) - -projects.forEach { (path, _) -> - include(path) -} - +include("surf-transaction-api") +include("surf-transaction-paper") +include("surf-transaction-velocity") include("surf-transaction-core:surf-transaction-core-common") include("surf-transaction-core:surf-transaction-core-client") include("surf-transaction-server") \ No newline at end of file diff --git a/surf-transaction-api/build.gradle.kts b/surf-transaction-api/build.gradle.kts index 0cf71ab..f02e251 100644 --- a/surf-transaction-api/build.gradle.kts +++ b/surf-transaction-api/build.gradle.kts @@ -4,8 +4,8 @@ plugins { id("dev.slne.surf.surfapi.gradle.core") } -dependencies { - compileOnly("dev.slne.surf.cloud:surf-cloud-api-common:1.21.7+") +surfCoreApi { + withCloudCommon() } publishing { diff --git a/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/account/Account.kt b/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/account/Account.kt new file mode 100644 index 0000000..bb11ba8 --- /dev/null +++ b/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/account/Account.kt @@ -0,0 +1,64 @@ +package dev.slne.surf.transaction.api.account + +import dev.slne.surf.cloud.api.common.player.OfflineCloudPlayer +import dev.slne.surf.transaction.api.user.HasTransactions +import dev.slne.surf.transaction.api.util.InternalTransactionApi +import kotlinx.serialization.Serializable +import net.kyori.adventure.text.Component +import java.util.* + +@OptIn(InternalTransactionApi::class) +@Serializable(with = AccountSerializer::class) +interface Account : HasTransactions { + + /** + * Unique identifier for the account. + */ + val accountId: UUID + + /** + * The name of the account. + */ + val name: String + + /** + * The owner of the account. + */ + val owner: OfflineCloudPlayer + + /** + * Indicates whether this account is the default account for the owner. + */ + val defaultAccount: Boolean + + /** + * Converts the account information into a [Component] for display purposes. + * + * @return A [Component] representing the account information, suitable for use in user interfaces. + */ + suspend fun asComponent(): Component + + companion object { + /** + * Retrieves an account by its unique identifier. + * + * @param accountId The unique identifier of the account to retrieve. + * @return The [Account] associated with the given [accountId], or null if no such account exists. + */ + suspend operator fun get(accountId: UUID?): Account? = + accountId?.let { InternalAccountBridge.instance.getAccountByAccountId(it) } + + /** + * Creates a new account with the specified owner and name. + * + * @param owner The owner of the account. + * @param name The name of the account. + * @return An [AccountCreationResult] indicating the success or failure of the account creation. + */ + suspend fun create( + owner: OfflineCloudPlayer, + name: String + ): AccountCreationResult = InternalAccountBridge.instance.createAccount(owner, name) + } + +} \ No newline at end of file diff --git a/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/account/AccountCreationResult.kt b/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/account/AccountCreationResult.kt new file mode 100644 index 0000000..23a9993 --- /dev/null +++ b/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/account/AccountCreationResult.kt @@ -0,0 +1,58 @@ +package dev.slne.surf.transaction.api.account + +import kotlinx.serialization.Serializable + +/** + * Represents the result of an account creation operation. + * This sealed class encapsulates both successful and failed account creation attempts. + * + * @property message A message describing the result of the account creation attempt. + */ +@Serializable +sealed class AccountCreationResult(val message: String) { + + /** + * Represents a successful account creation result. + * + * @property account The newly created account. + */ + @Serializable + data class Success(val account: Account) : + AccountCreationResult("Dein Account wurde erfolgreich erstellt.") + + /** + * Represents a failure in account creation. + * + * This class includes a reason for the failure, which can be one of several predefined reasons. + * @property reason The reason for the failure, represented by a [FailureReason] enum. + */ + @Serializable + data class Failure(val reason: FailureReason) : + AccountCreationResult("Fehler beim Erstellen des Accounts: $reason") + + /** + * Enum representing the possible reasons for a failure in account creation. + */ + @Serializable + enum class FailureReason { + /** + * The account name already exists. + */ + NAME_ALREADY_EXISTS, + + /** + * The account name is too long. + */ + NAME_TOO_LONG, + + /** + * The account name is too short. + */ + NAME_TOO_SHORT, + + /** + * The account name matches a UUID format. + */ + NAME_IS_UUID + } +} \ No newline at end of file diff --git a/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/account/AccountDeleteResult.kt b/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/account/AccountDeleteResult.kt new file mode 100644 index 0000000..17bb3e4 --- /dev/null +++ b/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/account/AccountDeleteResult.kt @@ -0,0 +1,43 @@ +package dev.slne.surf.transaction.api.account + +import kotlinx.serialization.Serializable + +/** + * Represents the result of an account deletion operation. + * This sealed class encapsulates both successful and failed account deletion attempts. + * + * @property message A message describing the result of the account deletion attempt. + */ +@Serializable +sealed class AccountDeleteResult(val message: String) { + + /** + * Represents a successful account deletion result. + * + * @property account The account that was successfully deleted. + */ + @Serializable + data class Success(val account: Account) : + AccountDeleteResult("Dein Account wurde erfolgreich gelöscht.") + + /** + * Represents a failure in account deletion. + * This class includes a reason for the failure, which can be one of several predefined reasons. + * + * @property reason The reason for the failure, represented by a [FailureReason] enum + */ + @Serializable + data class Failure(val reason: FailureReason) : + AccountDeleteResult("Fehler beim Löschen des Accounts: $reason") + + /** + * Represents the possible reasons for a failure in account deletion. + */ + @Serializable + enum class FailureReason { + /** + * The account could not be found. + */ + ACCOUNT_NOT_FOUND, + } +} \ No newline at end of file diff --git a/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/account/AccountSerializer.kt b/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/account/AccountSerializer.kt new file mode 100644 index 0000000..bc8f15b --- /dev/null +++ b/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/account/AccountSerializer.kt @@ -0,0 +1,22 @@ +package dev.slne.surf.transaction.api.account + +import dev.slne.surf.transaction.api.util.InternalTransactionApi +import kotlinx.serialization.KSerializer +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder + +@OptIn(InternalTransactionApi::class) +object AccountSerializer : KSerializer { + override val descriptor get() = InternalAccountBridge.instance.descriptor + + override fun serialize( + encoder: Encoder, + value: Account + ) { + InternalAccountBridge.instance.serialize(encoder, value) + } + + override fun deserialize(decoder: Decoder): Account { + return InternalAccountBridge.instance.deserialize(decoder) + } +} \ No newline at end of file diff --git a/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/account/HasAccounts.kt b/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/account/HasAccounts.kt new file mode 100644 index 0000000..241c932 --- /dev/null +++ b/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/account/HasAccounts.kt @@ -0,0 +1,57 @@ +package dev.slne.surf.transaction.api.account + +import dev.slne.surf.cloud.api.common.player.OfflineCloudPlayer +import dev.slne.surf.transaction.api.util.InternalTransactionApi + +@OptIn(InternalTransactionApi::class) +interface HasAccounts { + + /** + * Represents the cloud player associated with this account holder. + */ + val cloudPlayer: OfflineCloudPlayer + + /** + * Retrieves the default account for this player. + * + * @return the default [Account] associated with this player + */ + suspend fun getDefaultAccount() = + InternalAccountBridge.instance.getDefaultAccount(cloudPlayer) + + /** + * Retrieves all accounts associated with this player. + * + * @return a set of [Account]s owned by this player + */ + suspend fun getAllAccounts() = + InternalAccountBridge.instance.getAccounts(cloudPlayer) + + /** + * Creates a new account for this player with the specified [name]. + * + * @param name the name of the new account; must be non-empty + * @return the newly created [Account] + */ + suspend fun createAccount(name: String) = + InternalAccountBridge.instance.createAccount(cloudPlayer, name) + + /** + * Retrieves the account with the specified [accountName] for this player. + * + * @param accountName the name of the account to retrieve; must be non-empty + * @return the [Account] matching [accountName], or `null` if not found + */ + suspend fun getAccountByName(accountName: String) = + InternalAccountBridge.instance.getAccountByName(accountName) + + /** + * Deletes the specified [account] from this player's accounts. + * + * @param account the account to delete; must be non-null + * @return an [AccountDeleteResult] indicating success or failure + */ + suspend fun deleteAccount(account: Account) = + InternalAccountBridge.instance.deleteAccount(account) + +} \ No newline at end of file diff --git a/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/account/InternalAccountBridge.kt b/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/account/InternalAccountBridge.kt new file mode 100644 index 0000000..e5217e0 --- /dev/null +++ b/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/account/InternalAccountBridge.kt @@ -0,0 +1,90 @@ +package dev.slne.surf.transaction.api.account + +import dev.slne.surf.cloud.api.common.player.OfflineCloudPlayer +import dev.slne.surf.transaction.api.InternalTransactionApiBridge +import dev.slne.surf.transaction.api.util.InternalTransactionApi +import it.unimi.dsi.fastutil.objects.ObjectSet +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import org.springframework.beans.factory.getBean +import java.util.* + +@InternalTransactionApi +interface InternalAccountBridge { + /** + * Retrieves an account by its unique identifier. + * + * @param accountId The unique identifier of the account to retrieve. + * @return The account associated with the given identifier, or null if no such account exists. + */ + suspend fun getAccountByAccountId(accountId: UUID): Account? + + /** + * Creates a new account for a specified player. + * + * @param owner The player who will own the new account. + * @param name The name of the new account. + * @return A result indicating the success or failure of the account creation. + */ + suspend fun createAccount(owner: OfflineCloudPlayer, name: String): AccountCreationResult + + /** + * Retrieves all accounts owned by a specific player. + * + * @param owner The player whose accounts are to be retrieved. + * @return A set of accounts owned by the specified player. + */ + suspend fun getAllAccountsByOwner(owner: OfflineCloudPlayer): ObjectSet + + /** + * Retrieves an account by its name. + * + * @param name The name of the account to retrieve. + * @return The account associated with the given name, or null if no such account exists. + */ + suspend fun getAccountByName(name: String): Account? + + /** + * Retrieves all accounts associated with a specific player. + * + * @param player The player whose accounts are to be retrieved. + * @return A set of accounts associated with the specified player. + */ + suspend fun getAccounts(player: OfflineCloudPlayer): ObjectSet + + /** + * Retrieves the default account for a specified player. + * + * @param player The player whose default account is to be retrieved. + * @return The default account associated with the specified player. + * @throws IllegalStateException if no default account is found for the player. + */ + suspend fun getDefaultAccount(player: OfflineCloudPlayer): Account = + getDefaultAccountOrNull(player) + ?: error("Default account not found for player: ${player.uuid}") + + /** + * Retrieves the default account for a specified player, or returns null if no default account exists + * + * @param player The player whose default account is to be retrieved. + * @return The default account associated with the specified player, or null if no default account exists. + */ + suspend fun getDefaultAccountOrNull(player: OfflineCloudPlayer): Account? + + /** + * Deletes a specified account. + * + * @param account The account to be deleted. + * @return A result indicating the success or failure of the account deletion. + */ + suspend fun deleteAccount(account: Account): AccountDeleteResult + + val descriptor: SerialDescriptor + fun serialize(encoder: Encoder, value: Account) + fun deserialize(decoder: Decoder): Account + + companion object { + val instance get() = InternalTransactionApiBridge.instance.context.getBean() + } +} \ No newline at end of file diff --git a/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/currency/Currency.kt b/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/currency/Currency.kt index 997a3b9..be7f8a8 100644 --- a/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/currency/Currency.kt +++ b/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/currency/Currency.kt @@ -66,6 +66,13 @@ interface Currency : ComponentLike { fun format(amount: Double, color: TextColor = Colors.VARIABLE_VALUE) = format(amount.toBigDecimal(), color) + /** + * Returns the display name of this currency as a [Component]. + * This is primarily used for UI purposes, such as displaying the currency + * name in menus or transaction summaries. + * + * @return the display name of this currency as a [Component] + */ override fun asComponent(): Component = displayName companion object { diff --git a/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/currency/InternalCurrencyBridge.kt b/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/currency/InternalCurrencyBridge.kt index 1a22641..759930d 100644 --- a/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/currency/InternalCurrencyBridge.kt +++ b/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/currency/InternalCurrencyBridge.kt @@ -7,8 +7,22 @@ import org.springframework.beans.factory.getBean @InternalTransactionApi interface InternalCurrencyBridge { + /** + * Retrieves the default currency used in the transaction system. + */ val defaultCurrency: Currency + + /** + * All available currencies in the transaction system. + */ val currencies: ObjectSet + + /** + * Retrieves a currency by its unique name. + * + * @param name The unique identifier of the currency to retrieve. + * @return The currency associated with the given name, or null if no such currency exists. + */ fun getCurrencyByName(name: String): Currency? = currencies.find { it.name.equals(name, ignoreCase = true) } diff --git a/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/transaction/InternalTransactionBridge.kt b/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/transaction/InternalTransactionBridge.kt index 637ef95..9882097 100644 --- a/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/transaction/InternalTransactionBridge.kt +++ b/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/transaction/InternalTransactionBridge.kt @@ -1,18 +1,104 @@ package dev.slne.surf.transaction.api.transaction +import dev.slne.surf.cloud.api.common.player.OfflineCloudPlayer +import dev.slne.surf.surfapi.core.api.util.objectSetOf import dev.slne.surf.transaction.api.InternalTransactionApiBridge +import dev.slne.surf.transaction.api.account.Account +import dev.slne.surf.transaction.api.currency.Currency +import dev.slne.surf.transaction.api.transaction.data.TransactionData import dev.slne.surf.transaction.api.util.InternalTransactionApi +import it.unimi.dsi.fastutil.objects.ObjectSet import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder import org.springframework.beans.factory.getBean +import java.math.BigDecimal @InternalTransactionApi interface InternalTransactionBridge { + val descriptor: SerialDescriptor fun serialize(encoder: Encoder, value: Transaction) fun deserialize(decoder: Decoder): Transaction + /** + * Processes a deposit transaction for the specified account. + * + * @param account The account to which the deposit will be made. + * @param initiator The player initiating the deposit. + * @param amount The amount to be deposited. + * @param currency The currency in which the deposit is made. + * @param ignoreMinimum Whether to ignore the minimum balance requirement for the deposit. + * @param additionalData Additional data related to the transaction. + * @return A result indicating the success or failure of the deposit transaction. + */ + suspend fun deposit( + account: Account, + initiator: OfflineCloudPlayer, + amount: BigDecimal, + currency: Currency, + ignoreMinimum: Boolean = false, + vararg additionalData: TransactionData + ): TransactionResult + + + /** + * Processes a withdrawal transaction for the specified account. + * + * @param account The account from which the withdrawal will be made. + * @param initiator The player initiating the withdrawal. + * @param amount The amount to be withdrawn. + * @param currency The currency in which the withdrawal is made. + * @param ignoreMinimum Whether to ignore the minimum balance requirement for the withdrawal. + * @param additionalData Additional data related to the transaction. + * @return A result indicating the success or failure of the withdrawal transaction. + */ + suspend fun withdraw( + account: Account, + initiator: OfflineCloudPlayer, + amount: BigDecimal, + currency: Currency, + ignoreMinimum: Boolean = false, + vararg additionalData: TransactionData + ): TransactionResult + + + /** + * Transfers an amount from one account to another. + * + * @param initiator The player initiating the transfer. + * @param sender The account from which the amount will be transferred. + * @param amount The amount to be transferred. + * @param currency The currency in which the transfer is made. + * @param receiver The account to which the amount will be transferred. + * @param ignoreSenderMinimum Whether to ignore the minimum balance requirement for the sender. + * @param ignoreReceiverMinimum Whether to ignore the minimum balance requirement for the receiver. + * @param additionalSenderData Additional data related to the sender's transaction. + * @param additionalReceiverData Additional data related to the receiver's transaction. + * @return A result indicating the success or failure of the transfer transaction. + */ + suspend fun transfer( + initiator: OfflineCloudPlayer, + sender: Account, + amount: BigDecimal, + currency: Currency, + receiver: Account, + ignoreSenderMinimum: Boolean = false, + ignoreReceiverMinimum: Boolean = false, + additionalSenderData: ObjectSet = objectSetOf(), + additionalReceiverData: ObjectSet = objectSetOf() + ): TransactionResult + + + /** + * Retrieves the balance of the specified account in the given currency. + * + * @param account The account for which to retrieve the balance. + * @param currency The currency in which to retrieve the balance. + * @return The balance amount as a [BigDecimal]. + */ + suspend fun balance(account: Account, currency: Currency): BigDecimal + companion object { val instance get() = InternalTransactionApiBridge.instance.context.getBean() } diff --git a/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/transaction/Transaction.kt b/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/transaction/Transaction.kt index 48b831d..a06643c 100644 --- a/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/transaction/Transaction.kt +++ b/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/transaction/Transaction.kt @@ -1,10 +1,10 @@ package dev.slne.surf.transaction.api.transaction import dev.slne.surf.cloud.api.common.player.OfflineCloudPlayer +import dev.slne.surf.transaction.api.account.Account import dev.slne.surf.transaction.api.currency.Currency import dev.slne.surf.transaction.api.transaction.data.TransactionData import dev.slne.surf.transaction.api.util.InternalTransactionApi -import it.unimi.dsi.fastutil.objects.ObjectSet import kotlinx.serialization.Serializable import org.jetbrains.annotations.Unmodifiable import java.math.BigDecimal @@ -16,7 +16,7 @@ import java.util.* * A transaction moves an [amount] of a specified [currency] from an optional [sender] * to an optional [receiver]. When either participant is `null`, the transfer is treated * as a **system transaction**—originating from or destined to the platform itself - * rather than a player. + * rather than an [Account]. * * All properties are read-only; implementations are expected to be thread-safe. */ @@ -28,24 +28,48 @@ interface Transaction { val identifier: UUID /** - * Player initiating the transfer, or `null` for system credits. + * The [OfflineCloudPlayer] who initiated this transaction. + */ + val initiator: OfflineCloudPlayer? + + /** + * The uuid of the [Account] initiating the transfer, or `null` for system credits. * * When `null`, the funds originate from the platform (e.g. daily rewards). */ - val sender: OfflineCloudPlayer? + val senderAccountId: UUID? /** - * Player receiving the funds, or `null` for system debits. + * The uuid of the [Account] receiving the funds, or `null` for system debits. * * When `null`, the funds are removed from circulation by the platform * (e.g., taxes, sinks). */ - val receiver: OfflineCloudPlayer? + val receiverAccountId: UUID? - /** Currency in which the [amount] is denominated. */ + /** + * Asynchronously retrieves the [Account] initiating the transfer, or `null` for system credits. + * + * When `null`, the funds originate from the platform (e.g. daily rewards). + */ + suspend fun sender(): Account? + + /** + * Asynchronously retrieves the [Account] receiving the funds, or `null` for system debits. + * + * When `null`, the funds are removed from circulation by the platform + * (e.g., taxes, sinks). + */ + suspend fun receiver(): Account? + + /** + * Currency in which the [amount] is denominated. + * */ val currency: Currency - /** Absolute value transferred, expressed with high-precision decimal arithmetic. */ + /** + * Absolute value transferred, expressed with high-precision decimal arithmetic. + */ val amount: BigDecimal /** diff --git a/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/user/HasTransactions.kt b/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/user/HasTransactions.kt new file mode 100644 index 0000000..0216ee0 --- /dev/null +++ b/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/user/HasTransactions.kt @@ -0,0 +1,117 @@ +package dev.slne.surf.transaction.api.user + +import dev.slne.surf.cloud.api.common.player.OfflineCloudPlayer +import dev.slne.surf.surfapi.core.api.util.objectSetOf +import dev.slne.surf.transaction.api.account.Account +import dev.slne.surf.transaction.api.currency.Currency +import dev.slne.surf.transaction.api.transaction.InternalTransactionBridge +import dev.slne.surf.transaction.api.transaction.TransactionResult +import dev.slne.surf.transaction.api.transaction.data.TransactionData +import dev.slne.surf.transaction.api.util.InternalTransactionApi +import it.unimi.dsi.fastutil.objects.ObjectSet +import java.math.BigDecimal + +@OptIn(InternalTransactionApi::class) +interface HasTransactions { + + /** + * Deposits [amount] into this player's account in the given [currency]. + * + * @param account the account to deposit into; must be non-null + * @param initiator the player initiating the deposit; defaults to `this` + * @param amount the amount to deposit; must be non-negative + * @param currency the monetary unit of [amount] + * @param ignoreMinimum `true` to bypass minimum-balance validation + * @param additionalData optional metadata attached to the transaction + * @return a [TransactionResult] describing the outcome + */ + suspend fun deposit( + account: Account, + initiator: OfflineCloudPlayer, + amount: BigDecimal, + currency: Currency, + ignoreMinimum: Boolean = false, + vararg additionalData: TransactionData + ): TransactionResult = InternalTransactionBridge.instance.deposit( + account, + initiator, + amount, + currency, + ignoreMinimum, + *additionalData + ) + + /** + * Withdraws [amount] from this player's account in the given [currency]. + * + * @param account the account to withdraw from; must be non-null + * @param initiator the player initiating the withdrawal; defaults to `this` + * @param amount the amount to withdraw; must be non-negative + * @param currency the monetary unit of [amount] + * @param ignoreMinimum `true` to bypass minimum-balance validation + * @param additionalData optional metadata attached to the transaction + * @return a [TransactionResult] describing the outcome + */ + suspend fun withdraw( + account: Account, + initiator: OfflineCloudPlayer, + amount: BigDecimal, + currency: Currency, + ignoreMinimum: Boolean = false, + vararg additionalData: TransactionData + ): TransactionResult = InternalTransactionBridge.instance.withdraw( + account, + initiator, + amount, + currency, + ignoreMinimum, + *additionalData + ) + + /** + * Transfers [amount] from this player's account to [receiver]. + * + * @param initiator the player initiating the transfer; defaults to `this` + * @param sender the account sending the funds; must be non-null + * @param amount the amount to transfer; must be non-negative + * @param currency the monetary unit of [amount] + * @param receiver the account receiving the funds + * @param ignoreSenderMinimum `true` to bypass sender's minimum-balance validation + * @param ignoreReceiverMinimum `true` to bypass receiver's minimum-balance validation + * @param additionalSenderData optional metadata attached to the sender's leg + * @param additionalReceiverData optional metadata attached to the receiver's leg + * @return a [TransactionResult] describing the outcome + */ + suspend fun transfer( + initiator: OfflineCloudPlayer, + sender: Account, + amount: BigDecimal, + currency: Currency, + receiver: Account, + ignoreSenderMinimum: Boolean = false, + ignoreReceiverMinimum: Boolean = false, + additionalSenderData: ObjectSet = objectSetOf(), + additionalReceiverData: ObjectSet = objectSetOf() + ): TransactionResult = InternalTransactionBridge.instance.transfer( + initiator, + sender, + amount, + currency, + receiver, + ignoreSenderMinimum, + ignoreReceiverMinimum, + additionalSenderData, + additionalReceiverData + ) + + /** + * Retrieves this player's balance in [currency] as a [BigDecimal]. + * + * @param account the account to check; must be non-null + * @param currency the monetary unit of the returned balance + * @return the current balance + */ + suspend fun balance(account: Account, currency: Currency): BigDecimal = + InternalTransactionBridge.instance.balance(account, currency) + +} \ No newline at end of file diff --git a/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/user/InternalTransactionUserBridge.kt b/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/user/InternalTransactionUserBridge.kt deleted file mode 100644 index ec149e8..0000000 --- a/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/user/InternalTransactionUserBridge.kt +++ /dev/null @@ -1,50 +0,0 @@ -package dev.slne.surf.transaction.api.user - -import dev.slne.surf.cloud.api.common.player.OfflineCloudPlayer -import dev.slne.surf.surfapi.core.api.util.objectSetOf -import dev.slne.surf.transaction.api.InternalTransactionApiBridge -import dev.slne.surf.transaction.api.currency.Currency -import dev.slne.surf.transaction.api.transaction.TransactionResult -import dev.slne.surf.transaction.api.transaction.data.TransactionData -import dev.slne.surf.transaction.api.util.InternalTransactionApi -import it.unimi.dsi.fastutil.objects.ObjectSet -import org.springframework.beans.factory.getBean -import java.math.BigDecimal - -@InternalTransactionApi -interface InternalTransactionUserBridge { - - suspend fun deposit( - player: OfflineCloudPlayer, - amount: BigDecimal, - currency: Currency, - ignoreMinimum: Boolean = false, - vararg additionalData: TransactionData - ): TransactionResult - - suspend fun withdraw( - player: OfflineCloudPlayer, - amount: BigDecimal, - currency: Currency, - ignoreMinimum: Boolean = false, - vararg additionalData: TransactionData - ): TransactionResult - - suspend fun transfer( - sender: OfflineCloudPlayer, - amount: BigDecimal, - currency: Currency, - receiver: OfflineCloudPlayer, - ignoreSenderMinimum: Boolean = false, - ignoreReceiverMinimum: Boolean = false, - additionalSenderData: ObjectSet = objectSetOf(), - additionalReceiverData: ObjectSet = objectSetOf() - ): TransactionResult - - suspend fun balanceDecimal(player: OfflineCloudPlayer, currency: Currency): BigDecimal - - - companion object { - val instance get() = InternalTransactionApiBridge.instance.context.getBean() - } -} \ No newline at end of file diff --git a/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/user/TransactionUser.kt b/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/user/TransactionUser.kt index 1c09d55..7b971a4 100644 --- a/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/user/TransactionUser.kt +++ b/surf-transaction-api/src/main/kotlin/dev/slne/surf/transaction/api/user/TransactionUser.kt @@ -1,21 +1,9 @@ -/** - * Provides extension utilities for performing currency transactions on an [OfflineCloudPlayer]. - * - * All functions are `suspend` and delegate to [InternalTransactionUserBridge] for execution. - * Amounts are primarily accepted as [BigDecimal] to avoid precision issues; `Double` overloads - * are offered for convenience and convert internally via [BigDecimal.valueOf]. - * - * ### Conventions - * * `ignoreMinimum` flags allow bypassing configured minimum-balance requirements. - * * Additional transaction metadata can be supplied via [TransactionData] (vararg or set). - * * Each call returns a [TransactionResult] describing success, failure, and context data. - */ -@file:OptIn(InternalTransactionApi::class) - package dev.slne.surf.transaction.api.user import dev.slne.surf.cloud.api.common.player.OfflineCloudPlayer -import dev.slne.surf.surfapi.core.api.util.objectSetOf +import dev.slne.surf.cloud.api.common.util.objectSetOf +import dev.slne.surf.transaction.api.account.Account +import dev.slne.surf.transaction.api.account.HasAccounts import dev.slne.surf.transaction.api.currency.Currency import dev.slne.surf.transaction.api.transaction.TransactionResult import dev.slne.surf.transaction.api.transaction.data.TransactionData @@ -23,294 +11,186 @@ import dev.slne.surf.transaction.api.util.InternalTransactionApi import it.unimi.dsi.fastutil.objects.ObjectSet import java.math.BigDecimal - -// region Deposit -/** - * Deposits [amount] into this player's account in the given [currency]. - * - * @receiver the player whose balance will be credited - * @param amount the amount to deposit; must be non-negative - * @param currency the monetary unit of [amount] - * @param ignoreMinimum `true` to bypass minimum-balance validation - * @param additionalData optional metadata attached to the transaction - * @return a [TransactionResult] describing the outcome - */ -suspend fun OfflineCloudPlayer.deposit( - amount: BigDecimal, - currency: Currency, - ignoreMinimum: Boolean = false, - vararg additionalData: TransactionData -): TransactionResult = InternalTransactionUserBridge.instance.deposit( - this, - amount, - currency, - ignoreMinimum, - *additionalData -) - -/** - * Convenience overload delegating to [deposit] with `ignoreMinimum = false`. - * - * @receiver the player whose balance will be credited - * @param amount the amount to deposit; must be non-negative - * @param currency the monetary unit of [amount] - * @param additionalData optional metadata attached to the transaction - * @return a [TransactionResult] describing the outcome - */ -suspend fun OfflineCloudPlayer.deposit( - amount: BigDecimal, - currency: Currency, - vararg additionalData: TransactionData -) = deposit(amount, currency, false, *additionalData) - -/** - * Deposits [amount] (converted from `Double`) into this player's account. - * - * @receiver the player whose balance will be credited - * @param amount the amount to deposit; must be non-negative - * @param currency the monetary unit of [amount] - * @param ignoreMinimum `true` to bypass minimum-balance validation - * @param additionalData optional metadata attached to the transaction - * @return a [TransactionResult] describing the outcome - */ -suspend fun OfflineCloudPlayer.deposit( - amount: Double, - currency: Currency, - ignoreMinimum: Boolean = false, - vararg additionalData: TransactionData -) = deposit(amount.toBigDecimal(), currency, ignoreMinimum, *additionalData) - -/** - * Convenience overload delegating to the `Double`-based [deposit] with - * `ignoreMinimum = false`. - * - * @receiver the player whose balance will be credited - * @param amount the amount to deposit; must be non-negative - * @param currency the monetary unit of [amount] - * @param additionalData optional metadata attached to the transaction - * @return a [TransactionResult] describing the outcome - */ -suspend fun OfflineCloudPlayer.deposit( - amount: Double, - currency: Currency, - vararg additionalData: TransactionData -) = deposit(BigDecimal.valueOf(amount), currency, *additionalData) -// endregion Deposit - -// region Withdraw -/** - * Withdraws [amount] from this player's account in the given [currency]. - * - * @receiver the player whose balance will be debited - * @param amount the amount to withdraw; must be non-negative - * @param currency the monetary unit of [amount] - * @param ignoreMinimum `true` to bypass minimum-balance validation - * @param additionalData optional metadata attached to the transaction - * @return a [TransactionResult] describing the outcome - */ -suspend fun OfflineCloudPlayer.withdraw( - amount: BigDecimal, - currency: Currency, - ignoreMinimum: Boolean = false, - vararg additionalData: TransactionData -): TransactionResult = InternalTransactionUserBridge.instance.withdraw( - this, - amount, - currency, - ignoreMinimum, - *additionalData -) - -/** - * Convenience overload delegating to [withdraw] with `ignoreMinimum = false`. - * - * @receiver the player whose balance will be debited - * @param amount the amount to withdraw; must be non-negative - * @param currency the monetary unit of [amount] - * @param additionalData optional metadata attached to the transaction - * @return a [TransactionResult] describing the outcome - */ -suspend fun OfflineCloudPlayer.withdraw( - amount: BigDecimal, - currency: Currency, - vararg additionalData: TransactionData -) = withdraw(amount, currency, false, *additionalData) - -/** - * Withdraws [amount] (converted from `Double`) from this player's account. - * - * @receiver the player whose balance will be debited - * @param amount the amount to withdraw; must be non-negative - * @param currency the monetary unit of [amount] - * @param ignoreMinimum `true` to bypass minimum-balance validation - * @param additionalData optional metadata attached to the transaction - * @return a [TransactionResult] describing the outcome - */ -suspend fun OfflineCloudPlayer.withdraw( - amount: Double, - currency: Currency, - ignoreMinimum: Boolean = false, - vararg additionalData: TransactionData -) = withdraw(amount.toBigDecimal(), currency, ignoreMinimum, *additionalData) - -/** - * Convenience overload delegating to the `Double`-based [withdraw] with - * `ignoreMinimum = false`. - * - * @receiver the player whose balance will be debited - * @param amount the amount to withdraw; must be non-negative - * @param currency the monetary unit of [amount] - * @param additionalData optional metadata attached to the transaction - * @return a [TransactionResult] describing the outcome - */ -suspend fun OfflineCloudPlayer.withdraw( - amount: Double, - currency: Currency, - vararg additionalData: TransactionData -) = withdraw(amount.toBigDecimal(), currency, *additionalData) -// endregion - -// region Transfer -/** - * Transfers [amount] from this player's account to [receiver]. - * - * @receiver the player sending the funds - * @param amount the amount to transfer; must be non-negative - * @param currency the monetary unit of [amount] - * @param receiver the player receiving the funds - * @param ignoreSenderMinimum `true` to bypass sender's minimum-balance validation - * @param ignoreReceiverMinimum `true` to bypass receiver's minimum-balance validation - * @param additionalSenderData optional metadata attached to the sender's leg - * @param additionalReceiverData optional metadata attached to the receiver's leg - * @return a [TransactionResult] describing the outcome - */ -suspend fun OfflineCloudPlayer.transfer( - amount: BigDecimal, - currency: Currency, - receiver: OfflineCloudPlayer, - ignoreSenderMinimum: Boolean = false, - ignoreReceiverMinimum: Boolean = false, - additionalSenderData: ObjectSet = objectSetOf(), - additionalReceiverData: ObjectSet = objectSetOf() -): TransactionResult = InternalTransactionUserBridge.instance.transfer( - this, - amount, - currency, - receiver, - ignoreSenderMinimum, - ignoreReceiverMinimum, - additionalSenderData, - additionalReceiverData -) - -/** - * Convenience overload delegating to [transfer] with `ignoreSenderMinimum` and - * `ignoreReceiverMinimum` defaulting to `false`. - * - * @receiver the player sending the funds - * @param amount the amount to transfer; must be non-negative - * @param currency the monetary unit of [amount] - * @param receiver the player receiving the funds - * @param additionalSenderData optional metadata attached to the sender's leg - * @param additionalReceiverData optional metadata attached to the receiver's leg - * @return a [TransactionResult] describing the outcome - */ -suspend fun OfflineCloudPlayer.transfer( - amount: BigDecimal, - currency: Currency, - receiver: OfflineCloudPlayer, - additionalSenderData: ObjectSet = objectSetOf(), - additionalReceiverData: ObjectSet = objectSetOf() -) = transfer( - amount, - currency, - receiver, - ignoreSenderMinimum = false, - ignoreReceiverMinimum = false, - additionalSenderData, - additionalReceiverData -) - -/** - * Transfers [amount] (converted from `Double`) from this player to [receiver]. - * - * @receiver the player sending the funds - * @param amount the amount to transfer; must be non-negative - * @param currency the monetary unit of [amount] - * @param receiver the player receiving the funds - * @param ignoreSenderMinimum `true` to bypass sender's minimum-balance validation - * @param ignoreReceiverMinimum `true` to bypass receiver's minimum-balance validation - * @param additionalSenderData optional metadata attached to the sender's leg - * @param additionalReceiverData optional metadata attached to the receiver's leg - * @return a [TransactionResult] describing the outcome - */ -suspend fun OfflineCloudPlayer.transfer( - amount: Double, - currency: Currency, - receiver: OfflineCloudPlayer, - ignoreSenderMinimum: Boolean = false, - ignoreReceiverMinimum: Boolean = false, - additionalSenderData: ObjectSet = objectSetOf(), - additionalReceiverData: ObjectSet = objectSetOf() -) = transfer( - amount.toBigDecimal(), - currency, - receiver, - ignoreSenderMinimum, - ignoreReceiverMinimum, - additionalSenderData, - additionalReceiverData -) - -/** - * Convenience overload delegating to the `Double`-based [transfer] with - * `ignoreSenderMinimum` and `ignoreReceiverMinimum` defaulting to `false`. - * - * @receiver the player sending the funds - * @param amount the amount to transfer; must be non-negative - * @param currency the monetary unit of [amount] - * @param receiver the player receiving the funds - * @param additionalSenderData optional metadata attached to the sender's leg - * @param additionalReceiverData optional metadata attached to the receiver's leg - * @return a [TransactionResult] describing the outcome - */ -suspend fun OfflineCloudPlayer.transfer( - amount: Double, - currency: Currency, - receiver: OfflineCloudPlayer, - additionalSenderData: ObjectSet = objectSetOf(), - additionalReceiverData: ObjectSet = objectSetOf() -) = transfer( - amount.toBigDecimal(), - currency, - receiver, - additionalSenderData, - additionalReceiverData -) -// endregion - -// region Balance -/** - * Retrieves this player's balance in [currency] as a [BigDecimal]. - * - * @receiver the player whose balance is requested - * @param currency the monetary unit of the returned balance - * @return the current balance - */ -suspend fun OfflineCloudPlayer.balance(currency: Currency): BigDecimal = - InternalTransactionUserBridge.instance.balanceDecimal(this, currency) +interface TransactionUser : HasTransactions, HasAccounts { + + override val cloudPlayer: OfflineCloudPlayer + + override suspend fun deposit( + account: Account, + initiator: OfflineCloudPlayer, + amount: BigDecimal, + currency: Currency, + ignoreMinimum: Boolean, + vararg additionalData: TransactionData + ) = getDefaultAccount().deposit( + account, + initiator, + amount, + currency, + ignoreMinimum, + *additionalData + ) + + /** + * Deposits an amount into the default account of this user in the specified currency. + * + * @param amount The amount to deposit. + * @param currency The currency in which the deposit is made. + * @param ignoreMinimum Whether to bypass the minimum balance validation. + * @param additionalData Additional data related to the transaction. + * @return A [TransactionResult] describing the outcome of the deposit. + */ + suspend fun deposit( + amount: BigDecimal, + currency: Currency, + ignoreMinimum: Boolean = false, + vararg additionalData: TransactionData + ) = deposit( + getDefaultAccount(), + cloudPlayer, + amount, + currency, + ignoreMinimum, + *additionalData + ) + + override suspend fun withdraw( + account: Account, + initiator: OfflineCloudPlayer, + amount: BigDecimal, + currency: Currency, + ignoreMinimum: Boolean, + vararg additionalData: TransactionData + ) = getDefaultAccount().withdraw( + account, + initiator, + amount, + currency, + ignoreMinimum, + *additionalData + ) + + /** + * Withdraws an amount from the default account of this user in the specified currency. + * + * @param amount The amount to withdraw. + * @param currency The currency in which the withdrawal is made. + * @param ignoreMinimum Whether to bypass the minimum balance validation. + * @param additionalData Additional data related to the transaction. + * @return A [TransactionResult] describing the outcome of the withdrawal. + */ + suspend fun withdraw( + amount: BigDecimal, + currency: Currency, + ignoreMinimum: Boolean = false, + vararg additionalData: TransactionData + ) = withdraw( + getDefaultAccount(), + cloudPlayer, + amount, + currency, + ignoreMinimum, + *additionalData + ) + + override suspend fun transfer( + initiator: OfflineCloudPlayer, + sender: Account, + amount: BigDecimal, + currency: Currency, + receiver: Account, + ignoreSenderMinimum: Boolean, + ignoreReceiverMinimum: Boolean, + additionalSenderData: ObjectSet, + additionalReceiverData: ObjectSet + ) = getDefaultAccount().transfer( + initiator, + sender, + amount, + currency, + receiver, + ignoreSenderMinimum, + ignoreReceiverMinimum, + additionalSenderData, + additionalReceiverData + ) + + /** + * Transfers an amount from the default account of this user to the specified receiver account. + * + * @param amount The amount to transfer. + * @param currency The currency in which the transfer is made. + * @param receiver The account receiving the funds. + * @param ignoreSenderMinimum Whether to bypass the sender's minimum balance validation. + * @param ignoreReceiverMinimum Whether to bypass the receiver's minimum balance validation. + * @param additionalSenderData Additional data related to the sender's transaction. + * @param additionalReceiverData Additional data related to the receiver's transaction. + * @return A [TransactionResult] describing the outcome of the transfer. + */ + suspend fun transfer( + amount: BigDecimal, + currency: Currency, + receiver: Account, + ignoreSenderMinimum: Boolean = false, + ignoreReceiverMinimum: Boolean = false, + additionalSenderData: ObjectSet = objectSetOf(), + additionalReceiverData: ObjectSet = objectSetOf() + ) = + transfer( + cloudPlayer, + getDefaultAccount(), + amount, + currency, + receiver, + ignoreSenderMinimum, + ignoreReceiverMinimum, + additionalSenderData, + additionalReceiverData + ) + + override suspend fun balance( + account: Account, + currency: Currency + ) = getDefaultAccount().balance(account, currency) + + /** + * Retrieves the balance of the default account for this user in the specified currency. + * + * @param currency The currency in which the balance is to be retrieved. + * @return The balance of the default account in the specified currency as a [BigDecimal]. + */ + suspend fun balance( + currency: Currency + ): BigDecimal = balance(getDefaultAccount(), currency) + + @OptIn(InternalTransactionApi::class) + companion object { + /** + * Creates a [TransactionUser] instance for the given [OfflineCloudPlayer]. + * + * @param cloudPlayer The offline cloud player for whom the transaction user is created. + * @return A [TransactionUser] instance representing the specified cloud player. + */ + operator fun invoke(cloudPlayer: OfflineCloudPlayer): TransactionUser = + TransactionUserImpl(cloudPlayer) + + /** + * Internal implementation of [TransactionUser]. + * This class is not intended for public use and should only be accessed through the [TransactionUser] interface. + * + * @property cloudPlayer The offline cloud player for this transaction user. + */ + internal class TransactionUserImpl( + override val cloudPlayer: OfflineCloudPlayer + ) : TransactionUser + } + +} /** - * Convenience wrapper returning the balance as `Double`. + * Extension function to create a [TransactionUser] from an [OfflineCloudPlayer]. * - * @receiver the player whose balance is requested - * @param currency the monetary unit of the returned balance - * @return the current balance + * @receiver The offline cloud player for whom the transaction user is created. + * @return A [TransactionUser] instance representing the specified cloud player. */ -@Deprecated( - "Use balance(currency: Currency): BigDecimal instead for precision", - ReplaceWith("balance(currency).toDouble()") -) -suspend fun OfflineCloudPlayer.balanceDouble(currency: Currency): Double = - balance(currency).toDouble() -// endregion \ No newline at end of file +fun OfflineCloudPlayer.transactionUser() = TransactionUser(this) \ No newline at end of file diff --git a/surf-transaction-core/surf-transaction-core-client/build.gradle.kts b/surf-transaction-core/surf-transaction-core-client/build.gradle.kts index 6cbbc8d..f627ef2 100644 --- a/surf-transaction-core/surf-transaction-core-client/build.gradle.kts +++ b/surf-transaction-core/surf-transaction-core-client/build.gradle.kts @@ -2,9 +2,12 @@ plugins { id("dev.slne.surf.surfapi.gradle.core") } +surfCoreApi { + withCloudClientCommon() +} + dependencies { api(project(":surf-transaction-core:surf-transaction-core-common")) - compileOnly("dev.slne.surf.cloud:surf-cloud-api-client-common:1.21.7+") } kotlin { diff --git a/surf-transaction-core/surf-transaction-core-client/src/main/kotlin/dev/slne/surf/transaction/core/client/account/ClientAccountBridge.kt b/surf-transaction-core/surf-transaction-core-client/src/main/kotlin/dev/slne/surf/transaction/core/client/account/ClientAccountBridge.kt new file mode 100644 index 0000000..2d02cb6 --- /dev/null +++ b/surf-transaction-core/surf-transaction-core-client/src/main/kotlin/dev/slne/surf/transaction/core/client/account/ClientAccountBridge.kt @@ -0,0 +1,43 @@ +package dev.slne.surf.transaction.core.client.account + +import dev.slne.surf.cloud.api.client.netty.packet.fireAndAwaitOrThrow +import dev.slne.surf.cloud.api.common.player.OfflineCloudPlayer +import dev.slne.surf.cloud.api.common.util.toObjectSet +import dev.slne.surf.transaction.api.account.Account +import dev.slne.surf.transaction.core.account.CommonAccountBridge +import dev.slne.surf.transaction.core.netty.packets.serverbound.* +import org.springframework.stereotype.Component +import java.util.* + +@Component +class ClientAccountBridge : CommonAccountBridge() { + override suspend fun getAccountByAccountId(accountId: UUID) = + ServerboundGetAccountPacket(accountId).fireAndAwaitOrThrow().account + + override suspend fun getAllAccountsByOwner(owner: OfflineCloudPlayer) = + ServerboundGetAllAccountsPacket(owner).fireAndAwaitOrThrow().accounts.toObjectSet() + + override suspend fun getDefaultAccountOrNull(player: OfflineCloudPlayer): Account? = + ServerboundGetDefaultAccountPacket(player).fireAndAwaitOrThrow().account + + override suspend fun createAccount( + owner: OfflineCloudPlayer, + name: String + ) = ServerboundCreateAccountPacket( + owner, + name + ).fireAndAwaitOrThrow().result + + override suspend fun getAccountByName( + name: String + ) = ServerboundGetAccountByNamePacket( + name + ).fireAndAwaitOrThrow().account + + override suspend fun getAccounts(player: OfflineCloudPlayer) = + ServerboundGetAllAccountsPacket(player).fireAndAwaitOrThrow().accounts.toObjectSet() + + override suspend fun deleteAccount( + account: Account + ) = ServerboundDeleteAccountPacket(account.accountId).fireAndAwaitOrThrow().result +} \ No newline at end of file diff --git a/surf-transaction-core/surf-transaction-core-client/src/main/kotlin/dev/slne/surf/transaction/core/client/currency/ClientCurrencyBridge.kt b/surf-transaction-core/surf-transaction-core-client/src/main/kotlin/dev/slne/surf/transaction/core/client/currency/ClientCurrencyBridge.kt index 48c2d79..9909859 100644 --- a/surf-transaction-core/surf-transaction-core-client/src/main/kotlin/dev/slne/surf/transaction/core/client/currency/ClientCurrencyBridge.kt +++ b/surf-transaction-core/surf-transaction-core-client/src/main/kotlin/dev/slne/surf/transaction/core/client/currency/ClientCurrencyBridge.kt @@ -6,13 +6,11 @@ import dev.slne.surf.transaction.api.currency.Currency import dev.slne.surf.transaction.api.currency.InternalCurrencyBridge import dev.slne.surf.transaction.core.currency.CurrencyImpl import org.springframework.stereotype.Component +import kotlin.properties.Delegates @Component class ClientCurrencyBridge : InternalCurrencyBridge { - private var _defaultCurrency: CurrencyImpl? = null - - override val defaultCurrency: Currency - get() = _defaultCurrency ?: error("Transaction api is not initialized yet.") + override var defaultCurrency: Currency by Delegates.notNull() private val _currencies = mutableObjectSetOf() override val currencies = _currencies.freeze() @@ -20,6 +18,6 @@ class ClientCurrencyBridge : InternalCurrencyBridge { fun updateCurrencies(currencies: Set) { _currencies.clear() _currencies.addAll(currencies) - _defaultCurrency = currencies.find { it.defaultCurrency } ?: CurrencyImpl.DEFAULT + defaultCurrency = currencies.find { it.defaultCurrency } ?: CurrencyImpl.DEFAULT } } \ No newline at end of file diff --git a/surf-transaction-core/surf-transaction-core-client/src/main/kotlin/dev/slne/surf/transaction/core/client/netty/listener/RefreshCurrencyListener.kt b/surf-transaction-core/surf-transaction-core-client/src/main/kotlin/dev/slne/surf/transaction/core/client/netty/listener/RefreshCurrencyListener.kt index ac307de..2b53e01 100644 --- a/surf-transaction-core/surf-transaction-core-client/src/main/kotlin/dev/slne/surf/transaction/core/client/netty/listener/RefreshCurrencyListener.kt +++ b/surf-transaction-core/surf-transaction-core-client/src/main/kotlin/dev/slne/surf/transaction/core/client/netty/listener/RefreshCurrencyListener.kt @@ -2,7 +2,7 @@ package dev.slne.surf.transaction.core.client.netty.listener import dev.slne.surf.cloud.api.common.meta.SurfNettyPacketHandler import dev.slne.surf.transaction.core.client.currency.ClientCurrencyBridge -import dev.slne.surf.transaction.core.netty.packets.ClientboundRefreshCurrencies +import dev.slne.surf.transaction.core.netty.packets.clientbound.ClientboundRefreshCurrencies import org.springframework.stereotype.Component @Component diff --git a/surf-transaction-core/surf-transaction-core-client/src/main/kotlin/dev/slne/surf/transaction/core/client/user/ClientTransactionUserBridge.kt b/surf-transaction-core/surf-transaction-core-client/src/main/kotlin/dev/slne/surf/transaction/core/client/transaction/ClientTransactionBridge.kt similarity index 64% rename from surf-transaction-core/surf-transaction-core-client/src/main/kotlin/dev/slne/surf/transaction/core/client/user/ClientTransactionUserBridge.kt rename to surf-transaction-core/surf-transaction-core-client/src/main/kotlin/dev/slne/surf/transaction/core/client/transaction/ClientTransactionBridge.kt index 2defd84..9cd7468 100644 --- a/surf-transaction-core/surf-transaction-core-client/src/main/kotlin/dev/slne/surf/transaction/core/client/user/ClientTransactionUserBridge.kt +++ b/surf-transaction-core/surf-transaction-core-client/src/main/kotlin/dev/slne/surf/transaction/core/client/transaction/ClientTransactionBridge.kt @@ -1,29 +1,33 @@ -package dev.slne.surf.transaction.core.client.user +package dev.slne.surf.transaction.core.client.transaction import dev.slne.surf.cloud.api.client.netty.packet.awaitOrThrow import dev.slne.surf.cloud.api.client.netty.packet.fireAndAwaitOrThrow import dev.slne.surf.cloud.api.common.player.OfflineCloudPlayer +import dev.slne.surf.transaction.api.account.Account import dev.slne.surf.transaction.api.currency.Currency import dev.slne.surf.transaction.api.transaction.TransactionResult import dev.slne.surf.transaction.api.transaction.data.TransactionData -import dev.slne.surf.transaction.api.user.InternalTransactionUserBridge -import dev.slne.surf.transaction.core.netty.packets.ServerboundBalancePacket -import dev.slne.surf.transaction.core.netty.packets.ServerboundExecuteSingleTransactionPacket -import dev.slne.surf.transaction.core.netty.packets.ServerboundTransferTransactionPacket +import dev.slne.surf.transaction.core.netty.packets.serverbound.ServerboundBalancePacket +import dev.slne.surf.transaction.core.netty.packets.serverbound.ServerboundExecuteSingleTransactionPacket +import dev.slne.surf.transaction.core.netty.packets.serverbound.ServerboundTransferTransactionPacket +import dev.slne.surf.transaction.core.transaction.CommonTransactionBridgeImpl import it.unimi.dsi.fastutil.objects.ObjectSet import org.springframework.stereotype.Component import java.math.BigDecimal @Component -class ClientTransactionUserBridge : InternalTransactionUserBridge { +class ClientTransactionBridge : CommonTransactionBridgeImpl() { + override suspend fun deposit( - player: OfflineCloudPlayer, + account: Account, + initiator: OfflineCloudPlayer, amount: BigDecimal, currency: Currency, ignoreMinimum: Boolean, vararg additionalData: TransactionData ): TransactionResult = ServerboundExecuteSingleTransactionPacket( - player, + account.accountId, + initiator, amount, currency, ignoreMinimum, @@ -31,15 +35,16 @@ class ClientTransactionUserBridge : InternalTransactionUserBridge { ServerboundExecuteSingleTransactionPacket.Type.DEPOSIT ).fireAndAwaitOrThrow().result - override suspend fun withdraw( - player: OfflineCloudPlayer, + account: Account, + initiator: OfflineCloudPlayer, amount: BigDecimal, currency: Currency, ignoreMinimum: Boolean, vararg additionalData: TransactionData ): TransactionResult = ServerboundExecuteSingleTransactionPacket( - player, + account.accountId, + initiator, amount, currency, ignoreMinimum, @@ -48,27 +53,29 @@ class ClientTransactionUserBridge : InternalTransactionUserBridge { ).fireAndAwaitOrThrow().result override suspend fun transfer( - sender: OfflineCloudPlayer, + initiator: OfflineCloudPlayer, + sender: Account, amount: BigDecimal, currency: Currency, - receiver: OfflineCloudPlayer, + receiver: Account, ignoreSenderMinimum: Boolean, ignoreReceiverMinimum: Boolean, additionalSenderData: ObjectSet, additionalReceiverData: ObjectSet ): TransactionResult = ServerboundTransferTransactionPacket( - sender, + initiator, + sender.accountId, amount, currency, - receiver, + receiver.accountId, ignoreSenderMinimum, ignoreReceiverMinimum, additionalSenderData, additionalReceiverData ).fireAndAwaitOrThrow().result - override suspend fun balanceDecimal( - player: OfflineCloudPlayer, + override suspend fun balance( + account: Account, currency: Currency - ): BigDecimal = ServerboundBalancePacket(player, currency).awaitOrThrow() + ): BigDecimal = ServerboundBalancePacket(account.accountId, currency).awaitOrThrow() } \ No newline at end of file diff --git a/surf-transaction-core/surf-transaction-core-common/build.gradle.kts b/surf-transaction-core/surf-transaction-core-common/build.gradle.kts index 2efa2fd..ae9a7cb 100644 --- a/surf-transaction-core/surf-transaction-core-common/build.gradle.kts +++ b/surf-transaction-core/surf-transaction-core-common/build.gradle.kts @@ -2,9 +2,12 @@ plugins { id("dev.slne.surf.surfapi.gradle.core") } +surfCoreApi { + withCloudCommon() +} + dependencies { api(project(":surf-transaction-api")) - compileOnly("dev.slne.surf.cloud:surf-cloud-api-common:1.21.7+") } kotlin { diff --git a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/account/AccountImpl.kt b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/account/AccountImpl.kt new file mode 100644 index 0000000..519dd1b --- /dev/null +++ b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/account/AccountImpl.kt @@ -0,0 +1,43 @@ +package dev.slne.surf.transaction.core.account + +import dev.slne.surf.cloud.api.common.player.OfflineCloudPlayer +import dev.slne.surf.cloud.api.common.player.toOfflineCloudPlayer +import dev.slne.surf.surfapi.core.api.messages.adventure.appendNewline +import dev.slne.surf.surfapi.core.api.messages.adventure.buildText +import dev.slne.surf.transaction.api.account.Account +import kotlinx.serialization.Contextual +import kotlinx.serialization.Serializable +import java.util.* + +@Serializable +data class AccountImpl( + override val accountId: @Contextual UUID, + val ownerUuid: @Contextual UUID, + override val name: String, + override val defaultAccount: Boolean = false +) : Account { + + override val owner: OfflineCloudPlayer + get() = ownerUuid.toOfflineCloudPlayer() + + override suspend fun asComponent() = buildText { + variableValue(name) + + hoverEvent(buildText { + variableKey("Account Id: ") + variableValue(accountId.toString()) + appendNewline(2) + + variableKey("Besitzer: ") + append(owner.displayName()) + appendNewline(2) + + variableKey("Standardkonto: ") + if (defaultAccount) { + success("Ja") + } else { + error("Nein") + } + }) + } +} \ No newline at end of file diff --git a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/account/CommonAccountBridge.kt b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/account/CommonAccountBridge.kt new file mode 100644 index 0000000..7bcb9c6 --- /dev/null +++ b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/account/CommonAccountBridge.kt @@ -0,0 +1,21 @@ +package dev.slne.surf.transaction.core.account + +import dev.slne.surf.transaction.api.account.Account +import dev.slne.surf.transaction.api.account.InternalAccountBridge +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder + +abstract class CommonAccountBridge : InternalAccountBridge { + override val descriptor: SerialDescriptor + get() = AccountImpl.serializer().descriptor + + override fun serialize(encoder: Encoder, value: Account) { + require(value is AccountImpl) { "Value must be of type AccountImpl" } + AccountImpl.serializer().serialize(encoder, value) + } + + override fun deserialize(decoder: Decoder): Account { + return AccountImpl.serializer().deserialize(decoder) + } +} \ No newline at end of file diff --git a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/CurrencyCreateResultResponsePacket.kt b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/bidirectional/CurrencyCreateResultResponsePacket.kt similarity index 81% rename from surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/CurrencyCreateResultResponsePacket.kt rename to surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/bidirectional/CurrencyCreateResultResponsePacket.kt index 4addbf3..973ec2f 100644 --- a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/CurrencyCreateResultResponsePacket.kt +++ b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/bidirectional/CurrencyCreateResultResponsePacket.kt @@ -1,4 +1,4 @@ -package dev.slne.surf.transaction.core.netty.packets +package dev.slne.surf.transaction.core.netty.packets.bidirectional import dev.slne.surf.cloud.api.common.meta.SurfNettyPacket import dev.slne.surf.cloud.api.common.netty.network.protocol.PacketFlow @@ -8,4 +8,5 @@ import kotlinx.serialization.Serializable @SurfNettyPacket("transaction:bidirectional:currency_create_result", PacketFlow.BIDIRECTIONAL) @Serializable -data class CurrencyCreateResultResponsePacket(val result: CurrencyCreateResult): ResponseNettyPacket() \ No newline at end of file +data class CurrencyCreateResultResponsePacket(val result: CurrencyCreateResult) : + ResponseNettyPacket() \ No newline at end of file diff --git a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/TransactionResultResponsePacket.kt b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/bidirectional/TransactionResultResponsePacket.kt similarity index 88% rename from surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/TransactionResultResponsePacket.kt rename to surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/bidirectional/TransactionResultResponsePacket.kt index 7d14954..d536db1 100644 --- a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/TransactionResultResponsePacket.kt +++ b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/bidirectional/TransactionResultResponsePacket.kt @@ -1,4 +1,4 @@ -package dev.slne.surf.transaction.core.netty.packets +package dev.slne.surf.transaction.core.netty.packets.bidirectional import dev.slne.surf.cloud.api.common.meta.SurfNettyPacket import dev.slne.surf.cloud.api.common.netty.network.protocol.PacketFlow diff --git a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/clientbound/ClientboundAccountResponsePacket.kt b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/clientbound/ClientboundAccountResponsePacket.kt new file mode 100644 index 0000000..77e803a --- /dev/null +++ b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/clientbound/ClientboundAccountResponsePacket.kt @@ -0,0 +1,13 @@ +package dev.slne.surf.transaction.core.netty.packets.clientbound + +import dev.slne.surf.cloud.api.common.meta.SurfNettyPacket +import dev.slne.surf.cloud.api.common.netty.network.protocol.PacketFlow +import dev.slne.surf.cloud.api.common.netty.packet.ResponseNettyPacket +import dev.slne.surf.transaction.core.account.AccountImpl +import kotlinx.serialization.Serializable + +@SurfNettyPacket("transaction:clientbound:account_response", PacketFlow.CLIENTBOUND) +@Serializable +data class ClientboundAccountResponsePacket( + val account: AccountImpl? +) : ResponseNettyPacket() \ No newline at end of file diff --git a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/clientbound/ClientboundAllAccountsResponsePacket.kt b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/clientbound/ClientboundAllAccountsResponsePacket.kt new file mode 100644 index 0000000..6cc91f9 --- /dev/null +++ b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/clientbound/ClientboundAllAccountsResponsePacket.kt @@ -0,0 +1,13 @@ +package dev.slne.surf.transaction.core.netty.packets.clientbound + +import dev.slne.surf.cloud.api.common.meta.SurfNettyPacket +import dev.slne.surf.cloud.api.common.netty.network.protocol.PacketFlow +import dev.slne.surf.cloud.api.common.netty.packet.ResponseNettyPacket +import dev.slne.surf.transaction.core.account.AccountImpl +import kotlinx.serialization.Serializable + +@SurfNettyPacket("transaction:clientbound:account_all_response", PacketFlow.CLIENTBOUND) +@Serializable +data class ClientboundAllAccountsResponsePacket( + val accounts: Set +) : ResponseNettyPacket() \ No newline at end of file diff --git a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/clientbound/ClientboundCreateAccountResponsePacket.kt b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/clientbound/ClientboundCreateAccountResponsePacket.kt new file mode 100644 index 0000000..9502b9f --- /dev/null +++ b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/clientbound/ClientboundCreateAccountResponsePacket.kt @@ -0,0 +1,13 @@ +package dev.slne.surf.transaction.core.netty.packets.clientbound + +import dev.slne.surf.cloud.api.common.meta.SurfNettyPacket +import dev.slne.surf.cloud.api.common.netty.network.protocol.PacketFlow +import dev.slne.surf.cloud.api.common.netty.packet.ResponseNettyPacket +import dev.slne.surf.transaction.api.account.AccountCreationResult +import kotlinx.serialization.Serializable + +@SurfNettyPacket("transaction:clientbound:account_create_response", PacketFlow.CLIENTBOUND) +@Serializable +data class ClientboundCreateAccountResponsePacket( + val result: AccountCreationResult +) : ResponseNettyPacket() \ No newline at end of file diff --git a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/clientbound/ClientboundDeleteAccountResponsePacket.kt b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/clientbound/ClientboundDeleteAccountResponsePacket.kt new file mode 100644 index 0000000..397e598 --- /dev/null +++ b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/clientbound/ClientboundDeleteAccountResponsePacket.kt @@ -0,0 +1,13 @@ +package dev.slne.surf.transaction.core.netty.packets.clientbound + +import dev.slne.surf.cloud.api.common.meta.SurfNettyPacket +import dev.slne.surf.cloud.api.common.netty.network.protocol.PacketFlow +import dev.slne.surf.cloud.api.common.netty.packet.ResponseNettyPacket +import dev.slne.surf.transaction.api.account.AccountDeleteResult +import kotlinx.serialization.Serializable + +@SurfNettyPacket("transaction:clientbound:account_delete_response", PacketFlow.CLIENTBOUND) +@Serializable +data class ClientboundDeleteAccountResponsePacket( + val result: AccountDeleteResult +) : ResponseNettyPacket() \ No newline at end of file diff --git a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/ClientboundRefreshCurrencies.kt b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/clientbound/ClientboundRefreshCurrencies.kt similarity index 88% rename from surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/ClientboundRefreshCurrencies.kt rename to surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/clientbound/ClientboundRefreshCurrencies.kt index 024f7af..8029a2d 100644 --- a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/ClientboundRefreshCurrencies.kt +++ b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/clientbound/ClientboundRefreshCurrencies.kt @@ -1,4 +1,4 @@ -package dev.slne.surf.transaction.core.netty.packets +package dev.slne.surf.transaction.core.netty.packets.clientbound import dev.slne.surf.cloud.api.common.meta.SurfNettyPacket import dev.slne.surf.cloud.api.common.netty.network.protocol.PacketFlow diff --git a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/ServerboundBalancePacket.kt b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundBalancePacket.kt similarity index 75% rename from surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/ServerboundBalancePacket.kt rename to surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundBalancePacket.kt index 6e987fa..2991fed 100644 --- a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/ServerboundBalancePacket.kt +++ b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundBalancePacket.kt @@ -1,15 +1,16 @@ -package dev.slne.surf.transaction.core.netty.packets +package dev.slne.surf.transaction.core.netty.packets.serverbound import dev.slne.surf.cloud.api.common.meta.SurfNettyPacket import dev.slne.surf.cloud.api.common.netty.network.protocol.PacketFlow import dev.slne.surf.cloud.api.common.netty.network.protocol.double.BigDecimalResponsePacket -import dev.slne.surf.cloud.api.common.player.OfflineCloudPlayer import dev.slne.surf.transaction.api.currency.Currency +import kotlinx.serialization.Contextual import kotlinx.serialization.Serializable +import java.util.* @SurfNettyPacket("transaction:serverbound:balance", PacketFlow.SERVERBOUND) @Serializable data class ServerboundBalancePacket( - val player: OfflineCloudPlayer, + val accountId: @Contextual UUID, val currency: Currency ) : BigDecimalResponsePacket() \ No newline at end of file diff --git a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundCreateAccountPacket.kt b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundCreateAccountPacket.kt new file mode 100644 index 0000000..06f4283 --- /dev/null +++ b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundCreateAccountPacket.kt @@ -0,0 +1,15 @@ +package dev.slne.surf.transaction.core.netty.packets.serverbound + +import dev.slne.surf.cloud.api.common.meta.SurfNettyPacket +import dev.slne.surf.cloud.api.common.netty.network.protocol.PacketFlow +import dev.slne.surf.cloud.api.common.netty.packet.RespondingNettyPacket +import dev.slne.surf.cloud.api.common.player.OfflineCloudPlayer +import dev.slne.surf.transaction.core.netty.packets.clientbound.ClientboundCreateAccountResponsePacket +import kotlinx.serialization.Serializable + +@SurfNettyPacket("transaction:serverbound:account_create", PacketFlow.SERVERBOUND) +@Serializable +class ServerboundCreateAccountPacket( + val owner: OfflineCloudPlayer, + val name: String, +) : RespondingNettyPacket() \ No newline at end of file diff --git a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/ServerboundCreateCurrencyPacket.kt b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundCreateCurrencyPacket.kt similarity index 76% rename from surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/ServerboundCreateCurrencyPacket.kt rename to surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundCreateCurrencyPacket.kt index 6cb525f..3cfbdaf 100644 --- a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/ServerboundCreateCurrencyPacket.kt +++ b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundCreateCurrencyPacket.kt @@ -1,9 +1,10 @@ -package dev.slne.surf.transaction.core.netty.packets +package dev.slne.surf.transaction.core.netty.packets.serverbound import dev.slne.surf.cloud.api.common.meta.SurfNettyPacket import dev.slne.surf.cloud.api.common.netty.network.protocol.PacketFlow import dev.slne.surf.cloud.api.common.netty.packet.RespondingNettyPacket import dev.slne.surf.transaction.core.currency.CurrencyImpl +import dev.slne.surf.transaction.core.netty.packets.bidirectional.CurrencyCreateResultResponsePacket import kotlinx.serialization.Serializable @SurfNettyPacket("transaction:serverbound:create_currency", PacketFlow.SERVERBOUND) diff --git a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundDeleteAccountPacket.kt b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundDeleteAccountPacket.kt new file mode 100644 index 0000000..a2efa33 --- /dev/null +++ b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundDeleteAccountPacket.kt @@ -0,0 +1,15 @@ +package dev.slne.surf.transaction.core.netty.packets.serverbound + +import dev.slne.surf.cloud.api.common.meta.SurfNettyPacket +import dev.slne.surf.cloud.api.common.netty.network.protocol.PacketFlow +import dev.slne.surf.cloud.api.common.netty.packet.RespondingNettyPacket +import dev.slne.surf.transaction.core.netty.packets.clientbound.ClientboundDeleteAccountResponsePacket +import kotlinx.serialization.Contextual +import kotlinx.serialization.Serializable +import java.util.* + +@SurfNettyPacket("transaction:serverbound:account_delete", PacketFlow.SERVERBOUND) +@Serializable +class ServerboundDeleteAccountPacket( + val accountId: @Contextual UUID +) : RespondingNettyPacket() \ No newline at end of file diff --git a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/ServerboundExecuteSingleTransactionPacket.kt b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundExecuteSingleTransactionPacket.kt similarity index 70% rename from surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/ServerboundExecuteSingleTransactionPacket.kt rename to surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundExecuteSingleTransactionPacket.kt index 764c25f..6680d95 100644 --- a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/ServerboundExecuteSingleTransactionPacket.kt +++ b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundExecuteSingleTransactionPacket.kt @@ -1,4 +1,4 @@ -package dev.slne.surf.transaction.core.netty.packets +package dev.slne.surf.transaction.core.netty.packets.serverbound import dev.slne.surf.cloud.api.common.meta.SurfNettyPacket import dev.slne.surf.cloud.api.common.netty.network.protocol.PacketFlow @@ -6,14 +6,17 @@ import dev.slne.surf.cloud.api.common.netty.packet.RespondingNettyPacket import dev.slne.surf.cloud.api.common.player.OfflineCloudPlayer import dev.slne.surf.transaction.api.currency.Currency import dev.slne.surf.transaction.api.transaction.data.TransactionData +import dev.slne.surf.transaction.core.netty.packets.bidirectional.TransactionResultResponsePacket import kotlinx.serialization.Contextual import kotlinx.serialization.Serializable import java.math.BigDecimal +import java.util.* @SurfNettyPacket("transaction:serverbound:deposit", PacketFlow.SERVERBOUND) @Serializable data class ServerboundExecuteSingleTransactionPacket( - val player: OfflineCloudPlayer, + val accountId: @Contextual UUID, + val initiator: OfflineCloudPlayer?, val amount: @Contextual BigDecimal, val currency: Currency, val ignoreMinimum: Boolean, @@ -28,14 +31,18 @@ data class ServerboundExecuteSingleTransactionPacket( override fun equals(other: Any?): Boolean { if (this === other) return true - if (other !is ServerboundExecuteSingleTransactionPacket) return false + if (javaClass != other?.javaClass) return false if (!super.equals(other)) return false + other as ServerboundExecuteSingleTransactionPacket + if (ignoreMinimum != other.ignoreMinimum) return false - if (player != other.player) return false + if (accountId != other.accountId) return false + if (initiator != other.initiator) return false if (amount != other.amount) return false if (currency != other.currency) return false if (!additionalData.contentEquals(other.additionalData)) return false + if (type != other.type) return false return true } @@ -43,10 +50,13 @@ data class ServerboundExecuteSingleTransactionPacket( override fun hashCode(): Int { var result = super.hashCode() result = 31 * result + ignoreMinimum.hashCode() - result = 31 * result + player.hashCode() + result = 31 * result + accountId.hashCode() + result = 31 * result + (initiator?.hashCode() ?: 0) result = 31 * result + amount.hashCode() result = 31 * result + currency.hashCode() result = 31 * result + additionalData.contentHashCode() + result = 31 * result + type.hashCode() return result } + } \ No newline at end of file diff --git a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundGetAccountByNamePacket.kt b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundGetAccountByNamePacket.kt new file mode 100644 index 0000000..7f566de --- /dev/null +++ b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundGetAccountByNamePacket.kt @@ -0,0 +1,13 @@ +package dev.slne.surf.transaction.core.netty.packets.serverbound + +import dev.slne.surf.cloud.api.common.meta.SurfNettyPacket +import dev.slne.surf.cloud.api.common.netty.network.protocol.PacketFlow +import dev.slne.surf.cloud.api.common.netty.packet.RespondingNettyPacket +import dev.slne.surf.transaction.core.netty.packets.clientbound.ClientboundAccountResponsePacket +import kotlinx.serialization.Serializable + +@SurfNettyPacket("transaction:serverbound:account_get_name", PacketFlow.SERVERBOUND) +@Serializable +class ServerboundGetAccountByNamePacket( + val accountName: String +) : RespondingNettyPacket() \ No newline at end of file diff --git a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundGetAccountPacket.kt b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundGetAccountPacket.kt new file mode 100644 index 0000000..2b77a12 --- /dev/null +++ b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundGetAccountPacket.kt @@ -0,0 +1,15 @@ +package dev.slne.surf.transaction.core.netty.packets.serverbound + +import dev.slne.surf.cloud.api.common.meta.SurfNettyPacket +import dev.slne.surf.cloud.api.common.netty.network.protocol.PacketFlow +import dev.slne.surf.cloud.api.common.netty.packet.RespondingNettyPacket +import dev.slne.surf.transaction.core.netty.packets.clientbound.ClientboundAccountResponsePacket +import kotlinx.serialization.Contextual +import kotlinx.serialization.Serializable +import java.util.* + +@SurfNettyPacket("transaction:serverbound:account_get", PacketFlow.SERVERBOUND) +@Serializable +class ServerboundGetAccountPacket( + val accountId: @Contextual UUID +) : RespondingNettyPacket() \ No newline at end of file diff --git a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundGetAllAccountsPacket.kt b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundGetAllAccountsPacket.kt new file mode 100644 index 0000000..5b4e390 --- /dev/null +++ b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundGetAllAccountsPacket.kt @@ -0,0 +1,14 @@ +package dev.slne.surf.transaction.core.netty.packets.serverbound + +import dev.slne.surf.cloud.api.common.meta.SurfNettyPacket +import dev.slne.surf.cloud.api.common.netty.network.protocol.PacketFlow +import dev.slne.surf.cloud.api.common.netty.packet.RespondingNettyPacket +import dev.slne.surf.cloud.api.common.player.OfflineCloudPlayer +import dev.slne.surf.transaction.core.netty.packets.clientbound.ClientboundAllAccountsResponsePacket +import kotlinx.serialization.Serializable + +@SurfNettyPacket("transaction:serverbound:account_get_all", PacketFlow.SERVERBOUND) +@Serializable +class ServerboundGetAllAccountsPacket( + val owner: OfflineCloudPlayer +) : RespondingNettyPacket() \ No newline at end of file diff --git a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundGetDefaultAccountPacket.kt b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundGetDefaultAccountPacket.kt new file mode 100644 index 0000000..d774859 --- /dev/null +++ b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundGetDefaultAccountPacket.kt @@ -0,0 +1,14 @@ +package dev.slne.surf.transaction.core.netty.packets.serverbound + +import dev.slne.surf.cloud.api.common.meta.SurfNettyPacket +import dev.slne.surf.cloud.api.common.netty.network.protocol.PacketFlow +import dev.slne.surf.cloud.api.common.netty.packet.RespondingNettyPacket +import dev.slne.surf.cloud.api.common.player.OfflineCloudPlayer +import dev.slne.surf.transaction.core.netty.packets.clientbound.ClientboundAccountResponsePacket +import kotlinx.serialization.Serializable + +@SurfNettyPacket("transaction:serverbound:account_default", PacketFlow.SERVERBOUND) +@Serializable +class ServerboundGetDefaultAccountPacket( + val player: OfflineCloudPlayer +) : RespondingNettyPacket() \ No newline at end of file diff --git a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/ServerboundMakeDefaultCurrencyPacket.kt b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundMakeDefaultCurrencyPacket.kt similarity index 76% rename from surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/ServerboundMakeDefaultCurrencyPacket.kt rename to surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundMakeDefaultCurrencyPacket.kt index 2e65fbf..4b42b71 100644 --- a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/ServerboundMakeDefaultCurrencyPacket.kt +++ b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundMakeDefaultCurrencyPacket.kt @@ -1,9 +1,10 @@ -package dev.slne.surf.transaction.core.netty.packets +package dev.slne.surf.transaction.core.netty.packets.serverbound import dev.slne.surf.cloud.api.common.meta.SurfNettyPacket import dev.slne.surf.cloud.api.common.netty.network.protocol.PacketFlow import dev.slne.surf.cloud.api.common.netty.packet.RespondingNettyPacket import dev.slne.surf.transaction.api.currency.Currency +import dev.slne.surf.transaction.core.netty.packets.bidirectional.CurrencyCreateResultResponsePacket import kotlinx.serialization.Serializable @SurfNettyPacket("transaction:serverbound:make_default_currency", PacketFlow.SERVERBOUND) diff --git a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/ServerboundTransferTransactionPacket.kt b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundTransferTransactionPacket.kt similarity index 75% rename from surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/ServerboundTransferTransactionPacket.kt rename to surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundTransferTransactionPacket.kt index 6c54ea6..725773d 100644 --- a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/ServerboundTransferTransactionPacket.kt +++ b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/netty/packets/serverbound/ServerboundTransferTransactionPacket.kt @@ -1,4 +1,4 @@ -package dev.slne.surf.transaction.core.netty.packets +package dev.slne.surf.transaction.core.netty.packets.serverbound import dev.slne.surf.cloud.api.common.meta.SurfNettyPacket import dev.slne.surf.cloud.api.common.netty.network.protocol.PacketFlow @@ -6,17 +6,20 @@ import dev.slne.surf.cloud.api.common.netty.packet.RespondingNettyPacket import dev.slne.surf.cloud.api.common.player.OfflineCloudPlayer import dev.slne.surf.transaction.api.currency.Currency import dev.slne.surf.transaction.api.transaction.data.TransactionData +import dev.slne.surf.transaction.core.netty.packets.bidirectional.TransactionResultResponsePacket import kotlinx.serialization.Contextual import kotlinx.serialization.Serializable import java.math.BigDecimal +import java.util.* @SurfNettyPacket("transaction:serverbound:transfer", PacketFlow.SERVERBOUND) @Serializable data class ServerboundTransferTransactionPacket( - val sender: OfflineCloudPlayer, + val initiator: OfflineCloudPlayer?, + val senderAccountId: @Contextual UUID, val amount: @Contextual BigDecimal, val currency: Currency, - val receiver: OfflineCloudPlayer, + val receiverAccountId: @Contextual UUID, val ignoreSenderMinimum: Boolean, val ignoreReceiverMinimum: Boolean, val additionalSenderData: Set, diff --git a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/transaction/TransactionBridgeImpl.kt b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/transaction/CommonTransactionBridgeImpl.kt similarity index 87% rename from surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/transaction/TransactionBridgeImpl.kt rename to surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/transaction/CommonTransactionBridgeImpl.kt index 00bfedc..481f497 100644 --- a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/transaction/TransactionBridgeImpl.kt +++ b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/transaction/CommonTransactionBridgeImpl.kt @@ -5,10 +5,8 @@ import dev.slne.surf.transaction.api.transaction.Transaction import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder -import org.springframework.stereotype.Component -@Component -class TransactionBridgeImpl: InternalTransactionBridge { +abstract class CommonTransactionBridgeImpl : InternalTransactionBridge { override val descriptor: SerialDescriptor get() = TransactionImpl.serializer().descriptor diff --git a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/transaction/TransactionImpl.kt b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/transaction/TransactionImpl.kt index 9ba9088..4e6993e 100644 --- a/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/transaction/TransactionImpl.kt +++ b/surf-transaction-core/surf-transaction-core-common/src/main/kotlin/dev/slne/surf/transaction/core/transaction/TransactionImpl.kt @@ -1,7 +1,7 @@ package dev.slne.surf.transaction.core.transaction import dev.slne.surf.cloud.api.common.player.OfflineCloudPlayer -import dev.slne.surf.cloud.api.common.player.toOfflineCloudPlayer +import dev.slne.surf.transaction.api.account.Account import dev.slne.surf.transaction.api.currency.Currency import dev.slne.surf.transaction.api.transaction.Transaction import dev.slne.surf.transaction.api.transaction.data.TransactionData @@ -13,15 +13,18 @@ import java.util.* @Serializable data class TransactionImpl( override val identifier: @Contextual UUID, - val senderUuid: @Contextual UUID?, - val receiverUuid: @Contextual UUID?, + override val initiator: OfflineCloudPlayer?, + override val senderAccountId: @Contextual UUID?, + override val receiverAccountId: @Contextual UUID?, val currencyName: String, override val amount: @Contextual BigDecimal, override val ignoreMinimumAmount: Boolean = false, override val data: Set = emptySet() ) : Transaction { - override val sender: OfflineCloudPlayer? get() = senderUuid.toOfflineCloudPlayer() - override val receiver: OfflineCloudPlayer? get() = receiverUuid.toOfflineCloudPlayer() + + override suspend fun sender() = Account[senderAccountId] + override suspend fun receiver() = Account[receiverAccountId] + override val currency: Currency get() = Currency.byName(currencyName) ?: error("Currency with name '$currencyName' not found. Ensure the currency is registered in the system.") diff --git a/surf-transaction-fallback/build.gradle.kts b/surf-transaction-fallback/build.gradle.kts deleted file mode 100644 index 7ef0441..0000000 --- a/surf-transaction-fallback/build.gradle.kts +++ /dev/null @@ -1,8 +0,0 @@ -plugins { - id("dev.slne.surf.surfapi.gradle.core") -} - -dependencies { - implementation(project(":surf-transaction-core:surf-transaction-core-common")) - implementation(libs.surf.database) -} \ No newline at end of file diff --git a/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/FallbackManager.kt b/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/FallbackManager.kt deleted file mode 100644 index d972918..0000000 --- a/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/FallbackManager.kt +++ /dev/null @@ -1,32 +0,0 @@ -package dev.slne.surf.transaction.fallback - -import dev.slne.surf.database.DatabaseManager -import dev.slne.surf.transaction.fallback.currency.FallbackCurrencyTable -import dev.slne.surf.transaction.fallback.transaction.FallbackTransactionTable -import dev.slne.surf.transaction.fallback.transaction.data.FallbackTransactionDataTable -import org.jetbrains.exposed.sql.SchemaUtils -import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction -import java.nio.file.Path - -object FallbackManager { - - private lateinit var databaseManager: DatabaseManager - - suspend fun init(configPath: Path, storagePath: Path) { - databaseManager = DatabaseManager(configPath, storagePath) - databaseManager.databaseProvider.connect() - - newSuspendedTransaction { - SchemaUtils.create( - FallbackCurrencyTable, - FallbackTransactionTable, - FallbackTransactionDataTable - ) - } - } - - fun disconnect() { - databaseManager.databaseProvider.disconnect() - } - -} \ No newline at end of file diff --git a/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/FallbackTransactionApi.kt b/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/FallbackTransactionApi.kt deleted file mode 100644 index 59241f8..0000000 --- a/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/FallbackTransactionApi.kt +++ /dev/null @@ -1,14 +0,0 @@ -package dev.slne.surf.transaction.fallback - -import com.google.auto.service.AutoService -import dev.slne.surf.transaction.api.InternalTransactionApiBridge -import net.kyori.adventure.util.Services.Fallback -import java.util.* - -@AutoService(InternalTransactionApiBridge::class) -class FallbackTransactionApi : InternalTransactionApiBridge, Fallback { - override val defaultCurrency get() = CurrencyService.defaultCurrency - override val currencies get() = CurrencyService.currencies - override fun getCurrencyByName(name: String) = CurrencyService.getCurrencyByName(name) - override fun getTransactionUser(uuid: UUID) = TransactionUserManager.get(uuid) -} \ No newline at end of file diff --git a/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/currency/FallbackCurrency.kt b/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/currency/FallbackCurrency.kt deleted file mode 100644 index c72e2ce..0000000 --- a/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/currency/FallbackCurrency.kt +++ /dev/null @@ -1,28 +0,0 @@ -package dev.slne.surf.transaction.fallback.currency - -import dev.slne.surf.transaction.core.currency.CurrencyImpl -import org.jetbrains.exposed.dao.LongEntity -import org.jetbrains.exposed.dao.LongEntityClass -import org.jetbrains.exposed.dao.id.EntityID - -class FallbackCurrency(id: EntityID) : LongEntity(id) { - companion object : LongEntityClass(FallbackCurrencyTable) - - var name by FallbackCurrencyTable.name - var displayName by FallbackCurrencyTable.displayName - var symbol by FallbackCurrencyTable.symbol - var symbolDisplay by FallbackCurrencyTable.symbolDisplay - var scale by FallbackCurrencyTable.scale - var defaultCurrency by FallbackCurrencyTable.defaultCurrency - var minimumAmount by FallbackCurrencyTable.minimumAmount - - fun toCurrency() = CurrencyImpl( - name, - displayName, - symbol, - symbolDisplay, - scale, - defaultCurrency, - minimumAmount - ) -} \ No newline at end of file diff --git a/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/currency/FallbackCurrencyService.kt b/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/currency/FallbackCurrencyService.kt deleted file mode 100644 index 3db9745..0000000 --- a/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/currency/FallbackCurrencyService.kt +++ /dev/null @@ -1,161 +0,0 @@ -package dev.slne.surf.transaction.fallback.currency - -import com.google.auto.service.AutoService -import dev.slne.surf.surfapi.core.api.util.freeze -import dev.slne.surf.surfapi.core.api.util.logger -import dev.slne.surf.surfapi.core.api.util.mutableObjectSetOf -import dev.slne.surf.transaction.api.currency.Currency -import dev.slne.surf.transaction.api.currency.Currency.Companion.CURRENCY_NAME_MAX_LENGTH -import dev.slne.surf.transaction.api.currency.Currency.Companion.CURRENCY_SYMBOL_MAX_LENGTH -import dev.slne.surf.transaction.core.currency.* -import it.unimi.dsi.fastutil.objects.ObjectSet -import kotlinx.coroutines.Dispatchers -import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer -import net.kyori.adventure.util.Services.Fallback -import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction - -@AutoService(CurrencyService::class) -class FallbackCurrencyService : CurrencyService, Fallback { - - @Volatile - private var _defaultCurrency: Currency? = null - override val defaultCurrency: Currency - get() = _defaultCurrency - ?: error("Currencies are not fetched yet. Call fetchCurrencies() first.") - - private val _fallbackCurrencies = mutableObjectSetOf() - val fallbackCurrencies = _fallbackCurrencies.freeze() - - private val _currencies = mutableObjectSetOf() - override val currencies = _currencies.freeze() - - override suspend fun fetchCurrencies(): ObjectSet { - newSuspendedTransaction(Dispatchers.IO) { - val fetchedCurrencies = FallbackCurrency.all() - - _fallbackCurrencies.clear() - _fallbackCurrencies.addAll(fetchedCurrencies) - - _currencies.clear() - _currencies.addAll(fetchedCurrencies.map { - it.toCurrency() - }) - - val defaultCurrency = _currencies.find { it.defaultCurrency } - if (defaultCurrency != null) { - _defaultCurrency = defaultCurrency - } else { - log.atWarning() - .log("No default currency found in the database. Creating a fallback default currency...") - - val fallbackDefaultCurrency = FallbackCurrency.new { - name = CurrencyImpl.DEFAULT.name - displayName = CurrencyImpl.DEFAULT.displayName - symbol = CurrencyImpl.DEFAULT.symbol - symbolDisplay = CurrencyImpl.DEFAULT.symbolDisplay - scale = CurrencyImpl.DEFAULT.scale - this.defaultCurrency = CurrencyImpl.DEFAULT.defaultCurrency - minimumAmount = CurrencyImpl.DEFAULT.minimumAmount - } - - _fallbackCurrencies.add(fallbackDefaultCurrency) - - val fallbackCurrency = fallbackDefaultCurrency.toCurrency() - _currencies.add(fallbackCurrency) - _defaultCurrency = fallbackCurrency - } - } - - return currencies - } - - override suspend fun createCurrency(currency: CurrencyImpl): CurrencyCreateResult { - return createCurrency(currency, false) - } - - override suspend fun makeDefaultCurrency(currency: CurrencyImpl): CurrencyCreateResult { - return createCurrency(currency, true) - } - - private suspend fun createCurrency( - currency: CurrencyImpl, - overrideDefault: Boolean - ): CurrencyCreateResult = - newSuspendedTransaction(Dispatchers.IO) { - val existing = FallbackCurrency - .find { FallbackCurrencyTable.name eq currency.name } - .forUpdate() - .singleOrNull() - - if (existing != null) { - if (currency.defaultCurrency && overrideDefault) { - FallbackCurrency.find { FallbackCurrencyTable.defaultCurrency eq true } - .forUpdate() - .singleOrNull() - ?.defaultCurrency = false - - existing.defaultCurrency = true - _defaultCurrency = currency - _currencies.forEach { it.defaultCurrency = it.name.equals(existing.name, true) } - } - return@newSuspendedTransaction CurrencyCreateResult.ALREADY_EXISTS - } - - - validate(currency)?.let { return@newSuspendedTransaction it } - - if (currency.defaultCurrency) { - val previousDefault = - FallbackCurrency.find { FallbackCurrencyTable.defaultCurrency eq true } - .forUpdate() - .singleOrNull() - - when { - previousDefault == null -> Unit - overrideDefault -> previousDefault.defaultCurrency = false - else -> return@newSuspendedTransaction CurrencyCreateResult.DEFAULT_ALREADY_EXISTS - } - } - - val newCurrency = FallbackCurrency.new { - name = currency.name - displayName = currency.displayName - symbol = currency.symbol - symbolDisplay = currency.symbolDisplay - scale = currency.scale - this.defaultCurrency = currency.defaultCurrency - minimumAmount = currency.minimumAmount - } - - _fallbackCurrencies.add(newCurrency) - _currencies.add(newCurrency.toCurrency()) - - if (newCurrency.defaultCurrency) { - _defaultCurrency = newCurrency.toCurrency() - _currencies.forEach { it.defaultCurrency = it.name.equals(newCurrency.name, true) } - } - - return@newSuspendedTransaction CurrencyCreateResult.SUCCESS - } - - private fun validate(c: CurrencyImpl): CurrencyCreateResult? { - fun invalidName() = c.name.isBlank() || c.name.length > CURRENCY_NAME_MAX_LENGTH - fun invalidSymbol() = c.symbol.isBlank() || c.symbol.length > CURRENCY_SYMBOL_MAX_LENGTH - - if (invalidName()) return CurrencyCreateResult.INVALID_NAME - if (invalidSymbol()) return CurrencyCreateResult.INVALID_SYMBOL - - val plainName = PlainTextComponentSerializer.plainText().serialize(c.displayName) - val plainSymbol = PlainTextComponentSerializer.plainText().serialize(c.symbolDisplay) - if (plainName.isBlank() || plainName.length > CURRENCY_NAME_MAX_LENGTH) return CurrencyCreateResult.INVALID_NAME - if (plainSymbol.isBlank() || plainSymbol.length > CURRENCY_SYMBOL_MAX_LENGTH) return CurrencyCreateResult.INVALID_SYMBOL - return null - } - - override fun getCurrencyByName(name: String) = - currencies.find { it.name.equals(name, ignoreCase = true) } - - companion object { - val log = logger() - } -} \ No newline at end of file diff --git a/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/currency/FallbackCurrencyTable.kt b/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/currency/FallbackCurrencyTable.kt deleted file mode 100644 index abc442f..0000000 --- a/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/currency/FallbackCurrencyTable.kt +++ /dev/null @@ -1,23 +0,0 @@ -package dev.slne.surf.transaction.fallback.currency - -import dev.slne.surf.transaction.api.currency.CurrencyScale -import dev.slne.surf.transaction.core.currency.CURRENCY_NAME_MAX_LENGTH -import dev.slne.surf.transaction.core.currency.CURRENCY_SYMBOL_MAX_LENGTH -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer -import org.jetbrains.exposed.dao.id.LongIdTable - -object FallbackCurrencyTable : LongIdTable("transaction_currencies") { - - val name = varchar("name", CURRENCY_NAME_MAX_LENGTH) - val displayName = largeText("display_name").transform( - { GsonComponentSerializer.gson().deserialize(it) }, - { GsonComponentSerializer.gson().serialize(it) }) - val symbol = varchar("symbol", CURRENCY_SYMBOL_MAX_LENGTH) - val symbolDisplay = largeText("symbol_display").transform( - { GsonComponentSerializer.gson().deserialize(it) }, - { GsonComponentSerializer.gson().serialize(it) }) - val scale = enumerationByName("scale", 255) - val defaultCurrency = bool("default_currency") - val minimumAmount = decimal("minimum_amount", 20, 10) - -} \ No newline at end of file diff --git a/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/transaction/FallbackTransaction.kt b/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/transaction/FallbackTransaction.kt deleted file mode 100644 index 853db3c..0000000 --- a/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/transaction/FallbackTransaction.kt +++ /dev/null @@ -1,38 +0,0 @@ -package dev.slne.surf.transaction.fallback.transaction - -import dev.slne.surf.transaction.api.transaction.data.TransactionData -import dev.slne.surf.transaction.core.transaction.TransactionImpl -import dev.slne.surf.transaction.fallback.currency.FallbackCurrency -import dev.slne.surf.transaction.fallback.transaction.data.FallbackTransactionData -import dev.slne.surf.transaction.fallback.transaction.data.FallbackTransactionDataTable -import org.jetbrains.exposed.dao.LongEntity -import org.jetbrains.exposed.dao.LongEntityClass -import org.jetbrains.exposed.dao.id.EntityID - -class FallbackTransaction(id: EntityID) : LongEntity(id) { - companion object : LongEntityClass(FallbackTransactionTable) - - var identifier by FallbackTransactionTable.identifier - var sender by FallbackTransactionTable.sender - var receiver by FallbackTransactionTable.receiver - var amount by FallbackTransactionTable.amount - var currency by FallbackCurrency referencedOn FallbackTransactionTable.currency - - val data by FallbackTransactionData referrersOn FallbackTransactionDataTable.transaction - - fun toTransaction() = TransactionImpl( - identifier = identifier, - sender = sender, - receiver = receiver, - amount = amount, - currency = currency.toCurrency(), - ).apply { - data.addAll(this@FallbackTransaction.data.map { - TransactionData(it.key, it.value) - }) - } - - override fun toString(): String { - return toTransaction().toString() - } -} \ No newline at end of file diff --git a/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/transaction/FallbackTransactionService.kt b/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/transaction/FallbackTransactionService.kt deleted file mode 100644 index 54a238d..0000000 --- a/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/transaction/FallbackTransactionService.kt +++ /dev/null @@ -1,142 +0,0 @@ -package dev.slne.surf.transaction.fallback.transaction - -import com.google.auto.service.AutoService -import dev.slne.surf.surfapi.core.api.util.objectSetOf -import dev.slne.surf.transaction.api.currency.Currency -import dev.slne.surf.transaction.api.transaction.Transaction -import dev.slne.surf.transaction.api.transaction.TransactionResult -import dev.slne.surf.transaction.api.transaction.TransactionResultType -import dev.slne.surf.transaction.api.user.TransactionUser -import dev.slne.surf.transaction.core.transaction.TransactionService -import dev.slne.surf.transaction.fallback.currency.FallbackCurrencyService -import dev.slne.surf.transaction.fallback.currency.FallbackCurrencyTable -import dev.slne.surf.transaction.fallback.transaction.data.FallbackTransactionData -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import net.kyori.adventure.util.Services.Fallback -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.sum -import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction -import java.math.BigDecimal -import java.util.* - -@AutoService(TransactionService::class) -class FallbackTransactionService : TransactionService, Fallback { - - override suspend fun balanceDecimal( - user: TransactionUser, - currency: Currency - ): BigDecimal = newSuspendedTransaction(Dispatchers.IO) { - FallbackTransactionTable - .innerJoin(FallbackCurrencyTable) - .select(FallbackTransactionTable.amount.sum()) - .where { - (FallbackCurrencyTable.name eq currency.name) and - (FallbackTransactionTable.receiver eq user) - } - .map { it[FallbackTransactionTable.amount.sum()] } - .singleOrNull() ?: BigDecimal.ZERO - } - - private fun internalBalanceDecimal( - user: TransactionUser, - currency: Currency - ): BigDecimal { - return FallbackTransactionTable - .innerJoin(FallbackCurrencyTable) - .select(FallbackTransactionTable.amount.sum()) - .where { - (FallbackCurrencyTable.name eq currency.name) and - (FallbackTransactionTable.receiver eq user) - } - .map { it[FallbackTransactionTable.amount.sum()] } - .singleOrNull() ?: BigDecimal.ZERO - } - - override suspend fun persistTransaction(transaction: Transaction): TransactionResultType = - newSuspendedTransaction(Dispatchers.IO) { - val fallbackCurrencyService = CurrencyService.instance as FallbackCurrencyService - val fallbackCurrency = - fallbackCurrencyService.fallbackCurrencies.find { it.name == transaction.currency.name }!! - - val transactionSender = transaction.sender - val transactionReceiver = transaction.receiver - - val fallbackTransaction = FallbackTransaction.new { - identifier = transaction.identifier - sender = transactionSender - receiver = transactionReceiver - amount = transaction.amount - currency = fallbackCurrency - } - - transaction.data.forEach { - FallbackTransactionData.new { - this.transaction = fallbackTransaction.id - - key = it.key - value = it.value - } - } - - if (transactionReceiver != null) { - val balanceAfterTransaction = - internalBalanceDecimal(transactionReceiver, transaction.currency) - - if ( - balanceAfterTransaction < transaction.currency.minimumAmount && - !transaction.ignoreMinimumAmount - ) { - rollback() - - return@newSuspendedTransaction TransactionResult.RECEIVER_INSUFFICIENT_FUNDS to null - } - } - - TransactionResult.SUCCESS to transaction - } - - override suspend fun transfer( - senderTransaction: Transaction, - receiverTransaction: Transaction - ): TransactionResultType = newSuspendedTransaction(Dispatchers.IO) { - val senderResult = persistTransaction(senderTransaction) - if (senderResult.first != TransactionResult.SUCCESS) { - rollback() - - if (senderResult.first == TransactionResult.RECEIVER_INSUFFICIENT_FUNDS) { - return@newSuspendedTransaction TransactionResult.SENDER_INSUFFICIENT_FUNDS to null - } - - return@newSuspendedTransaction senderResult - } - - val receiverResult = persistTransaction(receiverTransaction) - if (receiverResult.first != TransactionResult.SUCCESS) { - rollback() - - return@newSuspendedTransaction receiverResult - } - - TransactionResult.SUCCESS to senderTransaction - } - - override suspend fun generateTransactionId(vararg excludingIds: UUID): UUID = - withContext(Dispatchers.IO) { - val excludingIdSet = objectSetOf(*excludingIds) - var generatedId = UUID.randomUUID() - - while (existsTransactionById(generatedId) || generatedId in excludingIdSet) { - generatedId = UUID.randomUUID() - } - - generatedId - } - - private suspend fun existsTransactionById(identifier: UUID) = - newSuspendedTransaction(Dispatchers.IO) { - !FallbackTransaction.find { - FallbackTransactionTable.identifier eq identifier - }.empty() - } -} \ No newline at end of file diff --git a/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/transaction/FallbackTransactionTable.kt b/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/transaction/FallbackTransactionTable.kt deleted file mode 100644 index 01f7d62..0000000 --- a/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/transaction/FallbackTransactionTable.kt +++ /dev/null @@ -1,26 +0,0 @@ -package dev.slne.surf.transaction.fallback.transaction - -import dev.slne.surf.transaction.api.user.TransactionUser -import dev.slne.surf.transaction.fallback.currency.FallbackCurrencyTable -import org.jetbrains.exposed.dao.id.LongIdTable -import java.util.* - -object FallbackTransactionTable : LongIdTable("transaction_transactions") { - - val identifier = char("identifier", 36).transform({ UUID.fromString(it) }, { it.toString() }) - - val sender = char("sender", 36).transform({ - TransactionUser[UUID.fromString(it)] - }, { - it.uuid.toString() - }).nullable() - - val receiver = char("receiver", 36).transform({ - TransactionUser[UUID.fromString(it)] - }, { - it.uuid.toString() - }).nullable() - - val currency = reference("currency", FallbackCurrencyTable) - val amount = decimal("amount", 20, 10) -} \ No newline at end of file diff --git a/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/transaction/data/FallbackTransactionData.kt b/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/transaction/data/FallbackTransactionData.kt deleted file mode 100644 index 20c329e..0000000 --- a/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/transaction/data/FallbackTransactionData.kt +++ /dev/null @@ -1,15 +0,0 @@ -package dev.slne.surf.transaction.fallback.transaction.data - -import org.jetbrains.exposed.dao.LongEntity -import org.jetbrains.exposed.dao.LongEntityClass -import org.jetbrains.exposed.dao.id.EntityID - -class FallbackTransactionData(id: EntityID) : LongEntity(id) { - - companion object : LongEntityClass(FallbackTransactionDataTable) - - var transaction by FallbackTransactionDataTable.transaction - var key by FallbackTransactionDataTable.key - var value by FallbackTransactionDataTable.value - -} diff --git a/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/transaction/data/FallbackTransactionDataTable.kt b/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/transaction/data/FallbackTransactionDataTable.kt deleted file mode 100644 index f7904dd..0000000 --- a/surf-transaction-fallback/src/main/kotlin/dev/slne/surf/transaction/fallback/transaction/data/FallbackTransactionDataTable.kt +++ /dev/null @@ -1,12 +0,0 @@ -package dev.slne.surf.transaction.fallback.transaction.data - -import dev.slne.surf.transaction.fallback.transaction.FallbackTransactionTable -import org.jetbrains.exposed.dao.id.LongIdTable - -object FallbackTransactionDataTable : LongIdTable("transaction_transaction_data") { - - val transaction = reference("transaction", FallbackTransactionTable) - val key = varchar("data_key", 255) - val value = largeText("data_value") - -} diff --git a/surf-transaction-paper/build.gradle.kts b/surf-transaction-paper/build.gradle.kts index c6ee210..7dabf88 100644 --- a/surf-transaction-paper/build.gradle.kts +++ b/surf-transaction-paper/build.gradle.kts @@ -1,3 +1,4 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import dev.slne.surf.surfapi.gradle.util.registerRequired plugins { @@ -6,7 +7,6 @@ plugins { dependencies { api(project(":surf-transaction-core:surf-transaction-core-client")) - compileOnly("dev.slne.surf.cloud:surf-cloud-api-client-paper:1.21.7+") } surfPaperPluginApi { @@ -15,10 +15,16 @@ surfPaperPluginApi { authors.addAll("Ammo", "twisti") generateLibraryLoader(false) + withCloudClientPaper() + bootstrapDependencies { registerRequired("surf-cloud-bukkit") } serverDependencies { registerRequired("surf-cloud-bukkit") } +} + +tasks.withType { + destinationDirectory.set(rootProject.file("output")) } \ No newline at end of file diff --git a/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/PaperMain.kt b/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/PaperMain.kt index 5e63f19..731e594 100644 --- a/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/PaperMain.kt +++ b/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/PaperMain.kt @@ -1,6 +1,7 @@ package dev.slne.surf.transaction.paper import com.github.shynixn.mccoroutine.folia.SuspendingJavaPlugin +import dev.slne.surf.transaction.paper.commands.account.accountCommand import dev.slne.surf.transaction.paper.commands.balance.balanceCommand import dev.slne.surf.transaction.paper.commands.currency.currencyCommand import dev.slne.surf.transaction.paper.commands.pay.payCommand @@ -13,6 +14,7 @@ class PaperMain : SuspendingJavaPlugin() { currencyCommand() balanceCommand() payCommand() + accountCommand() } } diff --git a/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/CommandPermission.kt b/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/CommandPermission.kt index b6977f5..1bca2d7 100644 --- a/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/CommandPermission.kt +++ b/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/CommandPermission.kt @@ -2,7 +2,7 @@ package dev.slne.surf.transaction.paper.commands import dev.slne.surf.surfapi.bukkit.api.permission.PermissionRegistry -object CommandPermission: PermissionRegistry() { +object CommandPermission : PermissionRegistry() { private const val PREFIX = "surf.transaction.command" val BALANCE = create("$PREFIX.balance") @@ -19,4 +19,11 @@ object CommandPermission: PermissionRegistry() { val TRANSACTION_ADMIN = create("$TRANSACTION.admin") val TRANSACTION_ADMIN_ADD = create("$TRANSACTION_ADMIN.add") val TRANSACTION_ADMIN_REMOVE = create("$TRANSACTION_ADMIN.remove") + + val ACCOUNT = create("$PREFIX.account") + val ACCOUNT_INFO = create("$ACCOUNT.info") + val ACCOUNT_LIST = create("$ACCOUNT.list") + val ACCOUNT_CREATE = create("$ACCOUNT.create") + val ACCOUNT_DELETE = create("$ACCOUNT.delete") + } \ No newline at end of file diff --git a/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/account/AccountCommand.kt b/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/account/AccountCommand.kt new file mode 100644 index 0000000..71a0745 --- /dev/null +++ b/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/account/AccountCommand.kt @@ -0,0 +1,15 @@ +package dev.slne.surf.transaction.paper.commands.account + +import dev.jorel.commandapi.kotlindsl.commandAPICommand +import dev.slne.surf.transaction.paper.commands.CommandPermission +import dev.slne.surf.transaction.paper.commands.account.subcommands.accountCreateCommand +import dev.slne.surf.transaction.paper.commands.account.subcommands.accountDeleteCommand +import dev.slne.surf.transaction.paper.commands.account.subcommands.accountListCommand + +fun accountCommand() = commandAPICommand("account") { + withPermission(CommandPermission.ACCOUNT) + + accountListCommand() + accountCreateCommand() + accountDeleteCommand() +} \ No newline at end of file diff --git a/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/account/arguments/AccountArgument.kt b/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/account/arguments/AccountArgument.kt new file mode 100644 index 0000000..56cd8b4 --- /dev/null +++ b/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/account/arguments/AccountArgument.kt @@ -0,0 +1,74 @@ +package dev.slne.surf.transaction.paper.commands.account.arguments + +import com.github.shynixn.mccoroutine.folia.launch +import com.github.shynixn.mccoroutine.folia.scope +import dev.jorel.commandapi.CommandAPI +import dev.jorel.commandapi.CommandAPICommand +import dev.jorel.commandapi.arguments.Argument +import dev.jorel.commandapi.arguments.ArgumentSuggestions +import dev.jorel.commandapi.arguments.CustomArgument +import dev.jorel.commandapi.arguments.StringArgument +import dev.slne.surf.cloud.api.common.player.toCloudPlayer +import dev.slne.surf.surfapi.core.api.messages.adventure.sendText +import dev.slne.surf.transaction.api.account.Account +import dev.slne.surf.transaction.api.user.transactionUser +import dev.slne.surf.transaction.paper.plugin +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.future.future +import java.util.concurrent.CompletableFuture + +class AccountArgument(nodeName: String) : CustomArgument, String>( + StringArgument(nodeName), + { info -> + val sender = info.sender + val cloudPlayer = sender.toCloudPlayer() + ?: throw CommandAPI.failWithString("You must be a player to use this command.") + + val input = info.input + val deferred = CompletableDeferred() + + plugin.launch { + val account = cloudPlayer.transactionUser().getAccountByName(input) + + if (account == null) { + sender.sendText { + appendPrefix() + + error("Du besitzt kein Konto mit dem Namen ") + variableValue(input) + error(".") + } + + deferred.complete(null) + } else { + deferred.complete(account) + } + } + + deferred + } +) { + init { + replaceSuggestions(ArgumentSuggestions.stringCollectionAsync { info -> + val sender = info.sender + val cloudPlayer = sender.toCloudPlayer() + ?: return@stringCollectionAsync CompletableFuture.completedFuture(emptyList()) + + plugin.scope.future { + cloudPlayer.transactionUser().getAllAccounts() + .sortedBy { it.name } + .map { it.name } + } + }) + } +} + +inline fun CommandAPICommand.accountArgument( + nodeName: String, + optional: Boolean = false, + block: Argument<*>.() -> Unit = {} +): CommandAPICommand = withArguments(AccountArgument(nodeName).apply { + isOptional = optional + block() +}) \ No newline at end of file diff --git a/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/account/subcommands/AccountCreateCommand.kt b/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/account/subcommands/AccountCreateCommand.kt new file mode 100644 index 0000000..7d02af9 --- /dev/null +++ b/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/account/subcommands/AccountCreateCommand.kt @@ -0,0 +1,34 @@ +package dev.slne.surf.transaction.paper.commands.account.subcommands + +import com.github.shynixn.mccoroutine.folia.launch +import dev.jorel.commandapi.CommandAPICommand +import dev.jorel.commandapi.kotlindsl.getValue +import dev.jorel.commandapi.kotlindsl.playerExecutor +import dev.jorel.commandapi.kotlindsl.stringArgument +import dev.jorel.commandapi.kotlindsl.subcommand +import dev.slne.surf.cloud.api.common.player.toCloudPlayer +import dev.slne.surf.surfapi.core.api.messages.adventure.sendText +import dev.slne.surf.transaction.api.user.transactionUser +import dev.slne.surf.transaction.paper.commands.CommandPermission +import dev.slne.surf.transaction.paper.plugin + +fun CommandAPICommand.accountCreateCommand() = subcommand("create") { + withPermission(CommandPermission.ACCOUNT_CREATE) + + stringArgument("name") + + playerExecutor { player, args -> + val name: String by args + + plugin.launch { + val cloudPlayer = player.toCloudPlayer() ?: return@launch + val result = cloudPlayer.transactionUser().createAccount(name) + val message = result.message + + cloudPlayer.sendText { + appendPrefix() + info(message) + } + } + } +} \ No newline at end of file diff --git a/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/account/subcommands/AccountDeleteCommand.kt b/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/account/subcommands/AccountDeleteCommand.kt new file mode 100644 index 0000000..6448001 --- /dev/null +++ b/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/account/subcommands/AccountDeleteCommand.kt @@ -0,0 +1,38 @@ +package dev.slne.surf.transaction.paper.commands.account.subcommands + +import com.github.shynixn.mccoroutine.folia.launch +import dev.jorel.commandapi.CommandAPICommand +import dev.jorel.commandapi.kotlindsl.getValue +import dev.jorel.commandapi.kotlindsl.playerExecutor +import dev.jorel.commandapi.kotlindsl.subcommand +import dev.slne.surf.cloud.api.common.player.toCloudPlayer +import dev.slne.surf.surfapi.core.api.messages.adventure.sendText +import dev.slne.surf.transaction.api.account.Account +import dev.slne.surf.transaction.api.user.transactionUser +import dev.slne.surf.transaction.paper.commands.CommandPermission +import dev.slne.surf.transaction.paper.commands.account.arguments.accountArgument +import dev.slne.surf.transaction.paper.plugin +import kotlinx.coroutines.Deferred + +fun CommandAPICommand.accountDeleteCommand() = subcommand("delete") { + withPermission(CommandPermission.ACCOUNT_DELETE) + + accountArgument("account") + + playerExecutor { player, args -> + val account: Deferred by args + + plugin.launch { + val account = account.await() ?: return@launch + val cloudPlayer = player.toCloudPlayer() ?: return@launch + + val result = cloudPlayer.transactionUser().deleteAccount(account) + val message = result.message + + cloudPlayer.sendText { + appendPrefix() + info(message) + } + } + } +} \ No newline at end of file diff --git a/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/account/subcommands/AccountListCommand.kt b/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/account/subcommands/AccountListCommand.kt new file mode 100644 index 0000000..710c9b8 --- /dev/null +++ b/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/account/subcommands/AccountListCommand.kt @@ -0,0 +1,29 @@ +package dev.slne.surf.transaction.paper.commands.account.subcommands + +import com.github.shynixn.mccoroutine.folia.launch +import dev.jorel.commandapi.CommandAPICommand +import dev.jorel.commandapi.kotlindsl.playerExecutor +import dev.jorel.commandapi.kotlindsl.subcommand +import dev.slne.surf.cloud.api.common.player.toCloudPlayer +import dev.slne.surf.surfapi.core.api.messages.adventure.sendText +import dev.slne.surf.surfapi.core.api.util.mapAsync +import dev.slne.surf.transaction.api.user.transactionUser +import dev.slne.surf.transaction.paper.commands.CommandPermission +import dev.slne.surf.transaction.paper.plugin + +fun CommandAPICommand.accountListCommand() = subcommand("list") { + withPermission(CommandPermission.ACCOUNT_LIST) + + playerExecutor { player, args -> + plugin.launch { + val cloudPlayer = player.toCloudPlayer() ?: return@launch + val accounts = + cloudPlayer.transactionUser().getAllAccounts().mapAsync { it to it.asComponent() } + + cloudPlayer.sendText { + info("Deine Accounts: ") + appendCollectionNewLine(accounts) { it.second } + } + } + } +} \ No newline at end of file diff --git a/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/balance/BalanceCommand.kt b/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/balance/BalanceCommand.kt index 88b00e6..746d393 100644 --- a/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/balance/BalanceCommand.kt +++ b/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/balance/BalanceCommand.kt @@ -9,7 +9,7 @@ import dev.slne.surf.cloud.api.common.player.OfflineCloudPlayer import dev.slne.surf.cloud.api.common.player.toOfflineCloudPlayer import dev.slne.surf.surfapi.core.api.messages.adventure.sendText import dev.slne.surf.transaction.api.currency.Currency -import dev.slne.surf.transaction.api.user.balance +import dev.slne.surf.transaction.api.user.transactionUser import dev.slne.surf.transaction.paper.commands.CommandPermission import dev.slne.surf.transaction.paper.commands.arguments.currencyArgument import dev.slne.surf.transaction.paper.plugin @@ -49,7 +49,7 @@ private fun balance( playerDeferred: Deferred ) = plugin.launch { val player = playerDeferred.await() ?: return@launch - val balance = player.balance(currency) + val balance = player.transactionUser().balance(currency) sender.sendText { appendPrefix() diff --git a/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/currency/admin/subcommands/CurrencyCreateCommand.kt b/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/currency/admin/subcommands/CurrencyCreateCommand.kt index 8a6aaef..0c85152 100644 --- a/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/currency/admin/subcommands/CurrencyCreateCommand.kt +++ b/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/currency/admin/subcommands/CurrencyCreateCommand.kt @@ -14,7 +14,7 @@ import dev.slne.surf.transaction.api.currency.Currency.Companion.CURRENCY_SYMBOL import dev.slne.surf.transaction.api.currency.CurrencyScale import dev.slne.surf.transaction.core.currency.CurrencyCreateResult import dev.slne.surf.transaction.core.currency.CurrencyImpl -import dev.slne.surf.transaction.core.netty.packets.ServerboundCreateCurrencyPacket +import dev.slne.surf.transaction.core.netty.packets.serverbound.ServerboundCreateCurrencyPacket import dev.slne.surf.transaction.paper.commands.CommandPermission import dev.slne.surf.transaction.paper.commands.arguments.currencyScaleArgument import dev.slne.surf.transaction.paper.commands.arguments.getCurrencyScale diff --git a/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/currency/admin/subcommands/CurrencyMakeDefaultCommand.kt b/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/currency/admin/subcommands/CurrencyMakeDefaultCommand.kt index 438b30c..c327688 100644 --- a/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/currency/admin/subcommands/CurrencyMakeDefaultCommand.kt +++ b/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/currency/admin/subcommands/CurrencyMakeDefaultCommand.kt @@ -11,7 +11,7 @@ import dev.slne.surf.transaction.api.currency.Currency import dev.slne.surf.transaction.api.currency.Currency.Companion.CURRENCY_NAME_MAX_LENGTH import dev.slne.surf.transaction.api.currency.Currency.Companion.CURRENCY_SYMBOL_MAX_LENGTH import dev.slne.surf.transaction.core.currency.CurrencyCreateResult -import dev.slne.surf.transaction.core.netty.packets.ServerboundMakeDefaultCurrencyPacket +import dev.slne.surf.transaction.core.netty.packets.serverbound.ServerboundMakeDefaultCurrencyPacket import dev.slne.surf.transaction.paper.commands.CommandPermission import dev.slne.surf.transaction.paper.commands.arguments.currencyArgument import dev.slne.surf.transaction.paper.plugin diff --git a/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/pay/PayCommand.kt b/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/pay/PayCommand.kt index 734c17c..5d36c03 100644 --- a/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/pay/PayCommand.kt +++ b/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/pay/PayCommand.kt @@ -13,7 +13,7 @@ import dev.slne.surf.surfapi.core.api.messages.adventure.sendText import dev.slne.surf.surfapi.core.api.util.logger import dev.slne.surf.transaction.api.currency.Currency import dev.slne.surf.transaction.api.transaction.TransactionResult -import dev.slne.surf.transaction.api.user.transfer +import dev.slne.surf.transaction.api.user.transactionUser import dev.slne.surf.transaction.paper.commands.CommandPermission import dev.slne.surf.transaction.paper.plugin import kotlinx.coroutines.Deferred @@ -54,7 +54,11 @@ private fun pay( } val currency = Currency.default() - val result = senderUser.transfer(amount, currency, receiverUser) + val result = senderUser.transactionUser().transfer( + amount = amount.toBigDecimal(), + currency = currency, + receiver = receiverUser.transactionUser().getDefaultAccount() + ) when (result) { is TransactionResult.SUCCESS, is TransactionResult.TRANSFER_SUCCESS -> handleSuccess( diff --git a/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/transaction/admin/subcommands/TransactionAddCommand.kt b/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/transaction/admin/subcommands/TransactionAddCommand.kt index d83b46f..894f802 100644 --- a/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/transaction/admin/subcommands/TransactionAddCommand.kt +++ b/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/transaction/admin/subcommands/TransactionAddCommand.kt @@ -1,7 +1,6 @@ package dev.slne.surf.transaction.paper.commands.transaction.admin.subcommands import com.github.shynixn.mccoroutine.folia.launch -import dev.jorel.commandapi.CommandTree import dev.jorel.commandapi.arguments.Argument import dev.jorel.commandapi.kotlindsl.anyExecutor import dev.jorel.commandapi.kotlindsl.doubleArgument @@ -16,7 +15,7 @@ import dev.slne.surf.surfapi.core.api.util.logger import dev.slne.surf.transaction.api.currency.Currency import dev.slne.surf.transaction.api.transaction.TransactionResult import dev.slne.surf.transaction.api.transaction.data.TransactionData -import dev.slne.surf.transaction.api.user.deposit +import dev.slne.surf.transaction.api.user.transactionUser import dev.slne.surf.transaction.paper.commands.CommandPermission import dev.slne.surf.transaction.paper.commands.arguments.currencyArgument import dev.slne.surf.transaction.paper.plugin @@ -54,12 +53,14 @@ private fun add( amount: Double ) = plugin.launch { val user = player.await() ?: return@launch - val result = user.deposit( - amount, - currency, - TransactionData( - "admin.transaction.add", - (sender as? Player)?.uniqueId?.toString() ?: sender.name + val result = user.transactionUser().deposit( + amount = amount.toBigDecimal(), + currency = currency, + additionalData = arrayOf( + TransactionData( + "admin.transaction.add", + (sender as? Player)?.uniqueId?.toString() ?: sender.name + ) ) ) diff --git a/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/transaction/admin/subcommands/TransactionRemoveCommand.kt b/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/transaction/admin/subcommands/TransactionRemoveCommand.kt index 43a1f9d..7ca464d 100644 --- a/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/transaction/admin/subcommands/TransactionRemoveCommand.kt +++ b/surf-transaction-paper/src/main/kotlin/dev/slne/surf/transaction/paper/commands/transaction/admin/subcommands/TransactionRemoveCommand.kt @@ -1,7 +1,6 @@ package dev.slne.surf.transaction.paper.commands.transaction.admin.subcommands import com.github.shynixn.mccoroutine.folia.launch -import dev.jorel.commandapi.CommandTree import dev.jorel.commandapi.arguments.Argument import dev.jorel.commandapi.kotlindsl.anyExecutor import dev.jorel.commandapi.kotlindsl.doubleArgument @@ -16,7 +15,7 @@ import dev.slne.surf.surfapi.core.api.util.logger import dev.slne.surf.transaction.api.currency.Currency import dev.slne.surf.transaction.api.transaction.TransactionResult import dev.slne.surf.transaction.api.transaction.data.TransactionData -import dev.slne.surf.transaction.api.user.withdraw +import dev.slne.surf.transaction.api.user.transactionUser import dev.slne.surf.transaction.paper.commands.CommandPermission import dev.slne.surf.transaction.paper.commands.arguments.currencyArgument import dev.slne.surf.transaction.paper.plugin @@ -54,13 +53,15 @@ private fun remove( amount: Double ) = plugin.launch { val user = player.await() ?: return@launch - val result = user.withdraw( - amount, - currency, + val result = user.transactionUser().withdraw( + amount = amount.toBigDecimal(), + currency = currency, ignoreMinimum = true, - TransactionData( - "admin.transaction.remove", - (sender as? Player)?.uniqueId?.toString() ?: sender.name + additionalData = arrayOf( + TransactionData( + "admin.transaction.remove", + (sender as? Player)?.uniqueId?.toString() ?: sender.name + ) ) ) diff --git a/surf-transaction-server/build.gradle.kts b/surf-transaction-server/build.gradle.kts index ba047c2..8a297b4 100644 --- a/surf-transaction-server/build.gradle.kts +++ b/surf-transaction-server/build.gradle.kts @@ -1,13 +1,16 @@ -import java.util.Properties -import kotlin.apply +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar plugins { id("dev.slne.surf.surfapi.gradle.core") } +surfCoreApi { + withCloudServer() + migrationMainClass("dev.slne.surf.transaction.server.GenerateExposedMigrationScriptKt") +} + dependencies { api(project(":surf-transaction-core:surf-transaction-core-common")) - compileOnly("dev.slne.surf.cloud:surf-cloud-api-server:1.21.7+") } kotlin { @@ -16,43 +19,6 @@ kotlin { } } -tasks { - register("generateExposedMigrationScript") { - group = "migration" - description = "Generate Exposed migration script" - classpath = sourceSets.main.get().allJava - mainClass.set("dev.slne.surf.transaction.server.GenerateExposedMigrationScriptKt") - - val propertiesFile = file("migration.properties") - - doFirst { - if (!propertiesFile.exists()) { - propertiesFile.parentFile.mkdirs() - propertiesFile.writeText( - """ - # Migration database config - migration.dbUrl=jdbc:mysql://localhost:3306/database - migration.dbUser= - migration.dbPassword= - """.trimIndent() - ) - throw GradleException("Created 'migration.properties' file. Please enter your credentials and run the task again.") - } - - val migrationProperties = Properties().apply { - load(propertiesFile.inputStream()) - } - - val requiredKeys = listOf("migration.dbUrl", "migration.dbUser", "migration.dbPassword") - val missing = - requiredKeys.filter { migrationProperties.getProperty(it).isNullOrBlank() } - if (missing.isNotEmpty()) { - throw GradleException("'migration.properties' is incomplete. Missing keys: ${missing.joinToString()}") - } - - systemProperties( - requiredKeys.associateWith { migrationProperties.getProperty(it) } - ) - } - } +tasks.withType { + destinationDirectory.set(rootProject.file("output")) } \ No newline at end of file diff --git a/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/GenerateExposedMigrationScript.kt b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/GenerateExposedMigrationScript.kt index 8620a52..1d3c1e7 100644 --- a/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/GenerateExposedMigrationScript.kt +++ b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/GenerateExposedMigrationScript.kt @@ -1,29 +1,19 @@ package dev.slne.surf.transaction.server -import MigrationUtils -import dev.slne.surf.cloud.api.common.config.properties.requiredSystemProperty +import dev.slne.surf.cloud.api.server.exposed.migration.generateSimpleExposedMigration +import dev.slne.surf.transaction.server.account.db.AccountTable import dev.slne.surf.transaction.server.currency.db.CurrencyTable import dev.slne.surf.transaction.server.transaction.db.TransactionDataTable import dev.slne.surf.transaction.server.transaction.db.TransactionTable -import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.ExperimentalDatabaseMigrationApi -import org.jetbrains.exposed.sql.transactions.transaction - -val database = Database.connect( - url = requiredSystemProperty("migration", "dbUrl") { it }.value(), - user = requiredSystemProperty("migration", "dbUser") { it }.value(), - password = requiredSystemProperty("migration", "dbPassword") { it }.value() -) @OptIn(ExperimentalDatabaseMigrationApi::class) fun main() { - transaction(database) { - MigrationUtils.generateMigrationScript( - TransactionTable, - TransactionDataTable, - CurrencyTable, - scriptDirectory = "src/main/resources/db/migration", - scriptName = "V1__create_transaction_tables", - ) - } + generateSimpleExposedMigration( + TransactionTable, + TransactionDataTable, + CurrencyTable, + AccountTable, + scriptName = "V4__modify_accounts_table", + ) } \ No newline at end of file diff --git a/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/account/AccountRepository.kt b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/account/AccountRepository.kt new file mode 100644 index 0000000..7a1880f --- /dev/null +++ b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/account/AccountRepository.kt @@ -0,0 +1,147 @@ +package dev.slne.surf.transaction.server.account + +import dev.slne.surf.cloud.api.common.player.OfflineCloudPlayer +import dev.slne.surf.cloud.api.common.util.mutableObjectSetOf +import dev.slne.surf.cloud.api.common.util.toObjectSet +import dev.slne.surf.cloud.api.server.plugin.CoroutineTransactional +import dev.slne.surf.transaction.api.account.Account +import dev.slne.surf.transaction.api.account.AccountDeleteResult +import dev.slne.surf.transaction.core.account.AccountImpl +import dev.slne.surf.transaction.server.account.db.AccountEntity +import dev.slne.surf.transaction.server.account.db.AccountTable +import it.unimi.dsi.fastutil.objects.ObjectSet +import org.jetbrains.exposed.sql.and +import org.springframework.stereotype.Repository +import java.util.* + +@CoroutineTransactional +@Repository +class AccountRepository { + + suspend fun deleteAccount(accountId: UUID): AccountDeleteResult { + val accountEntity = fetchByAccountId(accountId) + ?: return AccountDeleteResult.Failure(AccountDeleteResult.FailureReason.ACCOUNT_NOT_FOUND) + + accountEntity.delete() + + return AccountDeleteResult.Success(accountEntity.toApi()) + } + + /** + * Create a new account for the given [OfflineCloudPlayer] with the specified name. + * Also marks the current default account as non-default if it exists. + * + * @param owner The [OfflineCloudPlayer] who will own the account. + * @param name The name of the account to be created. + * @param defaultAccount Whether this account should be marked as the default account for the owner. + * + * @return The newly created [Account]. + */ + suspend fun createAccount( + owner: OfflineCloudPlayer, + name: String, + defaultAccount: Boolean + ): AccountImpl { + val currentDefaultAccount = fetchDefaultAccount(owner) + + if (currentDefaultAccount != null) { + currentDefaultAccount.defaultAccount = false + } + + val accountByName = fetchByAccountName(name) + + if (accountByName != null) { + if (defaultAccount) { + accountByName.defaultAccount = true + } + + return accountByName.toApi() + } + + return AccountEntity.new { + this.owner = owner.uuid + this.accountId = UUID.randomUUID() + this.name = name + this.defaultAccount = defaultAccount + }.toApi() + } + + /** + * Get the default account for a player + * + * @param player The [OfflineCloudPlayer] to get the default account for + * + * @return The default [Account] for the player + */ + suspend fun getDefaultAccount( + player: OfflineCloudPlayer + ) = fetchDefaultAccount(player)?.toApi() ?: run { + createAccount(player, player.uuid.toString(), true) + } + + /** + * Fetches the default account for the given [OfflineCloudPlayer]. + * + * @param player The [OfflineCloudPlayer] whose default account is to be fetched. + * @return The [AccountEntity] representing the default account, or null if no default account exists. + */ + suspend fun fetchDefaultAccount( + player: OfflineCloudPlayer + ): AccountEntity? = AccountEntity.find { + (AccountTable.owner eq player.uuid) and (AccountTable.defaultAccount eq true) + }.singleOrNull() + + /** + * Fetches all accounts owned by the given [OfflineCloudPlayer]. + * + * @param player The [OfflineCloudPlayer] whose accounts are to be fetched. + * @return A set of [AccountEntity] objects representing the accounts owned by the player. + */ + suspend fun fetchByPlayer(player: OfflineCloudPlayer): ObjectSet = + AccountEntity.find { AccountTable.owner eq player.uuid }.toObjectSet() + + /** + * Fetches all accounts owned by the given [OfflineCloudPlayer] and converts them to API representation. + * + * @param owner The [OfflineCloudPlayer] whose accounts are to be fetched. + * @return A set of [Account] objects representing the accounts owned by the player. + */ + suspend fun getAllAccountsByOwner(owner: OfflineCloudPlayer) = + fetchByPlayer(owner).mapTo(mutableObjectSetOf()) { it.toApi() } + + /** + * Fetches an [AccountEntity] by its account ID. + * + * @param accountId The UUID of the account to fetch. + * @return The [AccountEntity] if found, or null if not found. + */ + suspend fun fetchByAccountId(accountId: UUID): AccountEntity? = + AccountEntity.find { AccountTable.accountId eq accountId }.singleOrNull() + + /** + * Fetches an [AccountEntity] by its name. + * + * @param name The name of the account to fetch. + * @return The [AccountEntity] if found, or null if not found. + */ + suspend fun fetchByAccountName(name: String): AccountEntity? = + AccountEntity.find { AccountTable.name like name }.singleOrNull() + + /** + * Fetches an [Account] by its account ID and converts it to the API representation. + * + * @param accountId The UUID of the account to fetch. + * @return The [Account] if found, or null if not found. + */ + suspend fun getByAccountId(accountId: UUID) = + fetchByAccountId(accountId)?.toApi() + + /** + * Fetches an [Account] by its name and converts it to the API representation. + * @param name The name of the account to fetch. + * @return The [Account] if found, or null if not found. + */ + suspend fun getByAccountName(name: String) = + fetchByAccountName(name)?.toApi() + +} \ No newline at end of file diff --git a/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/account/AccountService.kt b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/account/AccountService.kt new file mode 100644 index 0000000..ae12a09 --- /dev/null +++ b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/account/AccountService.kt @@ -0,0 +1,66 @@ +package dev.slne.surf.transaction.server.account + +import dev.slne.surf.cloud.api.common.player.OfflineCloudPlayer +import dev.slne.surf.transaction.api.account.AccountCreationResult +import org.springframework.stereotype.Service +import java.util.* + +@Service +class AccountService(private val accountRepository: AccountRepository) { + + suspend fun createAccount( + owner: OfflineCloudPlayer, + name: String, + defaultAccount: Boolean + ): AccountCreationResult { + val name = name.trim().replace(" ", "_") + + if (name.length < 3) { + return AccountCreationResult.Failure( + AccountCreationResult.FailureReason.NAME_TOO_SHORT + ) + } + + if (name.length > 32) { + return AccountCreationResult.Failure( + AccountCreationResult.FailureReason.NAME_TOO_LONG + ) + } + + if (runCatching { UUID.fromString(name) }.getOrNull() != null) { + return AccountCreationResult.Failure( + AccountCreationResult.FailureReason.NAME_IS_UUID + ) + } + + val account = accountRepository.getByAccountName(name) + + if (account != null) { + return AccountCreationResult.Failure( + AccountCreationResult.FailureReason.NAME_ALREADY_EXISTS + ) + } + + return AccountCreationResult.Success( + accountRepository.createAccount( + owner, + name, + defaultAccount + ) + ) + } + + suspend fun deleteAccount(accountId: UUID) = accountRepository.deleteAccount(accountId) + + suspend fun getAccountByName(name: String) = + accountRepository.getByAccountName(name) + + suspend fun getAllAccountsByOwner(owner: OfflineCloudPlayer) = + accountRepository.getAllAccountsByOwner(owner) + + suspend fun getDefaultAccount(player: OfflineCloudPlayer) = + accountRepository.getDefaultAccount(player) + + suspend fun findAccountByAccountId(accountId: UUID) = + accountRepository.getByAccountId(accountId) +} \ No newline at end of file diff --git a/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/account/ServerAccountBridge.kt b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/account/ServerAccountBridge.kt new file mode 100644 index 0000000..9929a23 --- /dev/null +++ b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/account/ServerAccountBridge.kt @@ -0,0 +1,31 @@ +package dev.slne.surf.transaction.server.account + +import dev.slne.surf.cloud.api.common.player.OfflineCloudPlayer +import dev.slne.surf.transaction.api.account.Account +import dev.slne.surf.transaction.core.account.CommonAccountBridge +import org.springframework.stereotype.Component +import java.util.* + +@Component +class ServerAccountBridge(private val accountService: AccountService) : CommonAccountBridge() { + override suspend fun getAccountByAccountId(accountId: UUID) = + accountService.findAccountByAccountId(accountId) + + override suspend fun createAccount(owner: OfflineCloudPlayer, name: String) = + accountService.createAccount(owner, name, false) + + override suspend fun getAllAccountsByOwner(owner: OfflineCloudPlayer) = + accountService.getAllAccountsByOwner(owner) + + override suspend fun getAccounts(player: OfflineCloudPlayer) = + accountService.getAllAccountsByOwner(player) + + override suspend fun getDefaultAccountOrNull(player: OfflineCloudPlayer) = + accountService.getDefaultAccount(player) + + override suspend fun getAccountByName(name: String) = + accountService.getAccountByName(name) + + override suspend fun deleteAccount(account: Account) = + accountService.deleteAccount(account.accountId) +} \ No newline at end of file diff --git a/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/account/db/AccountEntity.kt b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/account/db/AccountEntity.kt new file mode 100644 index 0000000..9e5e20b --- /dev/null +++ b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/account/db/AccountEntity.kt @@ -0,0 +1,23 @@ +package dev.slne.surf.transaction.server.account.db + +import dev.slne.surf.transaction.core.account.AccountImpl +import org.jetbrains.exposed.dao.LongEntity +import org.jetbrains.exposed.dao.LongEntityClass +import org.jetbrains.exposed.dao.id.EntityID + +class AccountEntity(id: EntityID) : LongEntity(id) { + + companion object : LongEntityClass(AccountTable) + + var accountId by AccountTable.accountId + var owner by AccountTable.owner + var name by AccountTable.name + var defaultAccount by AccountTable.defaultAccount + + fun toApi() = AccountImpl( + accountId = accountId, + ownerUuid = owner, + name = name, + defaultAccount = defaultAccount + ) +} \ No newline at end of file diff --git a/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/account/db/AccountTable.kt b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/account/db/AccountTable.kt new file mode 100644 index 0000000..b555d10 --- /dev/null +++ b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/account/db/AccountTable.kt @@ -0,0 +1,11 @@ +package dev.slne.surf.transaction.server.account.db + +import dev.slne.surf.cloud.api.server.exposed.columns.nativeUuid +import org.jetbrains.exposed.dao.id.LongIdTable + +object AccountTable : LongIdTable("transaction_accounts") { + val accountId = nativeUuid("account_id") + val owner = nativeUuid("owner_id") + val name = varchar("name", 64).uniqueIndex() + val defaultAccount = bool("default_account").default(false) +} \ No newline at end of file diff --git a/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/currency/CurrencyService.kt b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/currency/CurrencyService.kt index 2fc7263..50b7624 100644 --- a/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/currency/CurrencyService.kt +++ b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/currency/CurrencyService.kt @@ -6,7 +6,7 @@ import dev.slne.surf.surfapi.core.api.util.mutableObjectSetOf import dev.slne.surf.transaction.api.currency.Currency import dev.slne.surf.transaction.core.currency.CurrencyCreateResult import dev.slne.surf.transaction.core.currency.CurrencyImpl -import dev.slne.surf.transaction.core.netty.packets.ClientboundRefreshCurrencies +import dev.slne.surf.transaction.core.netty.packets.clientbound.ClientboundRefreshCurrencies import org.springframework.stereotype.Service @Service diff --git a/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/netty/listener/AccountPacketListener.kt b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/netty/listener/AccountPacketListener.kt new file mode 100644 index 0000000..15f40a9 --- /dev/null +++ b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/netty/listener/AccountPacketListener.kt @@ -0,0 +1,71 @@ +package dev.slne.surf.transaction.server.netty.listener + +import dev.slne.surf.cloud.api.common.meta.SurfNettyPacketHandler +import dev.slne.surf.transaction.core.netty.packets.clientbound.ClientboundAccountResponsePacket +import dev.slne.surf.transaction.core.netty.packets.clientbound.ClientboundAllAccountsResponsePacket +import dev.slne.surf.transaction.core.netty.packets.clientbound.ClientboundCreateAccountResponsePacket +import dev.slne.surf.transaction.core.netty.packets.clientbound.ClientboundDeleteAccountResponsePacket +import dev.slne.surf.transaction.core.netty.packets.serverbound.* +import dev.slne.surf.transaction.server.account.AccountService +import org.springframework.stereotype.Component + +@Component +class AccountPacketListener( + private val accountService: AccountService +) { + @SurfNettyPacketHandler + suspend fun handleAccountByAccountId(packet: ServerboundGetAccountPacket) { + packet.respond( + ClientboundAccountResponsePacket( + accountService.findAccountByAccountId(packet.accountId) + ) + ) + } + + @SurfNettyPacketHandler + suspend fun handleDefaultAccount(packet: ServerboundGetDefaultAccountPacket) { + packet.respond( + ClientboundAccountResponsePacket( + accountService.getDefaultAccount(packet.player) + ) + ) + } + + @SurfNettyPacketHandler + suspend fun handleCreateAccount(packet: ServerboundCreateAccountPacket) { + packet.respond( + ClientboundCreateAccountResponsePacket( + accountService.createAccount( + packet.owner, + packet.name, + false + ) + ) + ) + } + + @SurfNettyPacketHandler + suspend fun handleDeleteAccount(packet: ServerboundDeleteAccountPacket) { + packet.respond( + ClientboundDeleteAccountResponsePacket( + accountService.deleteAccount(packet.accountId) + ) + ) + } + + @SurfNettyPacketHandler + suspend fun handleGetAccountByName(packet: ServerboundGetAccountByNamePacket) { + packet.respond( + ClientboundAccountResponsePacket( + accountService.getAccountByName(packet.accountName) + ) + ) + } + + @SurfNettyPacketHandler + suspend fun handleGetAllAccounts(packet: ServerboundGetAllAccountsPacket) { + val accounts = accountService.getAllAccountsByOwner(packet.owner).toSet() + + packet.respond(ClientboundAllAccountsResponsePacket(accounts)) + } +} \ No newline at end of file diff --git a/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/netty/listener/CurrencyPacketListener.kt b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/netty/listener/CurrencyPacketListener.kt index bb34d9b..a57da6c 100644 --- a/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/netty/listener/CurrencyPacketListener.kt +++ b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/netty/listener/CurrencyPacketListener.kt @@ -2,9 +2,9 @@ package dev.slne.surf.transaction.server.netty.listener import dev.slne.surf.cloud.api.common.meta.SurfNettyPacketHandler import dev.slne.surf.transaction.core.currency.CurrencyImpl -import dev.slne.surf.transaction.core.netty.packets.CurrencyCreateResultResponsePacket -import dev.slne.surf.transaction.core.netty.packets.ServerboundCreateCurrencyPacket -import dev.slne.surf.transaction.core.netty.packets.ServerboundMakeDefaultCurrencyPacket +import dev.slne.surf.transaction.core.netty.packets.bidirectional.CurrencyCreateResultResponsePacket +import dev.slne.surf.transaction.core.netty.packets.serverbound.ServerboundCreateCurrencyPacket +import dev.slne.surf.transaction.core.netty.packets.serverbound.ServerboundMakeDefaultCurrencyPacket import dev.slne.surf.transaction.server.currency.CurrencyService import org.springframework.stereotype.Component diff --git a/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/netty/listener/TransactionPacketLister.kt b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/netty/listener/TransactionPacketLister.kt index 2a2f319..d10d0cd 100644 --- a/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/netty/listener/TransactionPacketLister.kt +++ b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/netty/listener/TransactionPacketLister.kt @@ -3,10 +3,10 @@ package dev.slne.surf.transaction.server.netty.listener import dev.slne.surf.cloud.api.common.meta.SurfNettyPacketHandler import dev.slne.surf.cloud.api.common.netty.network.protocol.respond import dev.slne.surf.surfapi.core.api.util.toMutableObjectSet -import dev.slne.surf.transaction.core.netty.packets.ServerboundBalancePacket -import dev.slne.surf.transaction.core.netty.packets.ServerboundExecuteSingleTransactionPacket -import dev.slne.surf.transaction.core.netty.packets.ServerboundTransferTransactionPacket -import dev.slne.surf.transaction.core.netty.packets.TransactionResultResponsePacket +import dev.slne.surf.transaction.core.netty.packets.bidirectional.TransactionResultResponsePacket +import dev.slne.surf.transaction.core.netty.packets.serverbound.ServerboundBalancePacket +import dev.slne.surf.transaction.core.netty.packets.serverbound.ServerboundExecuteSingleTransactionPacket +import dev.slne.surf.transaction.core.netty.packets.serverbound.ServerboundTransferTransactionPacket import dev.slne.surf.transaction.server.transaction.TransactionService import org.springframework.stereotype.Component @@ -15,14 +15,20 @@ class TransactionPacketLister(private val transactionService: TransactionService @SurfNettyPacketHandler suspend fun handleBalance(packet: ServerboundBalancePacket) { - packet.respond(transactionService.balanceDecimal(packet.player, packet.currency)) + packet.respond( + transactionService.balanceDecimal( + packet.accountId, + packet.currency + ) + ) } @SurfNettyPacketHandler suspend fun handleExecuteSingleTransaction(packet: ServerboundExecuteSingleTransactionPacket) { val result = when (packet.type) { ServerboundExecuteSingleTransactionPacket.Type.DEPOSIT -> transactionService.deposit( - packet.player, + packet.initiator, + packet.accountId, packet.amount, packet.currency, packet.ignoreMinimum, @@ -30,7 +36,8 @@ class TransactionPacketLister(private val transactionService: TransactionService ) ServerboundExecuteSingleTransactionPacket.Type.WITHDRAW -> transactionService.withdraw( - packet.player, + packet.initiator, + packet.accountId, packet.amount, packet.currency, packet.ignoreMinimum, @@ -44,10 +51,11 @@ class TransactionPacketLister(private val transactionService: TransactionService @SurfNettyPacketHandler suspend fun handleTransferTransaction(packet: ServerboundTransferTransactionPacket) { val result = transactionService.transfer( - packet.sender, + packet.initiator, + packet.senderAccountId, packet.amount, packet.currency, - packet.receiver, + packet.receiverAccountId, packet.ignoreSenderMinimum, packet.ignoreReceiverMinimum, packet.additionalSenderData.toMutableObjectSet(), diff --git a/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/netty/sync/SyncCurrenciesTasks.kt b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/netty/sync/SyncCurrenciesTasks.kt index 0fa036b..dd1c56b 100644 --- a/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/netty/sync/SyncCurrenciesTasks.kt +++ b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/netty/sync/SyncCurrenciesTasks.kt @@ -2,7 +2,7 @@ package dev.slne.surf.transaction.server.netty.sync import dev.slne.surf.cloud.api.common.netty.NettyClient import dev.slne.surf.cloud.api.common.plugin.spring.task.CloudInitialSynchronizeTask -import dev.slne.surf.transaction.core.netty.packets.ClientboundRefreshCurrencies +import dev.slne.surf.transaction.core.netty.packets.clientbound.ClientboundRefreshCurrencies import dev.slne.surf.transaction.server.currency.CurrencyService import org.springframework.stereotype.Component as SpringComponent diff --git a/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/user/ServerTransactionUserBridge.kt b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/transaction/ServerTransactionBridge.kt similarity index 50% rename from surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/user/ServerTransactionUserBridge.kt rename to surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/transaction/ServerTransactionBridge.kt index f3b47f4..162a500 100644 --- a/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/user/ServerTransactionUserBridge.kt +++ b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/transaction/ServerTransactionBridge.kt @@ -1,57 +1,77 @@ -package dev.slne.surf.transaction.server.user +package dev.slne.surf.transaction.server.transaction import dev.slne.surf.cloud.api.common.player.OfflineCloudPlayer +import dev.slne.surf.transaction.api.account.Account import dev.slne.surf.transaction.api.currency.Currency -import dev.slne.surf.transaction.api.transaction.TransactionResult import dev.slne.surf.transaction.api.transaction.data.TransactionData -import dev.slne.surf.transaction.api.user.InternalTransactionUserBridge -import dev.slne.surf.transaction.server.transaction.TransactionService +import dev.slne.surf.transaction.core.transaction.CommonTransactionBridgeImpl import it.unimi.dsi.fastutil.objects.ObjectSet import org.springframework.stereotype.Component import java.math.BigDecimal @Component -class ServerTransactionUserBridge(private val service: TransactionService) : - InternalTransactionUserBridge { +class ServerTransactionBridge( + private val transactionService: TransactionService +) : CommonTransactionBridgeImpl() { override suspend fun deposit( - player: OfflineCloudPlayer, + account: Account, + initiator: OfflineCloudPlayer, amount: BigDecimal, currency: Currency, ignoreMinimum: Boolean, vararg additionalData: TransactionData - ): TransactionResult = service.deposit(player, amount, currency, ignoreMinimum, *additionalData) + ) = transactionService.deposit( + initiator, + account.accountId, + amount, + currency, + ignoreMinimum, + *additionalData + ) override suspend fun withdraw( - player: OfflineCloudPlayer, + account: Account, + initiator: OfflineCloudPlayer, amount: BigDecimal, currency: Currency, ignoreMinimum: Boolean, vararg additionalData: TransactionData - ): TransactionResult = - service.withdraw(player, amount, currency, ignoreMinimum, *additionalData) + ) = transactionService.withdraw( + initiator, + account.accountId, + amount, + currency, + ignoreMinimum, + *additionalData + ) override suspend fun transfer( - sender: OfflineCloudPlayer, + initiator: OfflineCloudPlayer, + sender: Account, amount: BigDecimal, currency: Currency, - receiver: OfflineCloudPlayer, + receiver: Account, ignoreSenderMinimum: Boolean, ignoreReceiverMinimum: Boolean, additionalSenderData: ObjectSet, additionalReceiverData: ObjectSet - ): TransactionResult = service.transfer( - sender, + ) = transactionService.transfer( + initiator, + sender.accountId, amount, currency, - receiver, + receiver.accountId, ignoreSenderMinimum, ignoreReceiverMinimum, additionalSenderData, additionalReceiverData ) - override suspend fun balanceDecimal( - player: OfflineCloudPlayer, + override suspend fun balance( + account: Account, currency: Currency - ): BigDecimal = service.balanceDecimal(player, currency) + ) = transactionService.balanceDecimal( + account.accountId, + currency + ) } \ No newline at end of file diff --git a/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/transaction/TransactionRepository.kt b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/transaction/TransactionRepository.kt index 0a29776..44d613a 100644 --- a/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/transaction/TransactionRepository.kt +++ b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/transaction/TransactionRepository.kt @@ -1,10 +1,10 @@ package dev.slne.surf.transaction.server.transaction -import dev.slne.surf.cloud.api.common.player.OfflineCloudPlayer import dev.slne.surf.cloud.api.server.plugin.CoroutineTransactional import dev.slne.surf.transaction.api.currency.Currency import dev.slne.surf.transaction.api.transaction.Transaction import dev.slne.surf.transaction.api.transaction.TransactionResult +import dev.slne.surf.transaction.server.account.AccountRepository import dev.slne.surf.transaction.server.currency.CurrencyRepository import dev.slne.surf.transaction.server.currency.db.CurrencyTable import dev.slne.surf.transaction.server.transaction.db.TransactionDataTable @@ -23,27 +23,37 @@ import java.util.* */ @Repository @CoroutineTransactional -class TransactionRepository(private val currencyRepository: CurrencyRepository) { +class TransactionRepository( + private val currencyRepository: CurrencyRepository, + private val accountRepository: AccountRepository +) { /** - * Get the balance of a user + * Get the balance of an account * - * @param user The user to get the balance of - * @param currency The currency to get the balance in + * @param accountId The [UUID] of the account to get the balance of + * @param currency The [Currency] to get the balance in * - * @return The balance of the user + * @return The balance of the account */ - suspend fun balanceDecimal(user: OfflineCloudPlayer, currency: Currency, forUpdate: Boolean = false): BigDecimal = - TransactionTable + suspend fun balanceDecimal( + accountId: UUID, + currency: Currency, + forUpdate: Boolean = false + ): BigDecimal { + val account = accountRepository.fetchByAccountId(accountId) + + return TransactionTable .innerJoin(CurrencyTable) .select(TransactionTable.amount.sum()) .where { (CurrencyTable.name eq currency.name) and - (TransactionTable.receiver eq user.uuid) + (TransactionTable.receiver eq account?.id) } .let { if (forUpdate) it.forUpdate() else it } .map { it[TransactionTable.amount.sum()] } .singleOrNull() ?: BigDecimal.ZERO + } /** * Execute a transaction @@ -55,12 +65,18 @@ class TransactionRepository(private val currencyRepository: CurrencyRepository) suspend fun persistTransaction(transaction: Transaction): TransactionResult { val currency = currencyRepository.fetchCurrencyByName(transaction.currency.name) ?: error("Currency not found: ${transaction.currency.name}") - val receiver = transaction.receiver + + val receiver = transaction.receiver() + val sender = transaction.sender() + + val senderAccount = sender?.accountId?.let { accountRepository.fetchByAccountId(it) } + val receiverAccount = receiver?.accountId?.let { accountRepository.fetchByAccountId(it) } val persistedTransaction = TransactionEntity.new { identifier = transaction.identifier - sender = transaction.sender?.uuid - this.receiver = receiver?.uuid + this.initiator = transaction.initiator?.uuid + this.senderAccount = senderAccount + this.receiverAccount = receiverAccount amount = transaction.amount this.currency = currency } @@ -72,7 +88,8 @@ class TransactionRepository(private val currencyRepository: CurrencyRepository) } if (receiver != null && !transaction.ignoreMinimumAmount) { - val balanceAfterTransaction = balanceDecimal(receiver, transaction.currency, forUpdate = true) + val balanceAfterTransaction = + balanceDecimal(receiver.accountId, transaction.currency, forUpdate = true) if (balanceAfterTransaction < transaction.currency.minimumAmount) { TransactionManager.current().rollback() return TransactionResult.RECEIVER_INSUFFICIENT_FUNDS diff --git a/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/transaction/TransactionService.kt b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/transaction/TransactionService.kt index c06a247..6b6fc40 100644 --- a/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/transaction/TransactionService.kt +++ b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/transaction/TransactionService.kt @@ -1,7 +1,7 @@ package dev.slne.surf.transaction.server.transaction import dev.slne.surf.cloud.api.common.player.OfflineCloudPlayer -import dev.slne.surf.surfapi.core.api.util.toMutableObjectSet +import dev.slne.surf.transaction.api.account.Account import dev.slne.surf.transaction.api.currency.Currency import dev.slne.surf.transaction.api.transaction.TransactionResult import dev.slne.surf.transaction.api.transaction.data.TransactionData @@ -15,7 +15,8 @@ import java.util.* class TransactionService(private val transactionRepository: TransactionRepository) { suspend fun deposit( - player: OfflineCloudPlayer, + initiator: OfflineCloudPlayer?, + accountId: UUID, amount: BigDecimal, currency: Currency, ignoreMinimum: Boolean, @@ -23,8 +24,9 @@ class TransactionService(private val transactionRepository: TransactionRepositor ): TransactionResult { val transaction = TransactionImpl( identifier = UUID.randomUUID(), - senderUuid = null, - receiverUuid = player.uuid, + initiator = initiator, + senderAccountId = null, + receiverAccountId = accountId, amount = amount, currencyName = currency.name, ignoreMinimumAmount = ignoreMinimum, @@ -35,7 +37,8 @@ class TransactionService(private val transactionRepository: TransactionRepositor } suspend fun withdraw( - player: OfflineCloudPlayer, + initiator: OfflineCloudPlayer?, + accountId: UUID, amount: BigDecimal, currency: Currency, ignoreMinimum: Boolean, @@ -45,8 +48,9 @@ class TransactionService(private val transactionRepository: TransactionRepositor val transaction = TransactionImpl( identifier = UUID.randomUUID(), - senderUuid = null, - receiverUuid = player.uuid, + senderAccountId = null, + initiator = initiator, + receiverAccountId = accountId, amount = usableAmount, currencyName = currency.name, ignoreMinimumAmount = ignoreMinimum, @@ -57,10 +61,11 @@ class TransactionService(private val transactionRepository: TransactionRepositor } suspend fun transfer( - sender: OfflineCloudPlayer, + initiator: OfflineCloudPlayer?, + senderAccountId: UUID, amount: BigDecimal, currency: Currency, - receiver: OfflineCloudPlayer, + receiverAccountId: UUID, ignoreSenderMinimum: Boolean, ignoreReceiverMinimum: Boolean, additionalSenderData: ObjectSet, @@ -70,9 +75,10 @@ class TransactionService(private val transactionRepository: TransactionRepositor val receiverAmount = amount.abs() val senderTransaction = TransactionImpl( + initiator = initiator, identifier = UUID.randomUUID(), - senderUuid = receiver.uuid, - receiverUuid = sender.uuid, + senderAccountId = receiverAccountId, + receiverAccountId = senderAccountId, amount = senderAmount, currencyName = currency.name, ignoreMinimumAmount = ignoreSenderMinimum, @@ -80,9 +86,10 @@ class TransactionService(private val transactionRepository: TransactionRepositor ) val receiverTransaction = TransactionImpl( + initiator = initiator, identifier = UUID.randomUUID(), - senderUuid = sender.uuid, - receiverUuid = receiver.uuid, + senderAccountId = senderAccountId, + receiverAccountId = receiverAccountId, amount = receiverAmount, currencyName = currency.name, ignoreMinimumAmount = ignoreReceiverMinimum, @@ -91,10 +98,14 @@ class TransactionService(private val transactionRepository: TransactionRepositor return transactionRepository.transfer(senderTransaction, receiverTransaction) } - - + suspend fun balanceDecimal( - player: OfflineCloudPlayer, + account: Account, currency: Currency - ): BigDecimal = transactionRepository.balanceDecimal(player, currency) + ): BigDecimal = transactionRepository.balanceDecimal(account.accountId, currency) + + suspend fun balanceDecimal( + accountId: UUID, + currency: Currency, + ): BigDecimal = transactionRepository.balanceDecimal(accountId, currency) } \ No newline at end of file diff --git a/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/transaction/db/TransactionDataTable.kt b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/transaction/db/TransactionDataTable.kt index c57789a..2514dba 100644 --- a/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/transaction/db/TransactionDataTable.kt +++ b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/transaction/db/TransactionDataTable.kt @@ -1,10 +1,15 @@ package dev.slne.surf.transaction.server.transaction.db import org.jetbrains.exposed.dao.id.LongIdTable +import org.jetbrains.exposed.sql.ReferenceOption -object TransactionDataTable: LongIdTable("transaction_transaction_data"){ +object TransactionDataTable : LongIdTable("transaction_transaction_data") { - val transaction = reference("transaction", TransactionTable) + val transaction = reference( + "transaction", TransactionTable, + onUpdate = ReferenceOption.CASCADE, + onDelete = ReferenceOption.CASCADE + ) val key = varchar("data_key", 255) val value = largeText("data_value") } \ No newline at end of file diff --git a/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/transaction/db/TransactionEntity.kt b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/transaction/db/TransactionEntity.kt index 189abd7..29d4669 100644 --- a/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/transaction/db/TransactionEntity.kt +++ b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/transaction/db/TransactionEntity.kt @@ -1,8 +1,10 @@ package dev.slne.surf.transaction.server.transaction.db +import dev.slne.surf.cloud.api.common.player.toOfflineCloudPlayer import dev.slne.surf.cloud.api.server.exposed.table.AuditableLongEntity import dev.slne.surf.cloud.api.server.exposed.table.AuditableLongEntityClass import dev.slne.surf.transaction.core.transaction.TransactionImpl +import dev.slne.surf.transaction.server.account.db.AccountEntity import dev.slne.surf.transaction.server.currency.db.CurrencyEntity import org.jetbrains.exposed.dao.id.EntityID @@ -10,8 +12,9 @@ class TransactionEntity(id: EntityID) : AuditableLongEntity(id, Transactio companion object : AuditableLongEntityClass(TransactionTable) var identifier by TransactionTable.identifier - var sender by TransactionTable.sender - var receiver by TransactionTable.receiver + var initiator by TransactionTable.initiator + var senderAccount by AccountEntity optionalReferencedOn TransactionTable.sender + var receiverAccount by AccountEntity optionalReferencedOn TransactionTable.receiver var amount by TransactionTable.amount var currency by CurrencyEntity referencedOn TransactionTable.currency @@ -19,8 +22,9 @@ class TransactionEntity(id: EntityID) : AuditableLongEntity(id, Transactio fun toApi() = TransactionImpl( identifier = identifier, - senderUuid = sender, - receiverUuid = receiver, + initiator = initiator?.toOfflineCloudPlayer(), + senderAccountId = senderAccount?.accountId, + receiverAccountId = receiverAccount?.accountId, amount = amount, currencyName = currency.name, data = data.map { it.toApi() }.toSet() diff --git a/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/transaction/db/TransactionTable.kt b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/transaction/db/TransactionTable.kt index 00fe283..3b3a4a0 100644 --- a/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/transaction/db/TransactionTable.kt +++ b/surf-transaction-server/src/main/kotlin/dev/slne/surf/transaction/server/transaction/db/TransactionTable.kt @@ -2,12 +2,27 @@ package dev.slne.surf.transaction.server.transaction.db import dev.slne.surf.cloud.api.server.exposed.columns.nativeUuid import dev.slne.surf.cloud.api.server.exposed.table.AuditableLongIdTable +import dev.slne.surf.transaction.server.account.db.AccountTable import dev.slne.surf.transaction.server.currency.db.CurrencyTable +import org.jetbrains.exposed.sql.ReferenceOption -object TransactionTable: AuditableLongIdTable("transaction_transactions") { +object TransactionTable : AuditableLongIdTable("transaction_transactions") { val identifier = nativeUuid("identifier").uniqueIndex() - val sender = nativeUuid("sender").nullable() - val receiver = nativeUuid("receiver").nullable() - val currency = reference("currency", CurrencyTable) + val initiator = nativeUuid("initiator_id").nullable() + val sender = optReference( + "sender_account", AccountTable, + onUpdate = ReferenceOption.CASCADE, + onDelete = ReferenceOption.CASCADE + ) + val receiver = optReference( + "receiver_account", AccountTable, + onUpdate = ReferenceOption.CASCADE, + onDelete = ReferenceOption.CASCADE + ) + val currency = reference( + "currency", CurrencyTable, + onUpdate = ReferenceOption.CASCADE, + onDelete = ReferenceOption.CASCADE + ) val amount = decimal("amount", 20, 10) } \ No newline at end of file diff --git a/surf-transaction-server/src/main/resources/db/migration/V2__add_accounts.sql b/surf-transaction-server/src/main/resources/db/migration/V2__add_accounts.sql new file mode 100644 index 0000000..9f2f649 --- /dev/null +++ b/surf-transaction-server/src/main/resources/db/migration/V2__add_accounts.sql @@ -0,0 +1,35 @@ +CREATE TABLE IF NOT EXISTS transaction_accounts +( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + account_id UUID NOT NULL, + owner_id UUID NOT NULL, + `name` VARCHAR(64) NOT NULL +); +ALTER TABLE transaction_accounts + ADD CONSTRAINT transaction_accounts_name_unique UNIQUE (`name`); +ALTER TABLE transaction_transactions + ADD initiator_id UUID NULL; +ALTER TABLE transaction_transactions + ADD sender_account BIGINT NULL; +ALTER TABLE transaction_transactions + ADD receiver_account BIGINT NULL; +ALTER TABLE transaction_transactions + MODIFY COLUMN created_at TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP(6) NOT NULL; +ALTER TABLE transaction_transactions + MODIFY COLUMN updated_at TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP(6) NOT NULL; +ALTER TABLE transaction_currencies + MODIFY COLUMN `name` CHAR(16) NOT NULL; +ALTER TABLE transaction_currencies + MODIFY COLUMN symbol CHAR(16) NOT NULL; +ALTER TABLE transaction_transactions + ADD CONSTRAINT fk_transaction_transactions_sender_account__id FOREIGN KEY (sender_account) REFERENCES transaction_accounts (id) ON DELETE RESTRICT ON UPDATE RESTRICT; +ALTER TABLE transaction_transactions + ADD CONSTRAINT fk_transaction_transactions_receiver_account__id FOREIGN KEY (receiver_account) REFERENCES transaction_accounts (id) ON DELETE RESTRICT ON UPDATE RESTRICT; +ALTER TABLE transaction_transactions + DROP COLUMN sender; +ALTER TABLE transaction_transactions + DROP COLUMN receiver; +ALTER TABLE transaction_currencies + DROP COLUMN created_at; +ALTER TABLE transaction_currencies + DROP COLUMN updated_at; diff --git a/surf-transaction-server/src/main/resources/db/migration/V3__add_foreign_key_constraints.sql b/surf-transaction-server/src/main/resources/db/migration/V3__add_foreign_key_constraints.sql new file mode 100644 index 0000000..469dd46 --- /dev/null +++ b/surf-transaction-server/src/main/resources/db/migration/V3__add_foreign_key_constraints.sql @@ -0,0 +1,16 @@ +ALTER TABLE transaction_transactions + DROP FOREIGN KEY fk_transaction_transactions_sender_account__id; +ALTER TABLE transaction_transactions + ADD CONSTRAINT fk_transaction_transactions_sender_account__id FOREIGN KEY (sender_account) REFERENCES transaction_accounts (id) ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE transaction_transactions + DROP FOREIGN KEY fk_transaction_transactions_receiver_account__id; +ALTER TABLE transaction_transactions + ADD CONSTRAINT fk_transaction_transactions_receiver_account__id FOREIGN KEY (receiver_account) REFERENCES transaction_accounts (id) ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE transaction_transactions + DROP FOREIGN KEY fk_transaction_currency; +ALTER TABLE transaction_transactions + ADD CONSTRAINT fk_transaction_transactions_currency__id FOREIGN KEY (currency) REFERENCES transaction_currencies (id) ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE transaction_transaction_data + DROP FOREIGN KEY fk_transaction_data_transaction; +ALTER TABLE transaction_transaction_data + ADD CONSTRAINT fk_transaction_transaction_data_transaction__id FOREIGN KEY (`transaction`) REFERENCES transaction_transactions (id) ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/surf-transaction-server/src/main/resources/db/migration/V4__add_default_account_table.sql b/surf-transaction-server/src/main/resources/db/migration/V4__add_default_account_table.sql new file mode 100644 index 0000000..9f6a2d2 --- /dev/null +++ b/surf-transaction-server/src/main/resources/db/migration/V4__add_default_account_table.sql @@ -0,0 +1 @@ +ALTER TABLE transaction_accounts ADD default_account BOOLEAN DEFAULT FALSE NOT NULL; diff --git a/surf-transaction-velocity/build.gradle.kts b/surf-transaction-velocity/build.gradle.kts index 4c4090e..59cf874 100644 --- a/surf-transaction-velocity/build.gradle.kts +++ b/surf-transaction-velocity/build.gradle.kts @@ -1,7 +1,13 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + plugins { id("dev.slne.surf.surfapi.gradle.velocity") } +surfVelocityApi { + withCloudClientVelocity() +} + velocityPluginFile { main = "dev.slne.surf.transaction.velocity.VelocityMain" pluginDependencies { @@ -11,5 +17,8 @@ velocityPluginFile { dependencies { api(project(":surf-transaction-core:surf-transaction-core-client")) - compileOnly("dev.slne.surf.cloud:surf-cloud-api-client-velocity:1.21.7+") +} + +tasks.withType { + destinationDirectory.set(rootProject.file("output")) } \ No newline at end of file