Skip to content

Commit

Permalink
#44 fix money drop abuse by adding database (#77)
Browse files Browse the repository at this point in the history
* #44 fix money drop abuse by adding database

* #44 add type-from constraint
  • Loading branch information
makeevrserg committed Aug 29, 2024
1 parent 958b608 commit 01fbe62
Show file tree
Hide file tree
Showing 14 changed files with 224 additions and 31 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ makeevrserg.java.ktarget=21
# Project
makeevrserg.project.name=AspeKt
makeevrserg.project.group=ru.astrainteractive.aspekt
makeevrserg.project.version.string=2.25.2
makeevrserg.project.version.string=2.26.0
makeevrserg.project.description=Essentials plugin for EmpireProjekt
makeevrserg.project.developers=makeevrserg|Makeev Roman|[email protected]
makeevrserg.project.url=https://empireprojekt.ru
Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ minecraft-astralibs-command-bukkit = { module = "ru.astrainteractive.astralibs:c
[bundles]
exposed = ["exposed-java-time", "exposed-jdbc", "exposed-dao", "exposed-core"]
minecraft-bukkit = ["minecraft-paper-api", "minecraft-spigot-api", "minecraft-spigot-core", "minecraft-vaultapi", "minecraft-papi"]
testing-kotlin = ["kotlin-coroutines-core", "kotlin-coroutines-coreJvm", "driver-jdbc", "driver-mysql", "kotlin-serialization", "kotlin-serializationJson"]
testing-kotlin = ["kotlin-coroutines-core", "kotlin-coroutines-coreJvm", "kotlin-coroutines-test", "driver-jdbc", "driver-mysql", "kotlin-serialization", "kotlin-serializationJson"]
kotlin = ["kotlin-coroutines-core", "kotlin-coroutines-coreJvm", "kotlin-serialization", "kotlin-serializationJson", "kotlin-serializationKaml", "kotlin-gradle"]


Expand Down
2 changes: 2 additions & 0 deletions modules/moneydrop/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ plugins {
dependencies {
// Kotlin
implementation(libs.bundles.kotlin)
implementation(libs.bundles.exposed)
// Spigot dependencies
compileOnly(libs.minecraft.paper.api)
implementation(libs.minecraft.bstats)
// AstraLibs
implementation(libs.minecraft.astralibs.core)
implementation(libs.minecraft.astralibs.orm)
implementation(libs.klibs.kdi)
implementation(libs.klibs.mikro.core)
implementation(libs.minecraft.astralibs.menu.bukkit)
implementation(libs.minecraft.astralibs.core.bukkit)
implementation(libs.minecraft.vaultapi)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,46 +1,51 @@
package ru.astrainteractive.aspekt.module.moneydrop

import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.bukkit.Location
import org.bukkit.Material
import org.bukkit.inventory.ItemStack
import org.jetbrains.kotlin.com.google.common.cache.Cache
import org.jetbrains.kotlin.com.google.common.cache.CacheBuilder
import ru.astrainteractive.aspekt.module.moneydrop.database.dao.MoneyDropDao
import ru.astrainteractive.aspekt.module.moneydrop.database.model.MoneyDropLocation
import ru.astrainteractive.aspekt.plugin.PluginConfiguration
import ru.astrainteractive.aspekt.plugin.PluginTranslation
import ru.astrainteractive.astralibs.async.AsyncComponent
import ru.astrainteractive.astralibs.kyori.KyoriComponentSerializer
import ru.astrainteractive.astralibs.persistence.Persistence.getPersistentData
import ru.astrainteractive.astralibs.persistence.Persistence.hasPersistentData
import ru.astrainteractive.astralibs.persistence.Persistence.setPersistentDataType
import ru.astrainteractive.klibs.kdi.Dependency
import ru.astrainteractive.klibs.kdi.getValue
import java.util.concurrent.TimeUnit
import ru.astrainteractive.klibs.mikro.core.dispatchers.KotlinDispatchers
import kotlin.random.Random

class MoneyDropController(
internal class MoneyDropController(
pluginConfigurationDependency: Dependency<PluginConfiguration>,
translationDependency: Dependency<PluginTranslation>,
kyoriComponentSerializerDependency: Dependency<KyoriComponentSerializer>
) {
kyoriComponentSerializerDependency: Dependency<KyoriComponentSerializer>,
private val dao: MoneyDropDao,
private val dispatchers: KotlinDispatchers
) : AsyncComponent() {
private val pluginConfiguration by pluginConfigurationDependency
private val translation by translationDependency
private val kyoriComponentSerializer by kyoriComponentSerializerDependency

private val dropCache: Cache<String, Unit> = CacheBuilder
.newBuilder()
.maximumSize(64)
.expireAfterWrite(30, TimeUnit.SECONDS)
.build()

private fun Location.toKeyLocation() = "${x.toInt()}${y.toInt()}${z.toInt()}"
private fun Location.toMoneyDropLocation(additionalConstraint: String?) = MoneyDropLocation(
x = this.x.toInt(),
y = this.y.toInt(),
z = this.z.toInt(),
world = this.world.name,
additionalConstraint = additionalConstraint
)

private fun checkForChance(entry: PluginConfiguration.MoneyDropEntry): Boolean {
val chance = entry.chance
return chance > Random.nextDouble(0.0, 100.0)
}

private fun drop(location: Location, entry: PluginConfiguration.MoneyDropEntry) {
if (dropCache.getIfPresent(location.toKeyLocation()) != null) return
dropCache.put(location.toKeyLocation(), Unit)
private suspend fun drop(location: Location, entry: PluginConfiguration.MoneyDropEntry) {
if (dao.isLocationExists(location.toMoneyDropLocation(entry.from))) return
rememberLocation(location, entry.from)

val amount = Random.nextDouble(entry.min, entry.max)
val material = Material.RAW_GOLD
Expand All @@ -51,7 +56,7 @@ class MoneyDropController(
it.setPersistentDataType(MoneyDropFlag.Flag, true)
it.setPersistentDataType(MoneyDropFlag.Amount, amount)
}
val item = location.world.dropItemNaturally(location, itemStack)
val item = withContext(dispatchers.Main) { location.world.dropItemNaturally(location, itemStack) }
item.customName(name)
item.isCustomNameVisible = true
}
Expand All @@ -65,14 +70,14 @@ class MoneyDropController(
return itemStack.itemMeta.getPersistentData(MoneyDropFlag.Amount)
}

fun tryDrop(location: Location, from: String) {
fun tryDrop(location: Location, from: String) = launch {
pluginConfiguration.moneyDrop.values
.filter { it.from == from }
.filter(::checkForChance)
.forEach { entry -> drop(location, entry) }
}

fun blockPlaced(location: Location) {
dropCache.put(location.toKeyLocation(), Unit)
fun rememberLocation(location: Location, additionalConstraint: String? = null) {
launch { dao.addLocation(location.toMoneyDropLocation(additionalConstraint)) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import org.bukkit.event.inventory.InventoryPickupItemEvent
import ru.astrainteractive.aspekt.module.moneydrop.di.MoneyDropDependencies
import ru.astrainteractive.astralibs.event.DSLEvent

class MoneyDropEvent(
internal class MoneyDropEvent(
dependencies: MoneyDropDependencies
) : MoneyDropDependencies by dependencies {

Expand Down Expand Up @@ -55,6 +55,6 @@ class MoneyDropEvent(
}

val blockPlaceEvent = DSLEvent<BlockPlaceEvent>(eventListener, plugin) { e ->
moneyDropController.blockPlaced(e.blockPlaced.location)
moneyDropController.rememberLocation(e.blockPlaced.location)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package ru.astrainteractive.aspekt.module.moneydrop
import org.bukkit.persistence.PersistentDataType
import ru.astrainteractive.astralibs.persistence.BukkitConstant

object MoneyDropFlag {
internal object MoneyDropFlag {
/**
* Amount of money will be added
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package ru.astrainteractive.aspekt.module.moneydrop.database.dao

import ru.astrainteractive.aspekt.module.moneydrop.database.model.MoneyDropLocation

internal interface MoneyDropDao {
suspend fun addLocation(location: MoneyDropLocation)
suspend fun isLocationExists(location: MoneyDropLocation): Boolean
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package ru.astrainteractive.aspekt.module.moneydrop.database.dao.impl

import kotlinx.coroutines.withContext
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction
import ru.astrainteractive.aspekt.module.moneydrop.database.dao.MoneyDropDao
import ru.astrainteractive.aspekt.module.moneydrop.database.model.MoneyDropLocation
import ru.astrainteractive.aspekt.module.moneydrop.database.table.MoneyDropLocationTable
import ru.astrainteractive.astralibs.logging.JUtiltLogger
import ru.astrainteractive.astralibs.logging.Logger
import kotlin.coroutines.CoroutineContext

internal class MoneyDropDaoImpl(
private val database: Database,
private val ioDispatcher: CoroutineContext
) : MoneyDropDao, Logger by JUtiltLogger("MoneyDropDao") {
override suspend fun addLocation(location: MoneyDropLocation) {
if (isLocationExists(location)) return
runCatching {
withContext(ioDispatcher) {
transaction(database) {
MoneyDropLocationTable.insert {
it[MoneyDropLocationTable.x] = location.x
it[MoneyDropLocationTable.y] = location.y
it[MoneyDropLocationTable.z] = location.z
it[MoneyDropLocationTable.world] = location.world
it[MoneyDropLocationTable.additionalConstraint] = location.additionalConstraint
}
}
}
}.onFailure { error { "#addLocation -> ${it.message}" } }
}

override suspend fun isLocationExists(location: MoneyDropLocation): Boolean {
return runCatching {
withContext(ioDispatcher) {
transaction(database) {
MoneyDropLocationTable
.selectAll()
.where {
MoneyDropLocationTable.x.eq(location.x)
.and(MoneyDropLocationTable.y.eq(location.y))
.and(MoneyDropLocationTable.z.eq(location.z))
.and(MoneyDropLocationTable.world.eq(location.world))
.and(MoneyDropLocationTable.additionalConstraint.eq(location.additionalConstraint))
}.count() > 0
}
}
}
.onFailure { error { "#isLocationExists -> ${it.message}" } }
.getOrElse { false }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package ru.astrainteractive.aspekt.module.moneydrop.database.di

import kotlinx.coroutines.runBlocking
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.SchemaUtils
import org.jetbrains.exposed.sql.Slf4jSqlDebugLogger
import org.jetbrains.exposed.sql.addLogger
import org.jetbrains.exposed.sql.transactions.TransactionManager
import org.jetbrains.exposed.sql.transactions.transaction
import ru.astrainteractive.aspekt.module.moneydrop.database.dao.MoneyDropDao
import ru.astrainteractive.aspekt.module.moneydrop.database.dao.impl.MoneyDropDaoImpl
import ru.astrainteractive.aspekt.module.moneydrop.database.table.MoneyDropLocationTable
import java.io.File
import kotlin.coroutines.CoroutineContext

internal interface MoneyDropDaoModule {
val dao: MoneyDropDao

class Default(
dataFolder: File,
ioDispatcher: CoroutineContext
) : MoneyDropDaoModule {
private val database by lazy {
val path = dataFolder.resolve("moneydrops.db").absolutePath
val database = Database.connect(
url = "jdbc:sqlite:$path",
driver = "org.sqlite.JDBC"
)
TransactionManager.manager.defaultIsolationLevel = java.sql.Connection.TRANSACTION_SERIALIZABLE
runBlocking {
transaction(database) {
addLogger(Slf4jSqlDebugLogger)
SchemaUtils.create(
MoneyDropLocationTable,
)
}
}
database
}
override val dao: MoneyDropDao = MoneyDropDaoImpl(
database = database,
ioDispatcher = ioDispatcher
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package ru.astrainteractive.aspekt.module.moneydrop.database.model

import kotlinx.serialization.Serializable

@Serializable
internal data class MoneyDropLocation(
val x: Int,
val y: Int,
val z: Int,
val world: String,
val additionalConstraint: String? = null
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package ru.astrainteractive.aspekt.module.moneydrop.database.table

import org.jetbrains.exposed.dao.id.LongIdTable

internal object MoneyDropLocationTable : LongIdTable(name = "MONEY_DROP_LOCATION") {
val x = integer("x")
val y = integer("y")
val z = integer("z")
val world = text("world")
val additionalConstraint = text("additional_constraint").nullable()
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,15 @@ package ru.astrainteractive.aspekt.module.moneydrop.di
import org.bukkit.plugin.java.JavaPlugin
import ru.astrainteractive.aspekt.di.CoreModule
import ru.astrainteractive.aspekt.module.moneydrop.MoneyDropController
import ru.astrainteractive.aspekt.plugin.PluginConfiguration
import ru.astrainteractive.aspekt.plugin.PluginTranslation
import ru.astrainteractive.astralibs.economy.EconomyProvider
import ru.astrainteractive.astralibs.event.EventListener
import ru.astrainteractive.astralibs.kyori.KyoriComponentSerializer
import ru.astrainteractive.klibs.kdi.getValue

interface MoneyDropDependencies {
internal interface MoneyDropDependencies {
val eventListener: EventListener
val plugin: JavaPlugin
val configuration: PluginConfiguration
val moneyDropController: MoneyDropController
val economyProvider: EconomyProvider?
val kyoriComponentSerializer: KyoriComponentSerializer
Expand All @@ -25,7 +23,6 @@ interface MoneyDropDependencies {
) : MoneyDropDependencies {
override val eventListener: EventListener = coreModule.eventListener
override val plugin: JavaPlugin by coreModule.plugin
override val configuration: PluginConfiguration by coreModule.pluginConfig
override val economyProvider: EconomyProvider? by coreModule.economyProvider
override val kyoriComponentSerializer by coreModule.kyoriComponentSerializer
override val translation: PluginTranslation by coreModule.translation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,33 @@ package ru.astrainteractive.aspekt.module.moneydrop.di
import ru.astrainteractive.aspekt.di.CoreModule
import ru.astrainteractive.aspekt.module.moneydrop.MoneyDropController
import ru.astrainteractive.aspekt.module.moneydrop.MoneyDropEvent
import ru.astrainteractive.aspekt.module.moneydrop.database.di.MoneyDropDaoModule
import ru.astrainteractive.astralibs.lifecycle.Lifecycle

interface MoneyDropModule {
val lifecycle: Lifecycle

class Default(coreModule: CoreModule) : MoneyDropModule {
private val moneyDropDaoModule by lazy {
MoneyDropDaoModule.Default(
dataFolder = coreModule.plugin.value.dataFolder,
ioDispatcher = coreModule.dispatchers.IO
)
}
private val moneyDropController: MoneyDropController by lazy {
MoneyDropController(
pluginConfigurationDependency = coreModule.pluginConfig,
kyoriComponentSerializerDependency = coreModule.kyoriComponentSerializer,
translationDependency = coreModule.translation
translationDependency = coreModule.translation,
dispatchers = coreModule.dispatchers,
dao = moneyDropDaoModule.dao
)
}
private val moneyDropEvent: MoneyDropEvent by lazy {
MoneyDropEvent(
dependencies = MoneyDropDependencies.Default(
coreModule = coreModule,
moneyDropController = moneyDropController
moneyDropController = moneyDropController,
)
)
}
Expand Down
Loading

0 comments on commit 01fbe62

Please sign in to comment.