diff --git a/docker-compose.yml b/docker-compose.yml index 175b847..2db713f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,6 +9,10 @@ services: ports: - "25565:25565" environment: + LEVEL_TYPE: FLAT + EXISTING_OPS_FILE: MERGE + OPS: | + RomaRoman EULA: true ONLINE_MODE: false # Forge -------------------- diff --git a/gradle.properties b/gradle.properties index 33acc36..2f0469e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ klibs.java.ktarget=21 # Project klibs.project.name=AstraMarket klibs.project.group=ru.astrainteractive.astramarket -klibs.project.version.string=1.24.0 +klibs.project.version.string=1.25.0 klibs.project.description=Market plugin for EmpireSMP klibs.project.developers=makeevrserg|Makeev Roman|makeevrserg@gmail.com klibs.project.url=https://empireprojekt.ru diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 323bd94..897cfd8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -28,7 +28,7 @@ kotlin-serialization = "1.11.0" kotlin-serialization-kaml = "0.104.0" kotlin-version = "2.2.0" ktor = "3.4.3" -minecraft-astralibs = "3.36.2" +minecraft-astralibs = "3.38.0" minecraft-brigadier = "1.3.10" minecraft-bstats = "3.2.1" minecraft-bungee = "26.1-R0.1-SNAPSHOT" diff --git a/modules/api-market/build.gradle.kts b/modules/api-market/build.gradle.kts index dcc00cc..f308fcf 100644 --- a/modules/api-market/build.gradle.kts +++ b/modules/api-market/build.gradle.kts @@ -17,4 +17,5 @@ dependencies { testImplementation(libs.driver.h2) testImplementation(libs.tests.kotlin.test) + testImplementation(libs.kotlin.coroutines.test) } diff --git a/modules/api-market/src/test/java/ru/astrainteractive/astramarket/api/market/MarketApiTest.kt b/modules/api-market/src/test/java/ru/astrainteractive/astramarket/api/market/MarketApiTest.kt deleted file mode 100644 index ce652cd..0000000 --- a/modules/api-market/src/test/java/ru/astrainteractive/astramarket/api/market/MarketApiTest.kt +++ /dev/null @@ -1,93 +0,0 @@ -package ru.astrainteractive.astramarket.api.market - -import kotlinx.coroutines.runBlocking -import ru.astrainteractive.astralibs.encoding.model.EncodedObject -import ru.astrainteractive.astralibs.util.YamlStringFormat -import ru.astrainteractive.astramarket.api.market.model.MarketSlot -import ru.astrainteractive.astramarket.di.ApiMarketModule -import ru.astrainteractive.klibs.mikro.core.coroutines.CoroutineFeature -import ru.astrainteractive.klibs.mikro.core.dispatchers.DefaultKotlinDispatchers -import ru.astrainteractive.klibs.mikro.exposed.model.DatabaseConfiguration -import java.io.File -import java.nio.file.Files -import java.util.UUID -import kotlin.random.Random -import kotlin.test.AfterTest -import kotlin.test.BeforeTest -import kotlin.test.Test -import kotlin.test.assertEquals - -class MarketApiTest { - - private var module: ApiMarketModule? = null - private val marketApi: MarketApi - get() = module?.marketApi ?: error("Module is null") - private val tempDir: File - get() = Files.createTempDirectory("TMP_DIR").toFile() - - private val randomAuction: MarketSlot - get() = MarketSlot( - id = -1, - minecraftUuid = UUID.randomUUID().toString(), - time = System.currentTimeMillis(), - item = EncodedObject.ByteArray(ByteArray(0)), - price = Random.nextInt().toFloat(), - expired = false, - minecraftUsername = "romaroman" - ) - - @BeforeTest - fun setup(): Unit = runBlocking { - tempDir.deleteRecursively() - val module = ApiMarketModule.Default( - dispatchers = DefaultKotlinDispatchers, - yamlStringFormat = YamlStringFormat(), - dataFolder = tempDir.also { it.deleteOnExit() }, - ioScope = CoroutineFeature.Unconfined, - default = { DatabaseConfiguration.H2(tempDir.resolve("db").absolutePath) } - ) - module.lifecycle.onEnable() - this@MarketApiTest.module = module - } - - @AfterTest - fun destroy(): Unit = runBlocking { - tempDir.deleteRecursively() - module?.lifecycle?.onDisable() - module = null - } - - @Test - fun `Insert, fetch, expire same auction`(): Unit = runBlocking { - val auction = randomAuction - // Insert and fetch - val id = marketApi.insertSlot(auction)!! - var auctionDTO = marketApi.getSlot(id)!! - assertEquals(auctionDTO.minecraftUuid, auction.minecraftUuid) - // Get unexpiredAuctions - var amount = marketApi.getUserSlots(auctionDTO.minecraftUuid, false)!!.size - assertEquals(amount, 1) - // Expire - marketApi.expireSlot(auctionDTO) - assertEquals(auctionDTO.expired, false) - auctionDTO = marketApi.getSlot(id)!! - assertEquals(auctionDTO.expired, true) - // Get expiredAuctions - amount = marketApi.getUserSlots(auctionDTO.minecraftUuid, true)!!.size - assertEquals(amount, 1) - // Get unexpiredAuctions - amount = marketApi.getUserSlots(auctionDTO.minecraftUuid, false)!!.size - assertEquals(amount, 0) - // Count auctions - amount = marketApi.countPlayerSlots(auctionDTO.minecraftUuid)!! - assertEquals(amount, 1) - // Delete and count auction - marketApi.deleteSlot(auctionDTO) - amount = marketApi.countPlayerSlots(auctionDTO.minecraftUuid)!! - assertEquals(amount, 0) - val oldAuctionDTO = randomAuction.copy(time = 0) - marketApi.insertSlot(oldAuctionDTO) - amount = marketApi.getSlotsOlderThan(System.currentTimeMillis() - 1)!!.size - assertEquals(amount, 1) - } -} diff --git a/modules/api-market/src/test/java/ru/astrainteractive/astramarket/api/market/impl/ExposedMarketApiTest.kt b/modules/api-market/src/test/java/ru/astrainteractive/astramarket/api/market/impl/ExposedMarketApiTest.kt new file mode 100644 index 0000000..fcfd195 --- /dev/null +++ b/modules/api-market/src/test/java/ru/astrainteractive/astramarket/api/market/impl/ExposedMarketApiTest.kt @@ -0,0 +1,253 @@ +package ru.astrainteractive.astramarket.api.market.impl + +import kotlinx.coroutines.test.runTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.assertTrue + +class ExposedMarketApiTest { + private suspend fun withTestContext(block: suspend TestContext.() -> Unit) { + val ctx = TestContext() + ctx.onStart() + try { + ctx.block() + } finally { + ctx.onDisable() + } + } + + @Test + fun GIVEN_emptyDatabase_WHEN_insertSlot_THEN_returnsNonNullId() { + runTest { + withTestContext { + val insertedId = marketApi.insertSlot(createMarketSlot()) + assertNotNull(insertedId) + } + } + } + + @Test + fun GIVEN_insertedSlot_WHEN_getSlotById_THEN_returnsSlotWithMatchingData() { + runTest { + withTestContext { + val slot = createMarketSlot(price = 250.0f, username = "Trader") + val insertedId = marketApi.insertSlot(slot) + assertNotNull(insertedId) + + val retrieved = marketApi.getSlot(insertedId) + assertNotNull(retrieved) + + assertEquals(slot.price, retrieved.price) + assertEquals(slot.minecraftUuid, retrieved.minecraftUuid) + assertEquals(slot.minecraftUsername, retrieved.minecraftUsername) + } + } + } + + @Test + fun GIVEN_nonExistentId_WHEN_getSlot_THEN_returnsNull() { + runTest { + withTestContext { + val retrieved = marketApi.getSlot(99999) + assertNull(retrieved) + } + } + } + + @Test + fun GIVEN_insertedActiveSlot_WHEN_getActiveSlots_THEN_slotAppearsInList() { + runTest { + withTestContext { + val slot = createMarketSlot(expired = false) + marketApi.insertSlot(slot) + + val activeSlots = marketApi.getSlots(isExpired = false) + assertNotNull(activeSlots) + + assertEquals(1, activeSlots.size) + assertEquals(slot.minecraftUuid, activeSlots.first().minecraftUuid) + } + } + } + + @Test + fun GIVEN_insertedActiveSlot_WHEN_getExpiredSlots_THEN_returnsEmptyList() { + runTest { + withTestContext { + marketApi.insertSlot(createMarketSlot(expired = false)) + + val expiredSlots = marketApi.getSlots(isExpired = true) + assertNotNull(expiredSlots) + + assertTrue(expiredSlots.isEmpty()) + } + } + } + + @Test + fun GIVEN_insertedActiveSlot_WHEN_expireSlot_THEN_slotMovesToExpiredList() { + runTest { + withTestContext { + val insertedId = marketApi.insertSlot(createMarketSlot(expired = false)) + assertNotNull(insertedId) + val activeSlot = marketApi.getSlot(insertedId) + assertNotNull(activeSlot) + + marketApi.expireSlot(activeSlot) + + val expiredSlots = marketApi.getSlots(isExpired = true) + assertNotNull(expiredSlots) + val activeSlots = marketApi.getSlots(isExpired = false) + assertNotNull(activeSlots) + assertEquals(1, expiredSlots.size) + assertTrue(activeSlots.isEmpty()) + } + } + } + + @Test + fun GIVEN_insertedSlot_WHEN_deleteSlot_THEN_slotCannotBeRetrieved() { + runTest { + withTestContext { + val insertedId = marketApi.insertSlot(createMarketSlot()) + assertNotNull(insertedId) + val insertedSlot = marketApi.getSlot(insertedId) + assertNotNull(insertedSlot) + + marketApi.deleteSlot(insertedSlot) + + val retrieved = marketApi.getSlot(insertedId) + assertNull(retrieved) + } + } + } + + @Test + fun GIVEN_deletedSlot_WHEN_getSlots_THEN_deletedSlotIsAbsent() { + runTest { + withTestContext { + val insertedId = marketApi.insertSlot(createMarketSlot()) + assertNotNull(insertedId) + val insertedSlot = marketApi.getSlot(insertedId) + assertNotNull(insertedSlot) + + marketApi.deleteSlot(insertedSlot) + + val allSlots = marketApi.getSlots(isExpired = false) + assertNotNull(allSlots) + assertTrue(allSlots.none { it.id == insertedId }) + } + } + } + + @Test + fun GIVEN_multipleSlotsForPlayer_WHEN_countPlayerSlots_THEN_returnsExactCount() { + runTest { + withTestContext { + val uuid = "550e8400-e29b-41d4-a716-446655440001" + repeat(3) { marketApi.insertSlot(createMarketSlot(uuid = uuid)) } + + val count = marketApi.countPlayerSlots(uuid) + + assertEquals(3, count) + } + } + } + + @Test + fun GIVEN_noSlotsForPlayer_WHEN_countPlayerSlots_THEN_returnsZero() { + runTest { + withTestContext { + val uuid = "550e8400-e29b-41d4-a716-446655440001" + + val count = marketApi.countPlayerSlots(uuid) + + assertEquals(0, count) + } + } + } + + @Test + fun GIVEN_slotsForTwoPlayers_WHEN_getUserSlots_THEN_returnsOnlyRequestedPlayerSlots() { + runTest { + withTestContext { + val firstPlayerUuid = "550e8400-e29b-41d4-a716-446655440001" + val secondPlayerUuid = "550e8400-e29b-41d4-a716-446655440002" + repeat(2) { marketApi.insertSlot(createMarketSlot(uuid = firstPlayerUuid)) } + marketApi.insertSlot(createMarketSlot(uuid = secondPlayerUuid)) + + val firstPlayerSlots = marketApi.getUserSlots(firstPlayerUuid, isExpired = false) + assertNotNull(firstPlayerSlots) + + assertEquals(2, firstPlayerSlots.size) + assertTrue(firstPlayerSlots.all { it.minecraftUuid == firstPlayerUuid }) + } + } + } + + @Test + fun GIVEN_activeAndExpiredSlotsForPlayer_WHEN_getUserActiveSlots_THEN_returnsOnlyActiveSlots() { + runTest { + withTestContext { + val uuid = "550e8400-e29b-41d4-a716-446655440001" + marketApi.insertSlot(createMarketSlot(uuid = uuid, expired = false)) + marketApi.insertSlot(createMarketSlot(uuid = uuid, expired = true)) + + val activeUserSlots = marketApi.getUserSlots(uuid, isExpired = false) + assertNotNull(activeUserSlots) + + assertEquals(1, activeUserSlots.size) + assertTrue(activeUserSlots.all { !it.expired }) + } + } + } + + @Test + fun GIVEN_slotCreatedLongAgo_WHEN_getSlotsOlderThan_THEN_slotIsIncluded() { + runTest { + withTestContext { + val oneHourInMillis = 60 * 60 * 1000L + marketApi.insertSlot(createMarketSlot(timeOffset = -oneHourInMillis * 2)) + + val oldSlots = marketApi.getSlotsOlderThan(oneHourInMillis) + assertNotNull(oldSlots) + + assertEquals(1, oldSlots.size) + } + } + } + + @Test + fun GIVEN_recentlyCreatedSlot_WHEN_getSlotsOlderThan_THEN_slotIsNotIncluded() { + runTest { + withTestContext { + val oneHourInMillis = 60 * 60 * 1000L + marketApi.insertSlot(createMarketSlot(timeOffset = 0L)) + + val oldSlots = marketApi.getSlotsOlderThan(oneHourInMillis) + assertNotNull(oldSlots) + + assertTrue(oldSlots.isEmpty()) + } + } + } + + @Test + fun GIVEN_mixOfOldAndNewSlots_WHEN_getSlotsOlderThan_THEN_returnsOnlyOldSlots() { + runTest { + withTestContext { + val oneHourInMillis = 60 * 60 * 1000L + marketApi.insertSlot(createMarketSlot(timeOffset = -oneHourInMillis * 2)) + marketApi.insertSlot(createMarketSlot(timeOffset = -oneHourInMillis * 3)) + marketApi.insertSlot(createMarketSlot(timeOffset = 0L)) + + val oldSlots = marketApi.getSlotsOlderThan(oneHourInMillis) + assertNotNull(oldSlots) + + assertEquals(2, oldSlots.size) + } + } + } +} diff --git a/modules/api-market/src/test/java/ru/astrainteractive/astramarket/api/market/impl/TestContext.kt b/modules/api-market/src/test/java/ru/astrainteractive/astramarket/api/market/impl/TestContext.kt new file mode 100644 index 0000000..7fb8251 --- /dev/null +++ b/modules/api-market/src/test/java/ru/astrainteractive/astramarket/api/market/impl/TestContext.kt @@ -0,0 +1,49 @@ +package ru.astrainteractive.astramarket.api.market.impl + +import kotlinx.coroutines.flow.flowOf +import org.jetbrains.exposed.v1.jdbc.Database +import org.jetbrains.exposed.v1.jdbc.SchemaUtils +import org.jetbrains.exposed.v1.jdbc.transactions.transaction +import ru.astrainteractive.astralibs.encoding.model.EncodedObject +import ru.astrainteractive.astramarket.api.market.model.MarketSlot +import ru.astrainteractive.astramarket.db.market.entity.AuctionTable +import ru.astrainteractive.klibs.mikro.core.dispatchers.DefaultKotlinDispatchers +import java.nio.file.Files + +internal class TestContext { + private val tempDir = Files.createTempDirectory("astra-market-test").toFile() + val database = Database.connect( + url = "jdbc:h2:${tempDir.absolutePath}/test", + driver = "org.h2.Driver" + ) + val marketApi = ExposedMarketApi( + databaseFlow = flowOf(database), + dispatchers = DefaultKotlinDispatchers + ) + + fun onStart() { + transaction(database) { + SchemaUtils.create(AuctionTable) + } + } + + fun onDisable() { + tempDir.deleteRecursively() + } + + fun createMarketSlot( + uuid: String = "550e8400-e29b-41d4-a716-446655440000", + username: String = "TestPlayer", + price: Float = 100.0f, + expired: Boolean = false, + timeOffset: Long = 0L + ): MarketSlot = MarketSlot( + id = 0, + minecraftUuid = uuid, + minecraftUsername = username, + time = System.currentTimeMillis() + timeOffset, + item = EncodedObject.ByteArray("test_item".toByteArray()), + price = price, + expired = expired + ) +} diff --git a/modules/command-bukkit/src/main/kotlin/ru/astrainteractive/astramarket/command/auction/AuctionCommandExecutor.kt b/modules/command-bukkit/src/main/kotlin/ru/astrainteractive/astramarket/command/auction/AuctionCommandExecutor.kt index c2679aa..af2eaef 100644 --- a/modules/command-bukkit/src/main/kotlin/ru/astrainteractive/astramarket/command/auction/AuctionCommandExecutor.kt +++ b/modules/command-bukkit/src/main/kotlin/ru/astrainteractive/astramarket/command/auction/AuctionCommandExecutor.kt @@ -28,7 +28,7 @@ internal class AuctionCommandExecutor( when (input) { is AuctionCommand.Result.OpenSlots -> { val route = GuiRouter.Route.Slots( - player = input.player, + inventoryOwner = input.player, isExpired = input.isExpired, targetPlayerUUID = input.targetPlayerUUID ) @@ -37,7 +37,7 @@ internal class AuctionCommandExecutor( is AuctionCommand.Result.OpenPlayers -> { val route = GuiRouter.Route.Players( - player = input.player, + inventoryOwner = input.player, isExpired = input.isExpired ) router.navigate(route) diff --git a/modules/core/src/main/kotlin/ru/astrainteractive/astramarket/core/PluginTranslation.kt b/modules/core/src/main/kotlin/ru/astrainteractive/astramarket/core/PluginTranslation.kt index 8f3579e..6244180 100644 --- a/modules/core/src/main/kotlin/ru/astrainteractive/astramarket/core/PluginTranslation.kt +++ b/modules/core/src/main/kotlin/ru/astrainteractive/astramarket/core/PluginTranslation.kt @@ -125,9 +125,7 @@ data class PluginTranslation( .plus("&#f55442У вас уже макстимальное число лотов") .toRaw(), @SerialName("auction_by") - private val auctionBy: StringDesc.Raw = PREFIX - .plus("&7Выставил: &#d6a213%player_owner%") - .toRaw(), + private val auctionBy: StringDesc.Raw = StringDesc.Raw("&7Выставил: &#d6a213%player_owner%"), @SerialName("auction_created_ago") private val auctionCreatedAgo: StringDesc.Raw = StringDesc.Raw("&7Время: &#d6a213%time%"), @SerialName("auction_last") @@ -232,8 +230,12 @@ data class PluginTranslation( val unexpectedError: StringDesc.Raw = PREFIX .plus("&#f55442Произошла непредвиденная ошибка") .toRaw(), - @SerialName("time_format") - val timeAgoFormat: StringDesc.Raw = StringDesc.Raw("%days%дн. %hours%ч. %minutes%м. назад") + @SerialName("time_format_dhm") + val timeAgoFormatDHM: StringDesc.Raw = StringDesc.Raw("%days%дн. %hours%ч. %minutes%м. назад"), + @SerialName("time_format_hm") + val timeAgoFormatHM: StringDesc.Raw = StringDesc.Raw("%hours%ч. %minutes%м. назад"), + @SerialName("time_format_m") + val timeAgoFormatM: StringDesc.Raw = StringDesc.Raw("%minutes%м. назад") ) companion object { diff --git a/modules/gui/api/src/main/kotlin/ru/astrainteractive/astramarket/gui/router/GuiRouter.kt b/modules/gui/api/src/main/kotlin/ru/astrainteractive/astramarket/gui/router/GuiRouter.kt index e1ca18c..229eb1e 100644 --- a/modules/gui/api/src/main/kotlin/ru/astrainteractive/astramarket/gui/router/GuiRouter.kt +++ b/modules/gui/api/src/main/kotlin/ru/astrainteractive/astramarket/gui/router/GuiRouter.kt @@ -5,19 +5,21 @@ import java.util.UUID interface GuiRouter { sealed interface Route { + val inventoryOwner: OnlineKPlayer + /** - * @param player player who opened route + * @param inventoryOwner player who opened route * @param isExpired expired auctions * @param targetPlayerUUID only this player auctions will be shown */ class Slots( - val player: OnlineKPlayer, + override val inventoryOwner: OnlineKPlayer, val isExpired: Boolean, val targetPlayerUUID: UUID? ) : Route class Players( - val player: OnlineKPlayer, + override val inventoryOwner: OnlineKPlayer, val isExpired: Boolean ) : Route } diff --git a/modules/gui/api/src/main/kotlin/ru/astrainteractive/astramarket/market/di/MarketViewModule.kt b/modules/gui/api/src/main/kotlin/ru/astrainteractive/astramarket/market/di/MarketViewModule.kt index 67134f6..9f2222a 100644 --- a/modules/gui/api/src/main/kotlin/ru/astrainteractive/astramarket/market/di/MarketViewModule.kt +++ b/modules/gui/api/src/main/kotlin/ru/astrainteractive/astramarket/market/di/MarketViewModule.kt @@ -7,7 +7,7 @@ import ru.astrainteractive.astramarket.market.domain.di.MarketViewDomainModule import ru.astrainteractive.astramarket.market.domain.di.PlatformMarketDomainModule import ru.astrainteractive.astramarket.market.presentation.AuctionComponent import ru.astrainteractive.astramarket.market.presentation.DefaultAuctionComponent -import ru.astrainteractive.astramarket.market.presentation.di.AuctionComponentDependencies +import ru.astrainteractive.klibs.kstorage.api.getValue import java.util.UUID interface MarketViewModule { @@ -43,11 +43,13 @@ interface MarketViewModule { playerUUID = playerUUID, targetPlayerUUID = targetPlayerUUID, isExpired = isExpired, - dependencies = AuctionComponentDependencies.Default( - coreModule = coreModule, - apiMarketModule = apiMarketModule, - marketViewDomainModule = marketViewDomainModule, - ) + configKrate = coreModule.configKrate, + marketApi = apiMarketModule.marketApi, + sortAuctionsUseCase = marketViewDomainModule.platformMarketDomainModule.sortAuctionsUseCase, + removeAuctionUseCase = marketViewDomainModule.removeAuctionUseCase, + auctionBuyUseCase = marketViewDomainModule.auctionBuyUseCase, + expireAuctionUseCase = marketViewDomainModule.expireAuctionUseCase, + playerInteractionBridge = marketViewDomainModule.marketDataModule.playerInteractionBridge ) } } diff --git a/modules/gui/api/src/main/kotlin/ru/astrainteractive/astramarket/market/presentation/DefaultAuctionComponent.kt b/modules/gui/api/src/main/kotlin/ru/astrainteractive/astramarket/market/presentation/DefaultAuctionComponent.kt index 74c1454..e9ea744 100644 --- a/modules/gui/api/src/main/kotlin/ru/astrainteractive/astramarket/market/presentation/DefaultAuctionComponent.kt +++ b/modules/gui/api/src/main/kotlin/ru/astrainteractive/astramarket/market/presentation/DefaultAuctionComponent.kt @@ -4,13 +4,17 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.sync.Mutex import ru.astrainteractive.astralibs.coroutines.withTimings +import ru.astrainteractive.astramarket.api.market.MarketApi import ru.astrainteractive.astramarket.api.market.model.MarketSlot +import ru.astrainteractive.astramarket.core.PluginConfig +import ru.astrainteractive.astramarket.market.data.bridge.PlayerInteractionBridge import ru.astrainteractive.astramarket.market.domain.model.AuctionSort import ru.astrainteractive.astramarket.market.domain.usecase.AuctionBuyUseCase import ru.astrainteractive.astramarket.market.domain.usecase.ExpireAuctionUseCase import ru.astrainteractive.astramarket.market.domain.usecase.RemoveAuctionUseCase import ru.astrainteractive.astramarket.market.domain.usecase.SortAuctionsUseCase -import ru.astrainteractive.astramarket.market.presentation.di.AuctionComponentDependencies +import ru.astrainteractive.klibs.kstorage.api.CachedKrate +import ru.astrainteractive.klibs.kstorage.api.getValue import ru.astrainteractive.klibs.mikro.core.coroutines.CoroutineFeature import ru.astrainteractive.klibs.mikro.core.coroutines.launch import java.util.UUID @@ -20,10 +24,17 @@ internal class DefaultAuctionComponent( private val playerUUID: UUID, private val targetPlayerUUID: UUID?, isExpired: Boolean, - private val dependencies: AuctionComponentDependencies + private val configKrate: CachedKrate, + private val marketApi: MarketApi, + private val sortAuctionsUseCase: SortAuctionsUseCase, + private val removeAuctionUseCase: RemoveAuctionUseCase, + private val auctionBuyUseCase: AuctionBuyUseCase, + private val expireAuctionUseCase: ExpireAuctionUseCase, + private val playerInteractionBridge: PlayerInteractionBridge ) : AuctionComponent, - CoroutineFeature by CoroutineFeature.IO.withTimings(), - AuctionComponentDependencies by dependencies { + CoroutineFeature by CoroutineFeature.IO.withTimings() { + private val config by configKrate + private val mutex = Mutex() override val model = MutableStateFlow( diff --git a/modules/gui/api/src/main/kotlin/ru/astrainteractive/astramarket/market/presentation/di/AuctionComponentDependencies.kt b/modules/gui/api/src/main/kotlin/ru/astrainteractive/astramarket/market/presentation/di/AuctionComponentDependencies.kt deleted file mode 100644 index 07e869a..0000000 --- a/modules/gui/api/src/main/kotlin/ru/astrainteractive/astramarket/market/presentation/di/AuctionComponentDependencies.kt +++ /dev/null @@ -1,40 +0,0 @@ -package ru.astrainteractive.astramarket.market.presentation.di - -import ru.astrainteractive.astramarket.api.market.MarketApi -import ru.astrainteractive.astramarket.core.PluginConfig -import ru.astrainteractive.astramarket.core.di.CoreModule -import ru.astrainteractive.astramarket.di.ApiMarketModule -import ru.astrainteractive.astramarket.market.data.bridge.PlayerInteractionBridge -import ru.astrainteractive.astramarket.market.domain.di.MarketViewDomainModule -import ru.astrainteractive.astramarket.market.domain.usecase.AuctionBuyUseCase -import ru.astrainteractive.astramarket.market.domain.usecase.ExpireAuctionUseCase -import ru.astrainteractive.astramarket.market.domain.usecase.RemoveAuctionUseCase -import ru.astrainteractive.astramarket.market.domain.usecase.SortAuctionsUseCase -import ru.astrainteractive.klibs.kstorage.api.getValue -import ru.astrainteractive.klibs.mikro.core.dispatchers.KotlinDispatchers - -internal interface AuctionComponentDependencies { - val config: PluginConfig - val dispatchers: KotlinDispatchers - val marketApi: MarketApi - val auctionBuyUseCase: AuctionBuyUseCase - val expireAuctionUseCase: ExpireAuctionUseCase - val removeAuctionUseCase: RemoveAuctionUseCase - val playerInteractionBridge: PlayerInteractionBridge - val sortAuctionsUseCase: SortAuctionsUseCase - - class Default( - coreModule: CoreModule, - apiMarketModule: ApiMarketModule, - marketViewDomainModule: MarketViewDomainModule, - ) : AuctionComponentDependencies { - override val config: PluginConfig by coreModule.configKrate - override val dispatchers: KotlinDispatchers = coreModule.dispatchers - override val marketApi: MarketApi = apiMarketModule.marketApi - override val auctionBuyUseCase: AuctionBuyUseCase = marketViewDomainModule.auctionBuyUseCase - override val expireAuctionUseCase: ExpireAuctionUseCase = marketViewDomainModule.expireAuctionUseCase - override val removeAuctionUseCase: RemoveAuctionUseCase = marketViewDomainModule.removeAuctionUseCase - override val playerInteractionBridge = marketViewDomainModule.marketDataModule.playerInteractionBridge - override val sortAuctionsUseCase = marketViewDomainModule.platformMarketDomainModule.sortAuctionsUseCase - } -} diff --git a/modules/gui/api/src/main/kotlin/ru/astrainteractive/astramarket/players/presentation/PlayersMarketComponent.kt b/modules/gui/api/src/main/kotlin/ru/astrainteractive/astramarket/players/presentation/PlayersMarketComponent.kt index 7e4f04b..02ae2b0 100644 --- a/modules/gui/api/src/main/kotlin/ru/astrainteractive/astramarket/players/presentation/PlayersMarketComponent.kt +++ b/modules/gui/api/src/main/kotlin/ru/astrainteractive/astramarket/players/presentation/PlayersMarketComponent.kt @@ -1,10 +1,11 @@ package ru.astrainteractive.astramarket.players.presentation +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.StateFlow import ru.astrainteractive.astramarket.api.market.model.PlayerAndSlots import ru.astrainteractive.astramarket.players.model.PlayerSort -interface PlayersMarketComponent { +interface PlayersMarketComponent : CoroutineScope { val model: StateFlow fun toggleExpired() diff --git a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/AuctionSortButton.kt b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/AuctionSortButton.kt index 21a77df..4d7f03b 100644 --- a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/AuctionSortButton.kt +++ b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/AuctionSortButton.kt @@ -2,15 +2,14 @@ package ru.astrainteractive.astramarket.gui.button import ru.astrainteractive.astralibs.menu.clicker.Click import ru.astrainteractive.astralibs.menu.slot.InventorySlot -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.addLore -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setDisplayName -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setIndex -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setItemStack -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setOnClickListener +import ru.astrainteractive.astralibs.menu.slot.addLore +import ru.astrainteractive.astralibs.menu.slot.setDisplayName +import ru.astrainteractive.astralibs.menu.slot.setIndex +import ru.astrainteractive.astralibs.menu.slot.setItemStack +import ru.astrainteractive.astralibs.menu.slot.setOnClickListener import ru.astrainteractive.astralibs.string.plus import ru.astrainteractive.astramarket.gui.button.di.ButtonContext -import ru.astrainteractive.astramarket.gui.button.util.InventorySlotBuilderExt.addLore -import ru.astrainteractive.astramarket.gui.util.ItemStackExt.toItemStack +import ru.astrainteractive.astramarket.gui.util.toItemStack import ru.astrainteractive.astramarket.market.domain.model.AuctionSort internal fun ButtonContext.auctionSort( diff --git a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/BackButton.kt b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/BackButton.kt index 5f03604..762bf79 100644 --- a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/BackButton.kt +++ b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/BackButton.kt @@ -2,12 +2,12 @@ package ru.astrainteractive.astramarket.gui.button import ru.astrainteractive.astralibs.menu.clicker.Click import ru.astrainteractive.astralibs.menu.slot.InventorySlot -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.editMeta -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setIndex -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setItemStack -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setOnClickListener +import ru.astrainteractive.astralibs.menu.slot.editMeta +import ru.astrainteractive.astralibs.menu.slot.setIndex +import ru.astrainteractive.astralibs.menu.slot.setItemStack +import ru.astrainteractive.astralibs.menu.slot.setOnClickListener import ru.astrainteractive.astramarket.gui.button.di.ButtonContext -import ru.astrainteractive.astramarket.gui.util.ItemStackExt.toItemStack +import ru.astrainteractive.astramarket.gui.util.toItemStack internal fun ButtonContext.back( index: Int, diff --git a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/BorderButton.kt b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/BorderButton.kt index c7605b1..1e44ff2 100644 --- a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/BorderButton.kt +++ b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/BorderButton.kt @@ -1,11 +1,11 @@ package ru.astrainteractive.astramarket.gui.button import ru.astrainteractive.astralibs.menu.slot.InventorySlot -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setDisplayName -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setIndex -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setItemStack +import ru.astrainteractive.astralibs.menu.slot.setDisplayName +import ru.astrainteractive.astralibs.menu.slot.setIndex +import ru.astrainteractive.astralibs.menu.slot.setItemStack import ru.astrainteractive.astramarket.gui.button.di.ButtonContext -import ru.astrainteractive.astramarket.gui.util.ItemStackExt.toItemStack +import ru.astrainteractive.astramarket.gui.util.toItemStack internal fun ButtonContext.border(index: Int) = InventorySlot.Builder() .setIndex(index) diff --git a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/DisplayTypeButton.kt b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/DisplayTypeButton.kt index 4392be0..3848b2e 100644 --- a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/DisplayTypeButton.kt +++ b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/DisplayTypeButton.kt @@ -2,14 +2,14 @@ package ru.astrainteractive.astramarket.gui.button import ru.astrainteractive.astralibs.menu.clicker.Click import ru.astrainteractive.astralibs.menu.slot.InventorySlot -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setDisplayName -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setIndex -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setItemStack -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setOnClickListener +import ru.astrainteractive.astralibs.menu.slot.addLore +import ru.astrainteractive.astralibs.menu.slot.setDisplayName +import ru.astrainteractive.astralibs.menu.slot.setIndex +import ru.astrainteractive.astralibs.menu.slot.setItemStack +import ru.astrainteractive.astralibs.menu.slot.setOnClickListener import ru.astrainteractive.astralibs.string.plus import ru.astrainteractive.astramarket.gui.button.di.ButtonContext -import ru.astrainteractive.astramarket.gui.button.util.InventorySlotBuilderExt.addLore -import ru.astrainteractive.astramarket.gui.util.ItemStackExt.toItemStack +import ru.astrainteractive.astramarket.gui.util.toItemStack internal fun ButtonContext.slotsType( index: Int, diff --git a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/ExpiredSlotButton.kt b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/ExpiredSlotButton.kt index 4805026..11b130a 100644 --- a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/ExpiredSlotButton.kt +++ b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/ExpiredSlotButton.kt @@ -3,15 +3,14 @@ package ru.astrainteractive.astramarket.gui.button import org.bukkit.Bukkit import ru.astrainteractive.astralibs.menu.clicker.Click import ru.astrainteractive.astralibs.menu.slot.InventorySlot -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.addLore -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setIndex -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setItemStack -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setOnClickListener +import ru.astrainteractive.astralibs.menu.slot.addLore +import ru.astrainteractive.astralibs.menu.slot.setIndex +import ru.astrainteractive.astralibs.menu.slot.setItemStack +import ru.astrainteractive.astralibs.menu.slot.setOnClickListener import ru.astrainteractive.astramarket.api.market.model.MarketSlot import ru.astrainteractive.astramarket.gui.button.di.ButtonContext -import ru.astrainteractive.astramarket.gui.button.util.InventorySlotBuilderExt.addLore -import ru.astrainteractive.astramarket.gui.util.DurationExt.getTimeFormatted -import java.util.UUID +import ru.astrainteractive.astramarket.gui.util.getTimeFormatted +import java.util.* import kotlin.time.Duration.Companion.milliseconds @Suppress("LongParameterList") @@ -49,7 +48,11 @@ internal fun ButtonContext.expiredSlot( pluginTranslation.auction.auctionBy(ownerName).component } .addLore { - val time = auctionItem.time.milliseconds.getTimeFormatted(pluginTranslation.general.timeAgoFormat).raw + val time = auctionItem.time.milliseconds.getTimeFormatted( + pluginTranslation.general.timeAgoFormatDHM, + pluginTranslation.general.timeAgoFormatHM, + pluginTranslation.general.timeAgoFormatM + ).raw pluginTranslation.auction.auctionCreatedAgo(time).component } .addLore { diff --git a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/FilterExpiredButton.kt b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/FilterExpiredButton.kt index 5dbdbd0..0d72ac7 100644 --- a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/FilterExpiredButton.kt +++ b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/FilterExpiredButton.kt @@ -2,15 +2,15 @@ package ru.astrainteractive.astramarket.gui.button import ru.astrainteractive.astralibs.menu.clicker.Click import ru.astrainteractive.astralibs.menu.slot.InventorySlot -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setDisplayName -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setIndex -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setItemStack -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setOnClickListener +import ru.astrainteractive.astralibs.menu.slot.addLore +import ru.astrainteractive.astralibs.menu.slot.setDisplayName +import ru.astrainteractive.astralibs.menu.slot.setIndex +import ru.astrainteractive.astralibs.menu.slot.setItemStack +import ru.astrainteractive.astralibs.menu.slot.setOnClickListener import ru.astrainteractive.astralibs.string.StringDesc import ru.astrainteractive.astralibs.string.plus import ru.astrainteractive.astramarket.gui.button.di.ButtonContext -import ru.astrainteractive.astramarket.gui.button.util.InventorySlotBuilderExt.addLore -import ru.astrainteractive.astramarket.gui.util.ItemStackExt.toItemStack +import ru.astrainteractive.astramarket.gui.util.toItemStack internal fun ButtonContext.filterExpired( index: Int, diff --git a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/NextPageButton.kt b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/NextPageButton.kt index 24ae525..b6e15f9 100644 --- a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/NextPageButton.kt +++ b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/NextPageButton.kt @@ -2,12 +2,12 @@ package ru.astrainteractive.astramarket.gui.button import ru.astrainteractive.astralibs.menu.clicker.Click import ru.astrainteractive.astralibs.menu.slot.InventorySlot -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.editMeta -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setIndex -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setItemStack -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setOnClickListener +import ru.astrainteractive.astralibs.menu.slot.editMeta +import ru.astrainteractive.astralibs.menu.slot.setIndex +import ru.astrainteractive.astralibs.menu.slot.setItemStack +import ru.astrainteractive.astralibs.menu.slot.setOnClickListener import ru.astrainteractive.astramarket.gui.button.di.ButtonContext -import ru.astrainteractive.astramarket.gui.util.ItemStackExt.toItemStack +import ru.astrainteractive.astramarket.gui.util.toItemStack internal fun ButtonContext.nextPage( index: Int, diff --git a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/PlayerItemButton.kt b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/PlayerItemButton.kt index faba102..5e3df72 100644 --- a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/PlayerItemButton.kt +++ b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/PlayerItemButton.kt @@ -6,14 +6,14 @@ import org.bukkit.Material import org.bukkit.inventory.meta.SkullMeta import ru.astrainteractive.astralibs.menu.clicker.Click import ru.astrainteractive.astralibs.menu.slot.InventorySlot -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.editMeta -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setIndex -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setMaterial -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setOnClickListener +import ru.astrainteractive.astralibs.menu.slot.addLore +import ru.astrainteractive.astralibs.menu.slot.editMeta +import ru.astrainteractive.astralibs.menu.slot.setIndex +import ru.astrainteractive.astralibs.menu.slot.setMaterial +import ru.astrainteractive.astralibs.menu.slot.setOnClickListener import ru.astrainteractive.astramarket.api.market.model.PlayerAndSlots import ru.astrainteractive.astramarket.gui.button.di.ButtonContext -import ru.astrainteractive.astramarket.gui.button.util.InventorySlotBuilderExt.addLore -import ru.astrainteractive.astramarket.gui.util.DurationExt.getTimeFormatted +import ru.astrainteractive.astramarket.gui.util.getTimeFormatted import kotlin.time.Duration.Companion.milliseconds internal fun ButtonContext.playerItem( @@ -48,7 +48,11 @@ internal fun ButtonContext.playerItem( val time = playerAndSlots.slots .maxBy { it.time } .time.milliseconds - .getTimeFormatted(pluginTranslation.general.timeAgoFormat).raw + .getTimeFormatted( + pluginTranslation.general.timeAgoFormatDHM, + pluginTranslation.general.timeAgoFormatHM, + pluginTranslation.general.timeAgoFormatM + ).raw pluginTranslation.auction.auctionLast(time).component } .setOnClickListener(click) diff --git a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/PlayersSortButton.kt b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/PlayersSortButton.kt index e410593..55be3a0 100644 --- a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/PlayersSortButton.kt +++ b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/PlayersSortButton.kt @@ -2,14 +2,14 @@ package ru.astrainteractive.astramarket.gui.button import ru.astrainteractive.astralibs.menu.clicker.Click import ru.astrainteractive.astralibs.menu.slot.InventorySlot -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setDisplayName -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setIndex -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setItemStack -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setOnClickListener +import ru.astrainteractive.astralibs.menu.slot.addLore +import ru.astrainteractive.astralibs.menu.slot.setDisplayName +import ru.astrainteractive.astralibs.menu.slot.setIndex +import ru.astrainteractive.astralibs.menu.slot.setItemStack +import ru.astrainteractive.astralibs.menu.slot.setOnClickListener import ru.astrainteractive.astralibs.string.plus import ru.astrainteractive.astramarket.gui.button.di.ButtonContext -import ru.astrainteractive.astramarket.gui.button.util.InventorySlotBuilderExt.addLore -import ru.astrainteractive.astramarket.gui.util.ItemStackExt.toItemStack +import ru.astrainteractive.astramarket.gui.util.toItemStack import ru.astrainteractive.astramarket.players.model.PlayerSort internal fun ButtonContext.playersSort( diff --git a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/PrevPageButton.kt b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/PrevPageButton.kt index 377debf..c2a2378 100644 --- a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/PrevPageButton.kt +++ b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/PrevPageButton.kt @@ -2,12 +2,12 @@ package ru.astrainteractive.astramarket.gui.button import ru.astrainteractive.astralibs.menu.clicker.Click import ru.astrainteractive.astralibs.menu.slot.InventorySlot -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.editMeta -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setIndex -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setItemStack -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.setOnClickListener +import ru.astrainteractive.astralibs.menu.slot.editMeta +import ru.astrainteractive.astralibs.menu.slot.setIndex +import ru.astrainteractive.astralibs.menu.slot.setItemStack +import ru.astrainteractive.astralibs.menu.slot.setOnClickListener import ru.astrainteractive.astramarket.gui.button.di.ButtonContext -import ru.astrainteractive.astramarket.gui.util.ItemStackExt.toItemStack +import ru.astrainteractive.astramarket.gui.util.toItemStack internal fun ButtonContext.prevPage( index: Int, diff --git a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/util/InventorySlotBuilderExt.kt b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/util/InventorySlotBuilderExt.kt deleted file mode 100644 index f56d456..0000000 --- a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/button/util/InventorySlotBuilderExt.kt +++ /dev/null @@ -1,11 +0,0 @@ -package ru.astrainteractive.astramarket.gui.button.util - -import net.kyori.adventure.text.Component -import ru.astrainteractive.astralibs.menu.slot.InventorySlot -import ru.astrainteractive.astralibs.menu.slot.util.InventorySlotBuilderExt.addLore - -object InventorySlotBuilderExt { - fun InventorySlot.Builder.addLore(component: () -> Component): InventorySlot.Builder { - return addLore(component.invoke()) - } -} diff --git a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/di/AuctionGuiDependencies.kt b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/di/AuctionGuiDependencies.kt deleted file mode 100644 index f55ba39..0000000 --- a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/di/AuctionGuiDependencies.kt +++ /dev/null @@ -1,38 +0,0 @@ -package ru.astrainteractive.astramarket.gui.di - -import ru.astrainteractive.astralibs.kyori.KyoriComponentSerializer -import ru.astrainteractive.astramarket.core.PluginConfig -import ru.astrainteractive.astramarket.core.PluginTranslation -import ru.astrainteractive.astramarket.core.di.BukkitCoreModule -import ru.astrainteractive.astramarket.core.di.CoreModule -import ru.astrainteractive.astramarket.core.itemstack.ItemStackEncoder -import ru.astrainteractive.astramarket.gui.router.GuiRouter -import ru.astrainteractive.astramarket.market.domain.di.MarketViewDomainModule -import ru.astrainteractive.astramarket.market.domain.mapping.AuctionSortTranslationMapping -import ru.astrainteractive.klibs.kstorage.api.getValue -import ru.astrainteractive.klibs.mikro.core.dispatchers.KotlinDispatchers - -internal interface AuctionGuiDependencies { - val config: PluginConfig - val pluginTranslation: PluginTranslation - val dispatchers: KotlinDispatchers - val sortTranslationMapping: AuctionSortTranslationMapping - val itemStackEncoder: ItemStackEncoder - val kyoriComponentSerializer: KyoriComponentSerializer - val router: GuiRouter - - class Default( - coreModule: CoreModule, - marketViewDomainModule: MarketViewDomainModule, - bukkitCoreModule: BukkitCoreModule, - override val router: GuiRouter - ) : AuctionGuiDependencies { - override val config: PluginConfig by coreModule.configKrate - override val pluginTranslation: PluginTranslation by coreModule.pluginTranslationKrate - override val kyoriComponentSerializer by bukkitCoreModule.kyoriKrate - - override val dispatchers: KotlinDispatchers = coreModule.dispatchers - override val sortTranslationMapping = marketViewDomainModule.auctionSortTranslationMapping - override val itemStackEncoder = bukkitCoreModule.itemStackEncoder - } -} diff --git a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/invmap/AuctionInventoryMap.kt b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/invmap/AuctionInventoryMap.kt deleted file mode 100644 index c6a4606..0000000 --- a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/invmap/AuctionInventoryMap.kt +++ /dev/null @@ -1,34 +0,0 @@ -package ru.astrainteractive.astramarket.gui.invmap - -import ru.astrainteractive.astramarket.gui.invmap.AuctionInventoryMap.AuctionSlotKey - -internal interface AuctionInventoryMap : InventoryMap { - enum class AuctionSlotKey { - // Border - BO, - - // Prev - PR, - - // Next - NE, - - // Auction/Expired - AU, - - // Back - BA, - - // Filter - FI, - - // Auction item - AI, - - // Group by player or all slots - GR, - - // Empty - EM - } -} diff --git a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/invmap/DefaultAuctionInventoryMap.kt b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/invmap/DefaultAuctionInventoryMap.kt deleted file mode 100644 index 4be658b..0000000 --- a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/invmap/DefaultAuctionInventoryMap.kt +++ /dev/null @@ -1,15 +0,0 @@ -package ru.astrainteractive.astramarket.gui.invmap - -import ru.astrainteractive.astramarket.gui.invmap.AuctionInventoryMap.AuctionSlotKey - -internal object DefaultAuctionInventoryMap : AuctionInventoryMap { - @Suppress("MaxLineLength") - override val map: Array> = arrayOf( - arrayOf(AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI), - arrayOf(AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI), - arrayOf(AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI), - arrayOf(AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI), - arrayOf(AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI, AuctionSlotKey.AI), - arrayOf(AuctionSlotKey.PR, AuctionSlotKey.EM, AuctionSlotKey.GR, AuctionSlotKey.AU, AuctionSlotKey.BA, AuctionSlotKey.FI, AuctionSlotKey.EM, AuctionSlotKey.EM, AuctionSlotKey.NE), - ) -} diff --git a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/invmap/InventoryMap.kt b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/invmap/InventoryMap.kt deleted file mode 100644 index c041dfb..0000000 --- a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/invmap/InventoryMap.kt +++ /dev/null @@ -1,5 +0,0 @@ -package ru.astrainteractive.astramarket.gui.invmap - -internal interface InventoryMap { - val map: Array> -} diff --git a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/invmap/InventoryMapExt.kt b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/invmap/InventoryMapExt.kt deleted file mode 100644 index 01381d5..0000000 --- a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/invmap/InventoryMapExt.kt +++ /dev/null @@ -1,35 +0,0 @@ -package ru.astrainteractive.astramarket.gui.invmap - -import ru.astrainteractive.astralibs.menu.slot.InventorySlot - -internal object InventoryMapExt { - - private val InventoryMap<*>.flatMap get() = map.flatMap { it.map { guiKey -> guiKey } } - - fun InventoryMap.countKeys(key: T): Int { - return flatMap.count { it == key } - } - - fun InventoryMap.withKeySlot( - key: T, - transform: (index: Int) -> InventorySlot? - ): List { - return flatMap - .mapIndexed { i, k -> - if (key != k) { - null - } else { - transform.invoke(i) - } - } - .filterNotNull() - } - - fun InventoryMap.indexOf(key: T): Int { - val i = flatMap.indexOf(key) - if (i == -1) { - error("Could not find $key in inventory map") - } - return i - } -} diff --git a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/layout/AuctionSlotKey.kt b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/layout/AuctionSlotKey.kt new file mode 100644 index 0000000..d655b41 --- /dev/null +++ b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/layout/AuctionSlotKey.kt @@ -0,0 +1,13 @@ +package ru.astrainteractive.astramarket.gui.layout + +enum class AuctionSlotKey { + BORDER, + PREV_PAGE, + NEXT_PAGE, + FILTER_EXPIRED, + BACK, + SORT, + AUCTION_ITEM, + DISPLAY_TYPE, + EMPTY +} diff --git a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/layout/DefaultAuctionInventoryLayoutFactory.kt b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/layout/DefaultAuctionInventoryLayoutFactory.kt new file mode 100644 index 0000000..dc617e7 --- /dev/null +++ b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/layout/DefaultAuctionInventoryLayoutFactory.kt @@ -0,0 +1,57 @@ +package ru.astrainteractive.astramarket.gui.layout + +import ru.astrainteractive.astralibs.menu.layout.slotInventoryLayout + +@Suppress("MagicNumber") +internal object DefaultAuctionInventoryLayoutFactory { + + private fun createCompactLayout() = slotInventoryLayout { + repeat(5) { + row(9, AuctionSlotKey.AUCTION_ITEM) + } + row( + AuctionSlotKey.PREV_PAGE, + AuctionSlotKey.EMPTY, + AuctionSlotKey.DISPLAY_TYPE, + AuctionSlotKey.FILTER_EXPIRED, + AuctionSlotKey.BACK, + AuctionSlotKey.SORT, + AuctionSlotKey.EMPTY, + AuctionSlotKey.EMPTY, + AuctionSlotKey.NEXT_PAGE + ) + } + + private fun createBorderedLayout() = slotInventoryLayout { + row(9, AuctionSlotKey.BORDER) + repeat(4) { + row( + AuctionSlotKey.BORDER, + AuctionSlotKey.AUCTION_ITEM, + AuctionSlotKey.AUCTION_ITEM, + AuctionSlotKey.AUCTION_ITEM, + AuctionSlotKey.AUCTION_ITEM, + AuctionSlotKey.AUCTION_ITEM, + AuctionSlotKey.AUCTION_ITEM, + AuctionSlotKey.AUCTION_ITEM, + AuctionSlotKey.BORDER + ) + } + row( + AuctionSlotKey.BORDER, + AuctionSlotKey.PREV_PAGE, + AuctionSlotKey.DISPLAY_TYPE, + AuctionSlotKey.FILTER_EXPIRED, + AuctionSlotKey.BACK, + AuctionSlotKey.SORT, + AuctionSlotKey.EMPTY, + AuctionSlotKey.NEXT_PAGE, + AuctionSlotKey.BORDER + ) + } + + fun create(isCompact: Boolean) = when { + isCompact -> createCompactLayout() + else -> createBorderedLayout() + } +} diff --git a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/players/PlayersGui.kt b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/players/PlayersGui.kt index 03c39a2..b844d26 100644 --- a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/players/PlayersGui.kt +++ b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/players/PlayersGui.kt @@ -1,25 +1,28 @@ package ru.astrainteractive.astramarket.gui.players -import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.withContext import net.kyori.adventure.text.Component import org.bukkit.event.inventory.InventoryClickEvent +import org.bukkit.event.inventory.InventoryOpenEvent +import ru.astrainteractive.astralibs.coroutines.withTimings import ru.astrainteractive.astralibs.kyori.KyoriComponentSerializer -import ru.astrainteractive.astralibs.menu.holder.DefaultPlayerHolder -import ru.astrainteractive.astralibs.menu.inventory.PaginatedInventoryMenu +import ru.astrainteractive.astralibs.kyori.unwrap +import ru.astrainteractive.astralibs.menu.core.setInventorySlot +import ru.astrainteractive.astralibs.menu.inventory.api.InventoryMenu import ru.astrainteractive.astralibs.menu.inventory.model.InventorySize -import ru.astrainteractive.astralibs.menu.inventory.model.PageContext -import ru.astrainteractive.astralibs.menu.inventory.util.PageContextExt.indexOfSlot -import ru.astrainteractive.astralibs.menu.inventory.util.PageContextExt.isFirstPage -import ru.astrainteractive.astralibs.menu.inventory.util.PageContextExt.isLastPage -import ru.astrainteractive.astralibs.menu.inventory.util.PaginatedInventoryMenuExt.showNextPage -import ru.astrainteractive.astralibs.menu.inventory.util.PaginatedInventoryMenuExt.showPage -import ru.astrainteractive.astralibs.menu.inventory.util.PaginatedInventoryMenuExt.showPrevPage +import ru.astrainteractive.astralibs.menu.paginator.api.DefaultPaginator +import ru.astrainteractive.astralibs.menu.paginator.api.context +import ru.astrainteractive.astralibs.menu.paginator.api.openNextPage +import ru.astrainteractive.astralibs.menu.paginator.api.openPrevPage +import ru.astrainteractive.astralibs.menu.paginator.model.indexOfSlot +import ru.astrainteractive.astralibs.menu.paginator.model.isFirstPage +import ru.astrainteractive.astralibs.menu.paginator.model.isLastPage import ru.astrainteractive.astralibs.menu.slot.InventorySlot -import ru.astrainteractive.astralibs.server.player.BukkitOnlineKPlayer import ru.astrainteractive.astralibs.server.player.OnlineKPlayer -import ru.astrainteractive.astralibs.server.util.asOnlineMinecraftPlayer +import ru.astrainteractive.astramarket.core.PluginConfig +import ru.astrainteractive.astramarket.core.PluginTranslation import ru.astrainteractive.astramarket.gui.button.back import ru.astrainteractive.astramarket.gui.button.di.ButtonContext import ru.astrainteractive.astramarket.gui.button.filterExpired @@ -28,87 +31,93 @@ import ru.astrainteractive.astramarket.gui.button.playerItem import ru.astrainteractive.astramarket.gui.button.playersSort import ru.astrainteractive.astramarket.gui.button.prevPage import ru.astrainteractive.astramarket.gui.button.slotsType -import ru.astrainteractive.astramarket.gui.di.AuctionGuiDependencies -import ru.astrainteractive.astramarket.gui.invmap.AuctionInventoryMap -import ru.astrainteractive.astramarket.gui.invmap.AuctionInventoryMap.AuctionSlotKey -import ru.astrainteractive.astramarket.gui.invmap.DefaultAuctionInventoryMap -import ru.astrainteractive.astramarket.gui.invmap.InventoryMapExt.countKeys -import ru.astrainteractive.astramarket.gui.invmap.InventoryMapExt.indexOf -import ru.astrainteractive.astramarket.gui.invmap.InventoryMapExt.withKeySlot +import ru.astrainteractive.astramarket.gui.layout.AuctionSlotKey +import ru.astrainteractive.astramarket.gui.layout.DefaultAuctionInventoryLayoutFactory import ru.astrainteractive.astramarket.gui.router.GuiRouter -import ru.astrainteractive.astramarket.gui.util.ItemStackExt.playSound +import ru.astrainteractive.astramarket.gui.util.closeInventory +import ru.astrainteractive.astramarket.gui.util.playSound import ru.astrainteractive.astramarket.players.presentation.PlayersMarketComponent -import ru.astrainteractive.klibs.mikro.core.util.cast +import ru.astrainteractive.klibs.kstorage.api.CachedKrate +import ru.astrainteractive.klibs.kstorage.api.getValue +import ru.astrainteractive.klibs.mikro.core.coroutines.CoroutineFeature +import ru.astrainteractive.klibs.mikro.core.dispatchers.KotlinDispatchers internal class PlayersGui( + configKrate: CachedKrate, + kyoriKrate: CachedKrate, + translationKrate: CachedKrate, + private val inventoryOwner: OnlineKPlayer, + private val buttonContext: ButtonContext, + private val dispatchers: KotlinDispatchers, private val playersMarketComponent: PlayersMarketComponent, - player: OnlineKPlayer, - dependencies: AuctionGuiDependencies, - private val buttonContext: ButtonContext -) : PaginatedInventoryMenu(), - AuctionGuiDependencies by dependencies, - KyoriComponentSerializer by dependencies.kyoriComponentSerializer { - override val inventorySize: InventorySize = InventorySize.XL + private val router: GuiRouter, +) : InventoryMenu(), + KyoriComponentSerializer by kyoriKrate.unwrap() { + private val config by configKrate + private val translation by translationKrate - private val inventoryMap: AuctionInventoryMap - get() = DefaultAuctionInventoryMap + override val inventorySize: InventorySize = InventorySize.XL - override val title: Component = pluginTranslation.menu.market.component + override val childComponents = listOf(playersMarketComponent) + override val menuScope = CoroutineFeature + .Default(dispatchers.Main) + .withTimings() + private val inventoryMap by lazy { + DefaultAuctionInventoryLayoutFactory.create(config.auction.useCompactDesign) + } - override val playerHolder = DefaultPlayerHolder(player.cast().instance) + override val title: Component = translation.menu.market.component - override var pageContext: PageContext = PageContext( - page = 0, - maxItemsPerPage = inventoryMap.countKeys(AuctionSlotKey.AI), - maxItems = 0 + private val paginator = DefaultPaginator( + maxItemsPerPage = inventoryMap.count(AuctionSlotKey.AUCTION_ITEM) ) - override val prevPageButton: InventorySlot + private val prevPageButton: InventorySlot get() = buttonContext.prevPage( - index = inventoryMap.indexOf(AuctionSlotKey.PR), + index = inventoryMap.firstIndexOf(AuctionSlotKey.PREV_PAGE), click = { - playerHolder.player.playSound(config.sounds.open) - showPrevPage() + inventoryOwner.playSound(config.sounds.open) + paginator.openPrevPage() } ) - override val nextPageButton: InventorySlot + private val nextPageButton: InventorySlot get() = buttonContext.nextPage( - index = inventoryMap.indexOf(AuctionSlotKey.NE), + index = inventoryMap.firstIndexOf(AuctionSlotKey.NEXT_PAGE), click = { - playerHolder.player.playSound(config.sounds.open) - showNextPage() + inventoryOwner.playSound(config.sounds.open) + paginator.openNextPage() } ) private val sortButton: InventorySlot get() = buttonContext.playersSort( - index = inventoryMap.indexOf(AuctionSlotKey.FI), + index = inventoryMap.firstIndexOf(AuctionSlotKey.SORT), sortType = playersMarketComponent.model.value.sort, click = { - playerHolder.player.playSound(config.sounds.open) + inventoryOwner.playSound(config.sounds.open) playersMarketComponent.onSortButtonClicked(it.isRightClick) } ) - private val expiredButton: InventorySlot + private val filterExpiredButton: InventorySlot get() = buttonContext.filterExpired( - index = inventoryMap.indexOf(AuctionSlotKey.AU), + index = inventoryMap.firstIndexOf(AuctionSlotKey.FILTER_EXPIRED), isExpired = playersMarketComponent.model.value.isExpired, click = { - playerHolder.player.playSound(config.sounds.open) - showPage(0) + inventoryOwner.playSound(config.sounds.open) + paginator.openPage(0) playersMarketComponent.toggleExpired() } ) - private val allSlots: InventorySlot + private val displayTypeButton: InventorySlot get() = buttonContext.slotsType( - index = inventoryMap.indexOf(AuctionSlotKey.GR), + index = inventoryMap.firstIndexOf(AuctionSlotKey.DISPLAY_TYPE), isGroupedByPlayers = true, click = { val route = GuiRouter.Route.Slots( - player = playerHolder.player.asOnlineMinecraftPlayer(), + inventoryOwner = inventoryOwner, isExpired = playersMarketComponent.model.value.isExpired, targetPlayerUUID = null ) @@ -116,31 +125,31 @@ internal class PlayersGui( } ) - private val closeButton: InventorySlot + private val backButton: InventorySlot get() = buttonContext.back( - index = inventoryMap.indexOf(AuctionSlotKey.BA), - click = { playerHolder.player.closeInventory() } + index = inventoryMap.firstIndexOf(AuctionSlotKey.BACK), + click = { inventoryOwner.closeInventory() } ) private val slots: List get() { var itemIndex = 0 val isExpired = playersMarketComponent.model.value.isExpired - return inventoryMap.withKeySlot(AuctionSlotKey.AI) { slotIndex -> - val index = pageContext.indexOfSlot(itemIndex) + return inventoryMap.mapSlotsNotNull(AuctionSlotKey.AUCTION_ITEM) { slotIndex -> + val index = paginator.context.indexOfSlot(itemIndex) itemIndex++ val items = playersMarketComponent.model .value .playersAndSlots .filter { it.slots.any { slot -> slot.expired == isExpired } } - .getOrNull(index) ?: return@withKeySlot null + .getOrNull(index) ?: return@mapSlotsNotNull null buttonContext.playerItem( playerAndSlots = items, index = slotIndex, isExpired = playersMarketComponent.model.value.isExpired, click = { val route = GuiRouter.Route.Slots( - player = playerHolder.player.asOnlineMinecraftPlayer(), + inventoryOwner = inventoryOwner, isExpired = playersMarketComponent.model.value.isExpired, targetPlayerUUID = items.minecraftUUID ) @@ -150,35 +159,36 @@ internal class PlayersGui( } } - override fun onInventoryClicked(e: InventoryClickEvent) { - super.onInventoryClicked(e) - e.isCancelled = true - } - private fun updatePageContext(model: PlayersMarketComponent.Model) { - pageContext = pageContext.copy( - maxItems = model.playersAndSlots - .filter { it.slots.any { slot -> slot.expired == model.isExpired } } - .size - ) + paginator.update { paginatorContext -> + paginatorContext.copy( + maxItems = model.playersAndSlots + .filter { it.slots.any { slot -> slot.expired == model.isExpired } } + .size + ) + } } - override fun render() { - super.render() - if (!pageContext.isFirstPage) prevPageButton.setInventorySlot() - if (!pageContext.isLastPage) nextPageButton.setInventorySlot() - sortButton.setInventorySlot() - expiredButton.setInventorySlot() - closeButton.setInventorySlot() - allSlots.setInventorySlot() - slots.forEach { it.setInventorySlot() } + override fun onInventoryClickEvent(e: InventoryClickEvent) { + super.onInventoryClickEvent(e) + e.isCancelled = true } - override fun onInventoryCreated() { + override fun onInventoryOpenEvent(e: InventoryOpenEvent) { playersMarketComponent.model .onEach { model -> updatePageContext(model) } - .onEach { render() } - .flowOn(dispatchers.IO) + .onEach { withContext(dispatchers.Main) { render() } } .launchIn(menuScope) } + + override fun render() { + super.render() + if (!paginator.context.isFirstPage) setInventorySlot(prevPageButton) + if (!paginator.context.isLastPage) setInventorySlot(nextPageButton) + setInventorySlot(sortButton) + setInventorySlot(filterExpiredButton) + setInventorySlot(backButton) + setInventorySlot(displayTypeButton) + setInventorySlot(slots) + } } diff --git a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/router/GuiRouterImpl.kt b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/router/GuiRouterImpl.kt index 42f73db..61f8ef5 100644 --- a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/router/GuiRouterImpl.kt +++ b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/router/GuiRouterImpl.kt @@ -1,15 +1,15 @@ package ru.astrainteractive.astramarket.gui.router import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext +import ru.astrainteractive.astralibs.server.player.BukkitOnlineKPlayer import ru.astrainteractive.astramarket.core.di.BukkitCoreModule import ru.astrainteractive.astramarket.core.di.CoreModule import ru.astrainteractive.astramarket.gui.button.di.ButtonContext -import ru.astrainteractive.astramarket.gui.di.AuctionGuiDependencies import ru.astrainteractive.astramarket.gui.players.PlayersGui import ru.astrainteractive.astramarket.gui.slots.SlotsGui import ru.astrainteractive.astramarket.market.di.MarketViewModule import ru.astrainteractive.astramarket.players.di.PlayersMarketViewModule +import ru.astrainteractive.klibs.mikro.core.util.tryCast internal class GuiRouterImpl( private val coreModule: CoreModule, @@ -17,12 +17,7 @@ internal class GuiRouterImpl( private val bukkitCoreModule: BukkitCoreModule, private val playersMarketViewModule: PlayersMarketViewModule ) : GuiRouter { - private val dependencies = AuctionGuiDependencies.Default( - coreModule = coreModule, - marketViewDomainModule = marketViewModule.marketViewDomainModule, - bukkitCoreModule = bukkitCoreModule, - router = this@GuiRouterImpl - ) + private val buttonContext = ButtonContext.Default( coreModule = coreModule, marketViewDomainModule = marketViewModule.marketViewDomainModule, @@ -31,15 +26,19 @@ internal class GuiRouterImpl( ) override fun navigate(route: GuiRouter.Route) { - coreModule.ioScope.launch { - val gui = when (route) { + coreModule.ioScope.launch(coreModule.dispatchers.Main) { + val menu = when (route) { is GuiRouter.Route.Slots -> { SlotsGui( - player = route.player, - dependencies = dependencies, + inventoryOwner = route.inventoryOwner, + configKrate = coreModule.configKrate, + translationKrate = coreModule.pluginTranslationKrate, + dispatchers = coreModule.dispatchers, + router = this@GuiRouterImpl, + kyoriKrate = coreModule.kyoriKrate, buttonContext = buttonContext, auctionComponent = marketViewModule.createAuctionComponent( - playerUUID = route.player.uuid, + playerUUID = route.inventoryOwner.uuid, isExpired = route.isExpired, targetPlayerUUID = route.targetPlayerUUID ) @@ -48,8 +47,12 @@ internal class GuiRouterImpl( is GuiRouter.Route.Players -> { PlayersGui( - player = route.player, - dependencies = dependencies, + inventoryOwner = route.inventoryOwner, + configKrate = coreModule.configKrate, + translationKrate = coreModule.pluginTranslationKrate, + dispatchers = coreModule.dispatchers, + router = this@GuiRouterImpl, + kyoriKrate = coreModule.kyoriKrate, buttonContext = buttonContext, playersMarketComponent = playersMarketViewModule.createPlayersMarketComponent( isExpired = route.isExpired @@ -57,9 +60,9 @@ internal class GuiRouterImpl( ) } } - withContext(coreModule.dispatchers.Main) { - gui.open() - } + route.inventoryOwner.tryCast() + ?.instance + ?.openInventory(menu.inventory) } } } diff --git a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/slots/SlotsGui.kt b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/slots/SlotsGui.kt index b22f701..26727ef 100644 --- a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/slots/SlotsGui.kt +++ b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/slots/SlotsGui.kt @@ -1,31 +1,34 @@ package ru.astrainteractive.astramarket.gui.slots import kotlinx.coroutines.cancel -import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.withContext import net.kyori.adventure.text.Component import org.bukkit.Bukkit import org.bukkit.event.inventory.ClickType import org.bukkit.event.inventory.InventoryClickEvent import org.bukkit.event.inventory.InventoryCloseEvent +import org.bukkit.event.inventory.InventoryOpenEvent +import ru.astrainteractive.astralibs.coroutines.withTimings import ru.astrainteractive.astralibs.kyori.KyoriComponentSerializer -import ru.astrainteractive.astralibs.menu.holder.DefaultPlayerHolder -import ru.astrainteractive.astralibs.menu.inventory.PaginatedInventoryMenu +import ru.astrainteractive.astralibs.kyori.unwrap +import ru.astrainteractive.astralibs.menu.core.setInventorySlot +import ru.astrainteractive.astralibs.menu.inventory.api.InventoryMenu import ru.astrainteractive.astralibs.menu.inventory.model.InventorySize -import ru.astrainteractive.astralibs.menu.inventory.model.PageContext -import ru.astrainteractive.astralibs.menu.inventory.util.PageContextExt.indexOfSlot -import ru.astrainteractive.astralibs.menu.inventory.util.PageContextExt.isFirstPage -import ru.astrainteractive.astralibs.menu.inventory.util.PageContextExt.isLastPage -import ru.astrainteractive.astralibs.menu.inventory.util.PaginatedInventoryMenuExt.showNextPage -import ru.astrainteractive.astralibs.menu.inventory.util.PaginatedInventoryMenuExt.showPage -import ru.astrainteractive.astralibs.menu.inventory.util.PaginatedInventoryMenuExt.showPrevPage +import ru.astrainteractive.astralibs.menu.paginator.api.DefaultPaginator +import ru.astrainteractive.astralibs.menu.paginator.api.context +import ru.astrainteractive.astralibs.menu.paginator.api.openNextPage +import ru.astrainteractive.astralibs.menu.paginator.api.openPrevPage +import ru.astrainteractive.astralibs.menu.paginator.api.setMaxItems +import ru.astrainteractive.astralibs.menu.paginator.model.indexOfSlot +import ru.astrainteractive.astralibs.menu.paginator.model.isFirstPage +import ru.astrainteractive.astralibs.menu.paginator.model.isLastPage import ru.astrainteractive.astralibs.menu.slot.InventorySlot -import ru.astrainteractive.astralibs.server.permission.asKPermissible -import ru.astrainteractive.astralibs.server.player.BukkitOnlineKPlayer import ru.astrainteractive.astralibs.server.player.OnlineKPlayer -import ru.astrainteractive.astralibs.server.util.asOnlineMinecraftPlayer +import ru.astrainteractive.astramarket.core.PluginConfig import ru.astrainteractive.astramarket.core.PluginPermission +import ru.astrainteractive.astramarket.core.PluginTranslation import ru.astrainteractive.astramarket.gui.button.auctionSort import ru.astrainteractive.astramarket.gui.button.back import ru.astrainteractive.astramarket.gui.button.border @@ -35,27 +38,31 @@ import ru.astrainteractive.astramarket.gui.button.filterExpired import ru.astrainteractive.astramarket.gui.button.nextPage import ru.astrainteractive.astramarket.gui.button.prevPage import ru.astrainteractive.astramarket.gui.button.slotsType -import ru.astrainteractive.astramarket.gui.di.AuctionGuiDependencies -import ru.astrainteractive.astramarket.gui.invmap.AuctionInventoryMap -import ru.astrainteractive.astramarket.gui.invmap.AuctionInventoryMap.AuctionSlotKey -import ru.astrainteractive.astramarket.gui.invmap.DefaultAuctionInventoryMap -import ru.astrainteractive.astramarket.gui.invmap.InventoryMapExt.countKeys -import ru.astrainteractive.astramarket.gui.invmap.InventoryMapExt.indexOf -import ru.astrainteractive.astramarket.gui.invmap.InventoryMapExt.withKeySlot +import ru.astrainteractive.astramarket.gui.layout.AuctionSlotKey +import ru.astrainteractive.astramarket.gui.layout.DefaultAuctionInventoryLayoutFactory import ru.astrainteractive.astramarket.gui.router.GuiRouter -import ru.astrainteractive.astramarket.gui.util.ItemStackExt.playSound +import ru.astrainteractive.astramarket.gui.util.closeInventory +import ru.astrainteractive.astramarket.gui.util.playSound import ru.astrainteractive.astramarket.market.presentation.AuctionComponent -import ru.astrainteractive.klibs.mikro.core.util.cast +import ru.astrainteractive.klibs.kstorage.api.CachedKrate +import ru.astrainteractive.klibs.kstorage.api.getValue +import ru.astrainteractive.klibs.mikro.core.coroutines.CoroutineFeature +import ru.astrainteractive.klibs.mikro.core.dispatchers.KotlinDispatchers internal class SlotsGui( - player: OnlineKPlayer, - dependencies: AuctionGuiDependencies, + configKrate: CachedKrate, + translationKrate: CachedKrate, + kyoriKrate: CachedKrate, + private val inventoryOwner: OnlineKPlayer, + private val router: GuiRouter, private val buttonContext: ButtonContext, - private val auctionComponent: AuctionComponent -) : PaginatedInventoryMenu(), - AuctionGuiDependencies by dependencies, - KyoriComponentSerializer by dependencies.kyoriComponentSerializer { - override val playerHolder = DefaultPlayerHolder(player.cast().instance) + private val auctionComponent: AuctionComponent, + private val dispatchers: KotlinDispatchers, +) : InventoryMenu(), + KyoriComponentSerializer by kyoriKrate.unwrap() { + private val config by configKrate + private val translation by translationKrate + override val title: Component = let { val playerNameComponent = auctionComponent.model.value .targetPlayerUUID @@ -63,134 +70,135 @@ internal class SlotsGui( ?.name ?.let { name -> Component.text(": $name") } ?: Component.empty() - pluginTranslation.menu.market.component.append(playerNameComponent) + translation.menu.market.component.append(playerNameComponent) } + override val childComponents = listOf(auctionComponent) + + override val menuScope = CoroutineFeature + .Default(dispatchers.Main) + .withTimings() + override val inventorySize: InventorySize = InventorySize.XL - private val inventoryMap: AuctionInventoryMap - get() = if (config.auction.useCompactDesign) DefaultAuctionInventoryMap else DefaultAuctionInventoryMap + private val inventoryMap by lazy { + DefaultAuctionInventoryLayoutFactory.create(config.auction.useCompactDesign) + } - override var pageContext: PageContext = PageContext( - page = 0, - maxItemsPerPage = inventoryMap.countKeys(AuctionSlotKey.AI), - maxItems = 0 + private val paginator = DefaultPaginator( + maxItemsPerPage = inventoryMap.count(AuctionSlotKey.AUCTION_ITEM) ) private val borderButtons: List - get() = inventoryMap.withKeySlot( - key = AuctionSlotKey.BO, + get() = inventoryMap.mapSlotsNotNull( + key = AuctionSlotKey.BORDER, transform = buttonContext::border ) - override val nextPageButton: InventorySlot + private val nextPageButton: InventorySlot get() = buttonContext.nextPage( - index = inventoryMap.indexOf(AuctionSlotKey.NE), + index = inventoryMap.firstIndexOf(AuctionSlotKey.NEXT_PAGE), click = { onNextPageClicked() } ) - override val prevPageButton: InventorySlot + private val prevPageButton: InventorySlot get() = buttonContext.prevPage( - index = inventoryMap.indexOf(AuctionSlotKey.PR), + index = inventoryMap.firstIndexOf(AuctionSlotKey.PREV_PAGE), click = { onPrevPageClicked() } ) private val sortButton: InventorySlot get() = buttonContext.auctionSort( - index = inventoryMap.indexOf(AuctionSlotKey.FI), + index = inventoryMap.firstIndexOf(AuctionSlotKey.SORT), sortType = auctionComponent.model.value.sortType, click = { - showPage(0) + paginator.openPage(0) onSortButtonClicked(it.isRightClick) } ) - private val expiredSlotsButton: InventorySlot + private val filterExpiredButton: InventorySlot get() = buttonContext.filterExpired( - index = inventoryMap.indexOf(AuctionSlotKey.AU), + index = inventoryMap.firstIndexOf(AuctionSlotKey.FILTER_EXPIRED), isExpired = auctionComponent.model.value.isExpired, click = { - playerHolder.player.playSound(config.sounds.open) - showPage(0) + this@SlotsGui.inventoryOwner.playSound(config.sounds.open) + paginator.openPage(0) auctionComponent.toggleExpired() } ) - private val openPlayersButton: InventorySlot - get() = buttonContext.back( - index = inventoryMap.indexOf(AuctionSlotKey.BA), - click = { - val route = GuiRouter.Route.Players( - player = playerHolder.player.asOnlineMinecraftPlayer(), - isExpired = auctionComponent.model.value.isExpired - ) - router.navigate(route) - } - ) - - private val playerSlots: InventorySlot + private val displayTypeButton: InventorySlot get() = buttonContext.slotsType( - index = inventoryMap.indexOf(AuctionSlotKey.GR), + index = inventoryMap.firstIndexOf(AuctionSlotKey.DISPLAY_TYPE), isGroupedByPlayers = false, click = { val route = GuiRouter.Route.Players( - player = playerHolder.player.asOnlineMinecraftPlayer(), + inventoryOwner = this@SlotsGui.inventoryOwner, isExpired = auctionComponent.model.value.isExpired ) router.navigate(route) } ) - private val closeButton: InventorySlot + private val backButton: InventorySlot get() = buttonContext.back( - index = inventoryMap.indexOf(AuctionSlotKey.BA), - click = { playerHolder.player.closeInventory() } + index = inventoryMap.firstIndexOf(AuctionSlotKey.BACK), + click = { this@SlotsGui.inventoryOwner.closeInventory() } ) - private fun onNextPageClicked() { - playerHolder.player.playSound(config.sounds.open) - showNextPage() - } - - private fun onPrevPageClicked() { - playerHolder.player.playSound(config.sounds.open) - showPrevPage() - } - - private fun onSortButtonClicked(isRightClick: Boolean) { - playerHolder.player.playSound(config.sounds.open) - auctionComponent.onSortButtonClicked(isRightClick) - sortButton.setInventorySlot() - } - private val itemSlots: List get() { var itemIndex = 0 - return inventoryMap.withKeySlot(AuctionSlotKey.AI) { slotIndex -> - val index = pageContext.indexOfSlot(itemIndex) + return inventoryMap.mapSlotsNotNull(AuctionSlotKey.AUCTION_ITEM) { slotIndex -> + val index = paginator.context.indexOfSlot(itemIndex) itemIndex++ val auctionItem = auctionComponent.model .value .items .getOrNull(index) - ?: return@withKeySlot null - val permissible = playerHolder.player.asKPermissible() + ?: return@mapSlotsNotNull null + val permissible = this@SlotsGui.inventoryOwner buttonContext.expiredSlot( auctionItem = auctionItem, index = slotIndex, click = { onAuctionItemClicked(index, it.click) }, - isOwner = auctionItem.minecraftUuid == playerHolder.player.uniqueId.toString(), + isOwner = auctionItem.minecraftUuid == this@SlotsGui.inventoryOwner.uuid.toString(), hasExpirePermission = permissible.hasPermission(PluginPermission.Expire), hasRemovePermission = permissible.hasPermission(PluginPermission.RemoveSlot) ) } } - override fun onInventoryClosed(it: InventoryCloseEvent) { - super.onInventoryClosed(it) - playerHolder.player.playSound(config.sounds.close) + private fun onNextPageClicked() { + this@SlotsGui.inventoryOwner.playSound(config.sounds.open) + paginator.openNextPage() + } + + private fun onPrevPageClicked() { + this@SlotsGui.inventoryOwner.playSound(config.sounds.open) + paginator.openPrevPage() + } + + private fun onSortButtonClicked(isRightClick: Boolean) { + this@SlotsGui.inventoryOwner.playSound(config.sounds.open) + auctionComponent.onSortButtonClicked(isRightClick) + setInventorySlot(sortButton) + } + + override fun onInventoryCloseEvent(e: InventoryCloseEvent) { + super.onInventoryCloseEvent(e) + this@SlotsGui.inventoryOwner.playSound(config.sounds.close) auctionComponent.cancel() } + override fun onInventoryOpenEvent(e: InventoryOpenEvent) { + this@SlotsGui.inventoryOwner.playSound(config.sounds.open) + auctionComponent.model + .onEach { paginator.setMaxItems(auctionComponent.model.value.items.size) } + .onEach { withContext(dispatchers.Main) { render() } } + .launchIn(menuScope) + } + private fun onAuctionItemClicked(i: Int, clickType: ClickType) { val sharedClickType = when (clickType) { ClickType.LEFT, @@ -205,38 +213,20 @@ internal class SlotsGui( auctionComponent.onAuctionItemClicked(i, sharedClickType) } - override fun onInventoryClicked(e: InventoryClickEvent) { - super.onInventoryClicked(e) + override fun onInventoryClickEvent(e: InventoryClickEvent) { + super.onInventoryClickEvent(e) e.isCancelled = true } override fun render() { super.render() - expiredSlotsButton.setInventorySlot() - if (!pageContext.isFirstPage) prevPageButton.setInventorySlot() - if (!pageContext.isLastPage) nextPageButton.setInventorySlot() - if (auctionComponent.model.value.targetPlayerUUID != null) { - openPlayersButton.setInventorySlot() - } else { - closeButton.setInventorySlot() - playerSlots.setInventorySlot() - } - borderButtons.forEach { it.setInventorySlot() } - sortButton.setInventorySlot() - itemSlots.forEach { it.setInventorySlot() } - } - - private val drawDispatcher = dispatchers.IO.limitedParallelism(1) - override fun onInventoryCreated() { - playerHolder.player.playSound(config.sounds.open) - auctionComponent.model - .onEach { - pageContext = pageContext.copy( - maxItems = auctionComponent.model.value.items.size - ) - render() - } - .flowOn(drawDispatcher) - .launchIn(menuScope) + if (!paginator.context.isFirstPage) setInventorySlot(prevPageButton) + if (!paginator.context.isLastPage) setInventorySlot(nextPageButton) + setInventorySlot(filterExpiredButton) + setInventorySlot(displayTypeButton) + setInventorySlot(backButton) + setInventorySlot(borderButtons) + setInventorySlot(sortButton) + setInventorySlot(itemSlots) } } diff --git a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/util/DurationExt.kt b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/util/DurationExt.kt index 40bc275..c83096f 100644 --- a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/util/DurationExt.kt +++ b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/util/DurationExt.kt @@ -5,17 +5,24 @@ import ru.astrainteractive.astralibs.string.replace import java.util.concurrent.TimeUnit import kotlin.time.Duration -internal object DurationExt { - @Suppress("MagicNumber") - fun Duration.getTimeFormatted(format: StringDesc): StringDesc { - val time = System.currentTimeMillis().minus(inWholeMilliseconds) - val unit = TimeUnit.MILLISECONDS - val days = unit.toDays(time) - val hours = unit.toHours(time) - days * 24 - val minutes = unit.toMinutes(time) - unit.toHours(time) * 60 - return format - .replace("%days%", days.toString()) - .replace("%hours%", hours.toString()) - .replace("%minutes%", minutes.toString()) +@Suppress("MagicNumber") +fun Duration.getTimeFormatted( + formatDHM: StringDesc, + formatHM: StringDesc, + formatM: StringDesc +): StringDesc { + val time = System.currentTimeMillis().minus(inWholeMilliseconds) + val unit = TimeUnit.MILLISECONDS + val days = unit.toDays(time) + val hours = unit.toHours(time) - days * 24 + val minutes = unit.toMinutes(time) - unit.toHours(time) * 60 + val format = when { + days == 0L && hours == 0L -> formatM + days == 0L -> formatHM + else -> formatDHM } + return format + .replace("%days%", days.toString()) + .replace("%hours%", hours.toString()) + .replace("%minutes%", minutes.toString()) } diff --git a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/util/ItemStackExt.kt b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/util/ItemStackExt.kt index 99ab7ae..424c18f 100644 --- a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/util/ItemStackExt.kt +++ b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/util/ItemStackExt.kt @@ -3,19 +3,14 @@ package ru.astrainteractive.astramarket.gui.util import org.bukkit.Material -import org.bukkit.entity.Player import org.bukkit.inventory.ItemStack import ru.astrainteractive.astramarket.core.PluginConfig -internal object ItemStackExt { - fun Player.playSound(sound: String) { - playSound(location, sound, 1f, 1f) +fun PluginConfig.Button.toItemStack(): ItemStack { + val type = Material.getMaterial(material.uppercase()) ?: Material.PAPER + return ItemStack(type).apply { + val meta = itemMeta ?: error("ItemStack with material $type didn't have itemMeta") + meta.setCustomModelData(customModelData) + itemMeta = meta } - - fun PluginConfig.Button.toItemStack() = - ItemStack(Material.getMaterial(material.uppercase()) ?: Material.PAPER).apply { - val meta = itemMeta!! - meta.setCustomModelData(customModelData) - itemMeta = meta - } } diff --git a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/util/OnlineKPlayerExt.kt b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/util/OnlineKPlayerExt.kt new file mode 100644 index 0000000..4ecb6fc --- /dev/null +++ b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/util/OnlineKPlayerExt.kt @@ -0,0 +1,13 @@ +package ru.astrainteractive.astramarket.gui.util + +import ru.astrainteractive.astralibs.server.player.BukkitOnlineKPlayer +import ru.astrainteractive.astralibs.server.player.OnlineKPlayer +import ru.astrainteractive.klibs.mikro.core.util.tryCast + +internal fun OnlineKPlayer.playSound(sound: String) { + this.tryCast()?.instance?.playSound(sound) +} + +internal fun OnlineKPlayer.closeInventory() { + this.tryCast()?.instance?.closeInventory() +} diff --git a/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/util/PlayerExt.kt b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/util/PlayerExt.kt new file mode 100644 index 0000000..5347b53 --- /dev/null +++ b/modules/gui/bukkit/src/main/kotlin/ru/astrainteractive/astramarket/gui/util/PlayerExt.kt @@ -0,0 +1,7 @@ +package ru.astrainteractive.astramarket.gui.util + +import org.bukkit.entity.Player + +fun Player.playSound(sound: String) { + playSound(location, sound, 1f, 1f) +}