Ещё сырые наброски
This commit is contained in:
parent
8a53f321af
commit
0dcf402e8e
57
src/main/kotlin/main/vp_server_integration/DataManager.kt
Normal file
57
src/main/kotlin/main/vp_server_integration/DataManager.kt
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package main.vp_server_integration
|
||||||
|
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class DataManager(private val dataFolder: File) {
|
||||||
|
companion object {
|
||||||
|
private lateinit var dataFile: File
|
||||||
|
private var playerData: MutableMap<String, String> = mutableMapOf()
|
||||||
|
|
||||||
|
fun initialize(dataFolder: File) {
|
||||||
|
dataFile = File(dataFolder, "player_data.yml")
|
||||||
|
if (!dataFile.exists()) {
|
||||||
|
dataFile.parentFile?.mkdirs()
|
||||||
|
dataFile.createNewFile()
|
||||||
|
}
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPlayerVPCUsername(playerName: String): String? {
|
||||||
|
return playerData[playerName]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setPlayerVPCUsername(playerName: String, vpcUsername: String) {
|
||||||
|
playerData[playerName] = vpcUsername
|
||||||
|
saveData()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removePlayerVPCUsername(playerName: String) {
|
||||||
|
playerData.remove(playerName)
|
||||||
|
saveData()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveData() {
|
||||||
|
val config = YamlConfiguration.loadConfiguration(dataFile)
|
||||||
|
playerData.forEach { (playerName, vpcUsername) ->
|
||||||
|
config.set(playerName, vpcUsername)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
config.save(dataFile)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadData() {
|
||||||
|
val config = YamlConfiguration.loadConfiguration(dataFile)
|
||||||
|
playerData.clear()
|
||||||
|
config.getKeys(false).forEach { key: String ->
|
||||||
|
config.getString(key)?.let { value: String ->
|
||||||
|
playerData[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -16,15 +16,17 @@ import net.md_5.bungee.api.chat.HoverEvent
|
|||||||
import net.md_5.bungee.api.chat.ClickEvent
|
import net.md_5.bungee.api.chat.ClickEvent
|
||||||
import net.md_5.bungee.api.chat.TextComponent
|
import net.md_5.bungee.api.chat.TextComponent
|
||||||
import org.bukkit.ChatColor
|
import org.bukkit.ChatColor
|
||||||
|
import org.bukkit.plugin.java.JavaPluginLoader
|
||||||
|
import org.bukkit.scheduler.BukkitRunnable
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
import java.util.Properties
|
import java.util.Properties
|
||||||
|
import kotlin.collections.mutableMapOf
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
||||||
class Vp_server_integration() : JavaPlugin(), CommandExecutor {
|
class Vp_server_integration() : JavaPlugin(), CommandExecutor {
|
||||||
companion object {
|
companion object {
|
||||||
lateinit var LOGGER: Logger
|
// A lot of needed configurations
|
||||||
lateinit var SERVER: org.bukkit.Server
|
|
||||||
lateinit var USERNAME: String
|
lateinit var USERNAME: String
|
||||||
lateinit var USER_TOKEN: String
|
lateinit var USER_TOKEN: String
|
||||||
lateinit var USER_API_URL: String
|
lateinit var USER_API_URL: String
|
||||||
@ -33,10 +35,10 @@ class Vp_server_integration() : JavaPlugin(), CommandExecutor {
|
|||||||
lateinit var COMMAND_REMOVE_COINS: String
|
lateinit var COMMAND_REMOVE_COINS: String
|
||||||
lateinit var COMMAND_REMOVE_ERROR: String
|
lateinit var COMMAND_REMOVE_ERROR: String
|
||||||
lateinit var COURSE_MODE: String
|
lateinit var COURSE_MODE: String
|
||||||
var COURSE_STATIC_VALUE: Float = DEFAULT_COURSE_STATIC_VALUE
|
var COURSE_STATIC_VALUE: Double = DEFAULT_COURSE_STATIC_VALUE
|
||||||
lateinit var COURSE_DYNAMIC_COMMAND: String
|
lateinit var COURSE_DYNAMIC_COMMAND: String
|
||||||
var COURSE_COMMISSION: Float = DEFAULT_COURSE_COMMISSION
|
var COURSE_COMMISSION: Float = DEFAULT_COURSE_COMMISSION
|
||||||
|
// A lot of needed configurations
|
||||||
const val DEFAULT_USERNAME: String = "test"
|
const val DEFAULT_USERNAME: String = "test"
|
||||||
const val DEFAULT_USER_TOKEN: String = "test"
|
const val DEFAULT_USER_TOKEN: String = "test"
|
||||||
const val DEFAULT_USER_API_URL: String = "http://127.0.0.1:8010/api/"
|
const val DEFAULT_USER_API_URL: String = "http://127.0.0.1:8010/api/"
|
||||||
@ -45,9 +47,26 @@ class Vp_server_integration() : JavaPlugin(), CommandExecutor {
|
|||||||
const val DEFAULT_COMMAND_REMOVE_COINS: String = "eco take %player% %amount%"
|
const val DEFAULT_COMMAND_REMOVE_COINS: String = "eco take %player% %amount%"
|
||||||
const val DEFAULT_COMMAND_REMOVE_ERROR: String = "Error:"
|
const val DEFAULT_COMMAND_REMOVE_ERROR: String = "Error:"
|
||||||
const val DEFAULT_COURSE_MODE: String = "dynamic"
|
const val DEFAULT_COURSE_MODE: String = "dynamic"
|
||||||
const val DEFAULT_COURSE_STATIC_VALUE: Float = 1000.0f
|
const val DEFAULT_COURSE_STATIC_VALUE: Double = 1000.0
|
||||||
const val DEFAULT_COURSE_DYNAMIC_COMMAND: String = "baltop force"
|
const val DEFAULT_COURSE_DYNAMIC_COMMAND: String = "baltop force"
|
||||||
const val DEFAULT_COURSE_COMMISSION: Float = 5.0f
|
const val DEFAULT_COURSE_COMMISSION: Float = 5.0f
|
||||||
|
|
||||||
|
const val PREFIX = "&9[&bVPC&7-&6I&9] &3"
|
||||||
|
|
||||||
|
// Helpful variables
|
||||||
|
lateinit var LOGGER: Logger
|
||||||
|
lateinit var SERVER: org.bukkit.Server
|
||||||
|
// For background checks
|
||||||
|
@JvmStatic
|
||||||
|
var TO_AUTH_PLAYERS: MutableMap<String, String> = mutableMapOf() // Pair: {player: vpc_username}
|
||||||
|
@JvmStatic
|
||||||
|
var TO_AUTH_PLAYERS_INVOICES: MutableMap<String, String> = mutableMapOf() // Pair: {player: invoice_id}
|
||||||
|
@JvmStatic
|
||||||
|
var TO_PAY_INVOICES: MutableMap<String, String> = mutableMapOf() // Pair: {player: invoice_id}
|
||||||
|
@JvmStatic
|
||||||
|
var INVOICES_AMOUNT: MutableMap<String, Double> = mutableMapOf() // To re-send message when user rejoin/etc.
|
||||||
|
@JvmStatic
|
||||||
|
var INVOICES_REWARDS: MutableMap<String, String> = mutableMapOf() // How many should we pay to player after invoice?
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadConfiguration() {
|
private fun loadConfiguration() {
|
||||||
@ -116,19 +135,19 @@ course_commission=$DEFAULT_COURSE_COMMISSION
|
|||||||
val properties = Properties()
|
val properties = Properties()
|
||||||
FileInputStream(configFile).use { properties.load(it) }
|
FileInputStream(configFile).use { properties.load(it) }
|
||||||
|
|
||||||
USERNAME = properties.getProperty("username", DEFAULT_USERNAME)
|
USERNAME = properties.getProperty("username", DEFAULT_USERNAME).replace("'", "")
|
||||||
USER_TOKEN = properties.getProperty("user_token", DEFAULT_USER_TOKEN)
|
USER_TOKEN = properties.getProperty("user_token", DEFAULT_USER_TOKEN).replace("'", "")
|
||||||
USER_API_URL = properties.getProperty("user_api_url", DEFAULT_USER_API_URL)
|
USER_API_URL = properties.getProperty("user_api_url", DEFAULT_USER_API_URL).replace("'", "")
|
||||||
COMMAND_ADD_COINS = properties.getProperty("command_add_coins", DEFAULT_COMMAND_ADD_COINS)
|
COMMAND_ADD_COINS = properties.getProperty("command_add_coins", DEFAULT_COMMAND_ADD_COINS).replace("'", "")
|
||||||
COMMAND_REMOVE_MODE = properties.getProperty("command_remove_mode", DEFAULT_COMMAND_REMOVE_MODE)
|
COMMAND_REMOVE_MODE = properties.getProperty("command_remove_mode", DEFAULT_COMMAND_REMOVE_MODE).replace("'", "")
|
||||||
COMMAND_REMOVE_COINS = properties.getProperty("command_remove_coins", DEFAULT_COMMAND_REMOVE_COINS)
|
COMMAND_REMOVE_COINS = properties.getProperty("command_remove_coins", DEFAULT_COMMAND_REMOVE_COINS).replace("'", "")
|
||||||
COMMAND_REMOVE_ERROR = properties.getProperty("command_remove_error", DEFAULT_COMMAND_REMOVE_ERROR)
|
COMMAND_REMOVE_ERROR = properties.getProperty("command_remove_error", DEFAULT_COMMAND_REMOVE_ERROR).replace("'", "")
|
||||||
COURSE_MODE = properties.getProperty("course_mode", DEFAULT_COURSE_MODE)
|
COURSE_MODE = properties.getProperty("course_mode", DEFAULT_COURSE_MODE).replace("'", "")
|
||||||
COURSE_STATIC_VALUE = properties.getProperty("course_static_value", DEFAULT_COURSE_STATIC_VALUE.toString()).toFloat()
|
COURSE_STATIC_VALUE = properties.getProperty("course_static_value", DEFAULT_COURSE_STATIC_VALUE.toString()).replace("'", "").toDouble()
|
||||||
COURSE_DYNAMIC_COMMAND = properties.getProperty("course_dynamic_command", DEFAULT_COURSE_DYNAMIC_COMMAND)
|
COURSE_DYNAMIC_COMMAND = properties.getProperty("course_dynamic_command", DEFAULT_COURSE_DYNAMIC_COMMAND).replace("'", "")
|
||||||
COURSE_COMMISSION = properties.getProperty("course_commission", DEFAULT_COURSE_COMMISSION.toString()).toFloat()
|
COURSE_COMMISSION = properties.getProperty("course_commission", DEFAULT_COURSE_COMMISSION.toString()).replace("'", "").toFloat()
|
||||||
|
|
||||||
logger.info("Loaded configuration, username: $USERNAME, user_api_url: $USER_API_URL")
|
logger.info("Loaded configuration: username: $USERNAME, user_api_url: $USER_API_URL")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
// In Spigot, we use severe() for error logging
|
// In Spigot, we use severe() for error logging
|
||||||
logger.severe("Failed to load configuration, using defaults: ${e.message}")
|
logger.severe("Failed to load configuration, using defaults: ${e.message}")
|
||||||
@ -149,6 +168,11 @@ course_commission=$DEFAULT_COURSE_COMMISSION
|
|||||||
|
|
||||||
getCommand("vpi")?.setExecutor(this)
|
getCommand("vpi")?.setExecutor(this)
|
||||||
|
|
||||||
|
// Background checks such a auth, invoice check, ...
|
||||||
|
startBackgroundChecks()
|
||||||
|
// Init data manager
|
||||||
|
DataManager.initialize(getDataFolder())
|
||||||
|
|
||||||
// Plugin startup logic
|
// Plugin startup logic
|
||||||
LOGGER.info("VP Server Integration plugin enabled!")
|
LOGGER.info("VP Server Integration plugin enabled!")
|
||||||
}
|
}
|
||||||
@ -168,54 +192,143 @@ course_commission=$DEFAULT_COURSE_COMMISSION
|
|||||||
// VpcApi.send(sender, TotalBalanceModules.getEssentialsBalance().toString())
|
// VpcApi.send(sender, TotalBalanceModules.getEssentialsBalance().toString())
|
||||||
// return true
|
// return true
|
||||||
|
|
||||||
if (args.size == 3 && args[0] == "convert") {
|
if ((args.size == 3 || args.size == 4) && args[0] == "convert") {
|
||||||
|
logger.severe("Step 1")
|
||||||
|
val vpcUsername = DataManager.getPlayerVPCUsername(sender.name)
|
||||||
|
if (vpcUsername == null) {
|
||||||
|
VpcApi.send(sender, "&cНеобходимо авторизоваться через: /vpi auth")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
logger.severe("Step 2")
|
||||||
val direction = args[1]
|
val direction = args[1]
|
||||||
val amount = abs(args[2].toFloat())
|
val amount = abs(args[2].toDouble())
|
||||||
if (!listOf<String>("vpc", "lc").contains(direction)) {
|
if (!listOf<String>("vpc", "lc").contains(direction)) {
|
||||||
VpcApi.send(sender, "&cНе существует такого направление, правильные: vpc/lc")
|
VpcApi.send(sender, "&cНе существует такого направление, правильные: vpc/lc")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
logger.severe("Step 3")
|
||||||
val course: Float // VPC to LC
|
val course: Double // VPC to LC
|
||||||
if (COURSE_MODE == "static") {
|
if (COURSE_MODE == "static") {
|
||||||
course = COURSE_STATIC_VALUE
|
course = COURSE_STATIC_VALUE
|
||||||
} else {
|
} else {
|
||||||
if (COURSE_DYNAMIC_COMMAND == "baltop") {
|
if (COURSE_DYNAMIC_COMMAND == "baltop") {
|
||||||
|
logger.severe("Step 4.1")
|
||||||
val globalBalance = TotalBalanceModules.getEssentialsBalance()
|
val globalBalance = TotalBalanceModules.getEssentialsBalance()
|
||||||
|
logger.severe("Step 4.1.2")
|
||||||
val vpcUser = VpcApi.user_in_db(username=USERNAME)
|
val vpcUser = VpcApi.user_in_db(username=USERNAME)
|
||||||
val vpcBalance: Float
|
logger.severe("Step 4.1.3")
|
||||||
|
val vpcBalance: Double
|
||||||
|
logger.severe("Step 4.1.4")
|
||||||
if (vpcUser == null) {
|
if (vpcUser == null) {
|
||||||
throw Exception("null vpcUser")
|
throw Exception("null vpcUser")
|
||||||
}
|
}
|
||||||
vpcBalance = vpcUser["balance"] as Float
|
logger.severe("Step 4.1.6")
|
||||||
|
logger.info(vpcUser["balance"].toString())
|
||||||
|
vpcBalance = vpcUser["balance"].toString().toDouble()
|
||||||
|
logger.severe("Step 4.1.7")
|
||||||
|
logger.info("globalBalance: $globalBalance")
|
||||||
|
logger.info("vpcBalance: $vpcBalance")
|
||||||
course = globalBalance/vpcBalance
|
course = globalBalance/vpcBalance
|
||||||
|
logger.info("course: $course")
|
||||||
} else {
|
} else {
|
||||||
val globalBalance = CommandCapture.execute(COURSE_DYNAMIC_COMMAND)[0].toFloat()
|
logger.severe("Step 4.2")
|
||||||
|
val globalBalance = CommandCapture.execute(COURSE_DYNAMIC_COMMAND).toString().toDouble()
|
||||||
|
logger.severe("$globalBalance")
|
||||||
val vpcUser = VpcApi.user_in_db(username=USERNAME)
|
val vpcUser = VpcApi.user_in_db(username=USERNAME)
|
||||||
val vpcBalance: Float
|
val vpcBalance: Double
|
||||||
if (vpcUser == null) {
|
if (vpcUser == null) {
|
||||||
throw Exception("null vpcUser")
|
throw Exception("null vpcUser")
|
||||||
}
|
}
|
||||||
vpcBalance = vpcUser["balance"] as Float
|
vpcBalance = vpcUser["balance"].toString().toDouble()
|
||||||
course = globalBalance/vpcBalance
|
course = globalBalance/vpcBalance
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (direction == "vpc") {
|
if (direction == "vpc") {
|
||||||
val result: String = CommandCapture.execute(COURSE_DYNAMIC_COMMAND)[0]
|
val amountVPC: Double = amount/course * (1 + COURSE_COMMISSION/100)
|
||||||
if (result.contains(COMMAND_REMOVE_ERROR)) {
|
if (args.size == 4) {
|
||||||
VpcApi.send(sender, "&cОшибка, возможно недостаточно средств.")
|
logger.severe("Step 5")
|
||||||
|
val result: String = CommandCapture.execute(COMMAND_REMOVE_COINS.replace("%player%", sender.name).replace("%amount%", amount.toString()))[0]
|
||||||
|
logger.severe("Step 6")
|
||||||
|
if (result.contains(COMMAND_REMOVE_ERROR)) {
|
||||||
|
VpcApi.send(sender, "&cОшибка, возможно недостаточно средств.")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
logger.severe("Step 8")
|
||||||
|
VpcApi.transfer_coins(vpcUsername, amountVPC)
|
||||||
|
} else {
|
||||||
|
val colored = ChatColor.translateAlternateColorCodes('&', "${PREFIX}Кликните здесь, чтобы конвертировать &6$amount&3 в &6${String.format("%.4f", amountVPC)} VPC")
|
||||||
|
val message = TextComponent(colored)
|
||||||
|
message.clickEvent = ClickEvent(ClickEvent.Action.RUN_COMMAND, "/vpi convert vpc $amount confirm")
|
||||||
|
message.hoverEvent = HoverEvent(HoverEvent.Action.SHOW_TEXT, ComponentBuilder("/vpi convert vpc $amount confirm").create())
|
||||||
|
sender.spigot().sendMessage(message)
|
||||||
|
}
|
||||||
|
} else if (direction == "lc") {
|
||||||
|
if (sender.name in TO_PAY_INVOICES) {
|
||||||
|
val invoice_id = TO_PAY_INVOICES[sender.name]
|
||||||
|
val amount = INVOICES_AMOUNT[invoice_id]
|
||||||
|
if (amount == null) return true // Stupid Kotlin, it's not null
|
||||||
|
val amountLC: Double = amount*course * (1 - COURSE_COMMISSION/100)
|
||||||
|
val colored = ChatColor.translateAlternateColorCodes('&', "${PREFIX}Кликните здесь, чтобы перевести &6$amount VPC &3в &6${String.format("%.4f", amountLC)}")
|
||||||
|
val message = TextComponent(colored)
|
||||||
|
message.clickEvent = ClickEvent(ClickEvent.Action.RUN_COMMAND, "/vpc pay $USERNAME $amount $invoice_id")
|
||||||
|
message.hoverEvent = HoverEvent(HoverEvent.Action.SHOW_TEXT, ComponentBuilder("/vpc pay $USERNAME $amount $invoice_id").create())
|
||||||
|
sender.spigot().sendMessage(message)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// VpcApi.transfer_coins()
|
logger.severe("amount: $amount")
|
||||||
} else if (direction == "lc") {
|
logger.severe("course: $course")
|
||||||
|
val amountLC: Double = amount*course * (1 - COURSE_COMMISSION/100)
|
||||||
|
logger.severe("amountLC: $amountLC")
|
||||||
|
val invoice_id = VpcApi.create_invoice(amount).toString()
|
||||||
|
TO_PAY_INVOICES[sender.name] = invoice_id
|
||||||
|
INVOICES_AMOUNT[invoice_id] = amountLC
|
||||||
|
|
||||||
|
val colored = ChatColor.translateAlternateColorCodes('&', "${PREFIX}Кликните здесь, чтобы перевести &6$amount VPC &3в &6${String.format("%.4f", amountLC)}")
|
||||||
|
val message = TextComponent(colored)
|
||||||
|
message.clickEvent = ClickEvent(ClickEvent.Action.RUN_COMMAND, "/vpc pay $USERNAME $amount $invoice_id")
|
||||||
|
message.hoverEvent = HoverEvent(HoverEvent.Action.SHOW_TEXT, ComponentBuilder("/vpc pay $USERNAME $amount $invoice_id").create())
|
||||||
|
sender.spigot().sendMessage(message)
|
||||||
}
|
}
|
||||||
} else if (args.isNotEmpty() && args[0] == "convert") {
|
} else if (args.isNotEmpty() && args[0] == "convert") {
|
||||||
VpcApi.send(sender, "/vpi convert <vpc/lc> <сумма>")
|
VpcApi.send(sender, "/vpi convert <vpc/lc> <сумма>")
|
||||||
|
|
||||||
|
} else if (args.size == 2 && args[0] == "auth") {
|
||||||
|
val vpcUsername = DataManager.getPlayerVPCUsername(sender.name)
|
||||||
|
if (vpcUsername != null) {
|
||||||
|
VpcApi.send(sender, "Вы уже авторизованы!")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (sender.name in TO_AUTH_PLAYERS) {
|
||||||
|
val colored = ChatColor.translateAlternateColorCodes('&', "${PREFIX}Кликните здесь, чтобы перевести &60.001 VPC")
|
||||||
|
val message = TextComponent(colored)
|
||||||
|
message.clickEvent = ClickEvent(ClickEvent.Action.RUN_COMMAND, "/vpc pay $USERNAME 0.001 ${TO_AUTH_PLAYERS_INVOICES[sender.name]}")
|
||||||
|
message.hoverEvent = HoverEvent(HoverEvent.Action.SHOW_TEXT, ComponentBuilder("/vpc pay $USERNAME 0.001 ${TO_AUTH_PLAYERS_INVOICES[sender.name]}").create())
|
||||||
|
sender.spigot().sendMessage(message)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
val invoice_id = VpcApi.create_invoice(0.001)
|
||||||
|
val vpc_username = args[1]
|
||||||
|
logger.severe("Adding to lists")
|
||||||
|
synchronized(TO_AUTH_PLAYERS) {
|
||||||
|
TO_AUTH_PLAYERS[sender.name] = vpc_username
|
||||||
|
TO_AUTH_PLAYERS_INVOICES[sender.name] = invoice_id.toString()
|
||||||
|
}
|
||||||
|
logger.severe("TO_AUTH_PLAYERS: $TO_AUTH_PLAYERS")
|
||||||
|
logger.severe("TO_AUTH_PLAYERS_INVOICES: $TO_AUTH_PLAYERS_INVOICES")
|
||||||
|
val colored = ChatColor.translateAlternateColorCodes('&', "${PREFIX}Кликните здесь, чтобы перевести 0.001 VPC")
|
||||||
|
val message = TextComponent(colored)
|
||||||
|
message.clickEvent = ClickEvent(ClickEvent.Action.RUN_COMMAND, "/vpc pay $USERNAME 0.001 $invoice_id")
|
||||||
|
message.hoverEvent = HoverEvent(HoverEvent.Action.SHOW_TEXT, ComponentBuilder("/vpc pay $USERNAME 0.001 $invoice_id").create())
|
||||||
|
sender.spigot().sendMessage(message)
|
||||||
|
|
||||||
|
} else if (args.isNotEmpty() && args[0] == "auth") {
|
||||||
|
VpcApi.send(sender, "/vpi auth <ник>")
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
VpcApi.send(
|
VpcApi.send(
|
||||||
sender, """Использование команд:
|
sender, """Использование команд:
|
||||||
|
/vpi auth <ник> - Авторизация
|
||||||
/vpi convert <куда: vpc/lc> <сумма> - Обмен VPC на локальную валюту или наоборот
|
/vpi convert <куда: vpc/lc> <сумма> - Обмен VPC на локальную валюту или наоборот
|
||||||
|
|
||||||
Почему 'VPC-I'? Потому что это интеграция на конечном сервере - 'VPC Integration'
|
Почему 'VPC-I'? Потому что это интеграция на конечном сервере - 'VPC Integration'
|
||||||
@ -228,6 +341,15 @@ course_commission=$DEFAULT_COURSE_COMMISSION
|
|||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
// Create a clickable message that runs /help when clicked
|
||||||
|
// val message = TextComponent("Click here to execute command")
|
||||||
|
// val colored = ChatColor.translateAlternateColorCodes('&', "&6Testing")
|
||||||
|
// val message = TextComponent(colored)
|
||||||
|
// message.clickEvent = ClickEvent(ClickEvent.Action.RUN_COMMAND, "/vpc bal")
|
||||||
|
// message.hoverEvent = HoverEvent(HoverEvent.Action.SHOW_TEXT, ComponentBuilder("Click here to execute command").create())
|
||||||
|
//
|
||||||
|
// sender.spigot().sendMessage(message)
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -237,7 +359,7 @@ course_commission=$DEFAULT_COURSE_COMMISSION
|
|||||||
if (command.name.equals("vpi", ignoreCase = true)) {
|
if (command.name.equals("vpi", ignoreCase = true)) {
|
||||||
when (args.size) {
|
when (args.size) {
|
||||||
1 -> {
|
1 -> {
|
||||||
completions.addAll(listOf("help", "convert"))
|
completions.addAll(listOf("help", "convert", "auth"))
|
||||||
}
|
}
|
||||||
2 -> {
|
2 -> {
|
||||||
if (args[0].equals("convert", ignoreCase = true)) {
|
if (args[0].equals("convert", ignoreCase = true)) {
|
||||||
@ -254,4 +376,93 @@ course_commission=$DEFAULT_COURSE_COMMISSION
|
|||||||
|
|
||||||
return completions.filter { it.startsWith(args.lastOrNull()?.lowercase() ?: "") }.sorted()
|
return completions.filter { it.startsWith(args.lastOrNull()?.lowercase() ?: "") }.sorted()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun startBackgroundChecks() {
|
||||||
|
object : BukkitRunnable() {
|
||||||
|
override fun run() {
|
||||||
|
// Safely check and process TO_AUTH_PLAYERS_INVOICES
|
||||||
|
synchronized(TO_AUTH_PLAYERS_INVOICES) {
|
||||||
|
if (TO_AUTH_PLAYERS_INVOICES.isNotEmpty()) {
|
||||||
|
val iterator = TO_AUTH_PLAYERS_INVOICES.iterator()
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
val entry = iterator.next()
|
||||||
|
try {
|
||||||
|
val result = VpcApi.get_invoice(entry.value) as Map<*, *>?
|
||||||
|
|
||||||
|
// Add null safety checks
|
||||||
|
if (result != null) {
|
||||||
|
val status = result["status"]
|
||||||
|
logger.severe("Invoice ${entry.value} status: $status")
|
||||||
|
|
||||||
|
if (status != null && status.toString() == "true") {
|
||||||
|
logger.severe("DELETE!!!!!!!!!!!!!!!!")
|
||||||
|
VpcApi.delete_invoice(entry.value)
|
||||||
|
|
||||||
|
DataManager.setPlayerVPCUsername(entry.key, TO_AUTH_PLAYERS[entry.key].toString())
|
||||||
|
VpcApi.send(Bukkit.getPlayer(entry.key.toString()), "&aВы успешно авторизованы!")
|
||||||
|
|
||||||
|
// Safely remove from both maps
|
||||||
|
TO_AUTH_PLAYERS.remove(entry.key)
|
||||||
|
iterator.remove() // Safe removal during iteration
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.warning("Received null result for invoice ${entry.value}")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logger.severe("Error processing invoice ${entry.value}: ${e.message}")
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safely check and process TO_PAY_INVOICES
|
||||||
|
synchronized(TO_PAY_INVOICES) {
|
||||||
|
if (TO_PAY_INVOICES.isNotEmpty()) {
|
||||||
|
val iterator = TO_PAY_INVOICES.iterator()
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
val entry = iterator.next()
|
||||||
|
try {
|
||||||
|
val result = VpcApi.get_invoice(entry.value) as Map<*, *>?
|
||||||
|
|
||||||
|
// Add null safety checks
|
||||||
|
if (result != null) {
|
||||||
|
val status = result["status"]
|
||||||
|
logger.severe("Invoice ${entry.value} status: $status")
|
||||||
|
|
||||||
|
if (status != null && status.toString() == "true") {
|
||||||
|
logger.severe("DELETE!!!!!!!!!!!!!!!!")
|
||||||
|
VpcApi.delete_invoice(entry.value)
|
||||||
|
|
||||||
|
val amountLC = INVOICES_AMOUNT[entry.value]
|
||||||
|
|
||||||
|
// Fix: Schedule command execution on main thread
|
||||||
|
val commandToAddCoins = COMMAND_ADD_COINS.replace("%player%", entry.key).replace("%amount%", amountLC.toString())
|
||||||
|
Bukkit.getScheduler().runTask(this@Vp_server_integration, Runnable {
|
||||||
|
try {
|
||||||
|
CommandCapture.execute(commandToAddCoins)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logger.severe("Error executing command: ${e.message}")
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Safely remove from both maps
|
||||||
|
INVOICES_AMOUNT.remove(entry.key)
|
||||||
|
iterator.remove() // Safe removal during iteration
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.warning("Received null result for invoice ${entry.value}")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logger.severe("Error processing invoice ${entry.value}: ${e.message}")
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.runTaskTimerAsynchronously(this, 0L, 20L) // Changed to 20L (1 second) for better performance
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,9 @@ import com.google.gson.Gson
|
|||||||
import com.google.gson.JsonElement
|
import com.google.gson.JsonElement
|
||||||
import com.google.gson.JsonParser
|
import com.google.gson.JsonParser
|
||||||
import com.google.gson.JsonSyntaxException
|
import com.google.gson.JsonSyntaxException
|
||||||
|
import main.vp_server_integration.Vp_server_integration.Companion.PREFIX
|
||||||
import main.vp_server_integration.Vp_server_integration.Companion.USERNAME
|
import main.vp_server_integration.Vp_server_integration.Companion.USERNAME
|
||||||
|
import main.vp_server_integration.Vp_server_integration.Companion.USER_API_URL
|
||||||
import main.vp_server_integration.Vp_server_integration.Companion.USER_TOKEN
|
import main.vp_server_integration.Vp_server_integration.Companion.USER_TOKEN
|
||||||
import org.bukkit.ChatColor
|
import org.bukkit.ChatColor
|
||||||
import org.bukkit.command.CommandSender
|
import org.bukkit.command.CommandSender
|
||||||
@ -20,9 +22,16 @@ import java.net.HttpURLConnection
|
|||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.net.URISyntaxException
|
import java.net.URISyntaxException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
class VpcApi {
|
class VpcApi {
|
||||||
companion object {
|
companion object {
|
||||||
|
// Rate limiting: track timestamps of requests
|
||||||
|
private val requestTimestamps = ConcurrentLinkedQueue<Long>()
|
||||||
|
private const val MAX_REQUESTS_PER_MINUTE = 60
|
||||||
|
private const val MINUTE_IN_MILLIS = 60 * 1000L
|
||||||
|
|
||||||
// Using Gson for JSON serialization - much cleaner than manual string building
|
// Using Gson for JSON serialization - much cleaner than manual string building
|
||||||
fun jsonify(args: List<String>): String {
|
fun jsonify(args: List<String>): String {
|
||||||
val map = mutableMapOf<String, String>()
|
val map = mutableMapOf<String, String>()
|
||||||
@ -43,13 +52,50 @@ class VpcApi {
|
|||||||
operator fun get(key: String): Any? = data?.get(key)
|
operator fun get(key: String): Any? = data?.get(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enforces rate limiting by checking if we've exceeded the request limit.
|
||||||
|
* If so, sleeps until we're within the limit again.
|
||||||
|
*/
|
||||||
|
private fun enforceRateLimit() {
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
|
||||||
|
// Remove timestamps older than 1 minute
|
||||||
|
while (requestTimestamps.isNotEmpty() && now - requestTimestamps.peek() > MINUTE_IN_MILLIS) {
|
||||||
|
requestTimestamps.poll()
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we've hit the limit, calculate sleep time
|
||||||
|
if (requestTimestamps.size >= MAX_REQUESTS_PER_MINUTE) {
|
||||||
|
val oldestRequestTime = requestTimestamps.peek()
|
||||||
|
val sleepTime = max(0, MINUTE_IN_MILLIS - (now - oldestRequestTime)) + 100 // Add small buffer
|
||||||
|
|
||||||
|
try {
|
||||||
|
Thread.sleep(sleepTime)
|
||||||
|
} catch (e: InterruptedException) {
|
||||||
|
Thread.currentThread().interrupt()
|
||||||
|
}
|
||||||
|
|
||||||
|
// After sleeping, clean up old timestamps again
|
||||||
|
val afterSleep = System.currentTimeMillis()
|
||||||
|
while (requestTimestamps.isNotEmpty() && afterSleep - requestTimestamps.peek() > MINUTE_IN_MILLIS) {
|
||||||
|
requestTimestamps.poll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add current timestamp
|
||||||
|
requestTimestamps.offer(now)
|
||||||
|
}
|
||||||
|
|
||||||
fun sendPost(urlPoint: String, json: String, customUrl: Boolean = false, baseurl: String = ""): ApiResponse {
|
fun sendPost(urlPoint: String, json: String, customUrl: Boolean = false, baseurl: String = ""): ApiResponse {
|
||||||
|
// Enforce rate limiting before making the request
|
||||||
|
enforceRateLimit()
|
||||||
|
|
||||||
var uri: URI
|
var uri: URI
|
||||||
try {
|
try {
|
||||||
uri = if (customUrl) {
|
uri = if (customUrl) {
|
||||||
URI.create(baseurl + urlPoint)
|
URI.create(baseurl + urlPoint)
|
||||||
} else {
|
} else {
|
||||||
URI.create(Vp_server_integration.DEFAULT_USER_API_URL + urlPoint)
|
URI.create(USER_API_URL + urlPoint)
|
||||||
}
|
}
|
||||||
} catch (e: URISyntaxException) {
|
} catch (e: URISyntaxException) {
|
||||||
return ApiResponse(-1, null, "Error: Invalid URL syntax")
|
return ApiResponse(-1, null, "Error: Invalid URL syntax")
|
||||||
@ -110,7 +156,7 @@ class VpcApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun send(source: CommandSender, message: String, prefix: String = "&9[&bVPC&7-&6I&9] &3") {
|
fun send(source: CommandSender, message: String, prefix: String = PREFIX) {
|
||||||
// Translate color codes for Spigot
|
// Translate color codes for Spigot
|
||||||
val coloredMessage = ChatColor.translateAlternateColorCodes('&', prefix + message)
|
val coloredMessage = ChatColor.translateAlternateColorCodes('&', prefix + message)
|
||||||
source.sendMessage(coloredMessage)
|
source.sendMessage(coloredMessage)
|
||||||
@ -128,6 +174,10 @@ class VpcApi {
|
|||||||
return "None"
|
return "None"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Simple hash function (you might want to replace this with a proper hashing algorithm)
|
||||||
|
private fun hash(input: String): String {
|
||||||
|
return UUID.nameUUIDFromBytes(input.toByteArray()).toString()
|
||||||
|
}
|
||||||
|
|
||||||
fun user_in_db(source: CommandSender? = null, username: String? = null): Map<String, Any>? {
|
fun user_in_db(source: CommandSender? = null, username: String? = null): Map<String, Any>? {
|
||||||
var mine_uuid: String? = null
|
var mine_uuid: String? = null
|
||||||
@ -141,7 +191,7 @@ class VpcApi {
|
|||||||
val response = sendPost(
|
val response = sendPost(
|
||||||
"user_in_db/", jsonify(
|
"user_in_db/", jsonify(
|
||||||
listOf(
|
listOf(
|
||||||
"token", Vp_server_integration.DEFAULT_USER_TOKEN,
|
"user_token", USER_TOKEN,
|
||||||
userParam.first, userParam.second
|
userParam.first, userParam.second
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -155,12 +205,12 @@ class VpcApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun transfer_coins(dst_username: String, amount: Float): Any? {
|
fun transfer_coins(dst_username: String, amount: Double): Any? {
|
||||||
val response = sendPost(
|
val response = sendPost(
|
||||||
"transfer_coins/", jsonify(
|
"transfer_coins/", jsonify(
|
||||||
listOf(
|
listOf(
|
||||||
|
"username", USERNAME,
|
||||||
"user_token", USER_TOKEN,
|
"user_token", USER_TOKEN,
|
||||||
"src_username", USERNAME,
|
|
||||||
"dst_username", dst_username,
|
"dst_username", dst_username,
|
||||||
"amount", amount.toString()
|
"amount", amount.toString()
|
||||||
)
|
)
|
||||||
@ -174,9 +224,65 @@ class VpcApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simple hash function (you might want to replace this with a proper hashing algorithm)
|
fun create_invoice(amount: Double): Any? {
|
||||||
private fun hash(input: String): String {
|
val response = sendPost(
|
||||||
return UUID.nameUUIDFromBytes(input.toByteArray()).toString()
|
"create_invoice/", jsonify(
|
||||||
|
listOf(
|
||||||
|
"username", USERNAME,
|
||||||
|
"user_token", USER_TOKEN,
|
||||||
|
"amount", amount.toString()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return if (response.status == 200) {
|
||||||
|
response.rawResponse
|
||||||
|
} else {
|
||||||
|
response.data?.get("detail")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun delete_invoice(invoice_id: String): Any? {
|
||||||
|
val response = sendPost(
|
||||||
|
"delete_invoice/", jsonify(
|
||||||
|
listOf(
|
||||||
|
"username", USERNAME,
|
||||||
|
"user_token", USER_TOKEN,
|
||||||
|
"invoice_id", invoice_id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return if (response.status == 200) {
|
||||||
|
response.rawResponse
|
||||||
|
} else {
|
||||||
|
response.data?.get("detail")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun get_invoice(invoice_id: String): Any? {
|
||||||
|
val response = sendPost(
|
||||||
|
"get_invoice/", jsonify(
|
||||||
|
listOf(
|
||||||
|
"username", USERNAME,
|
||||||
|
"user_token", USER_TOKEN,
|
||||||
|
"invoice_id", invoice_id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// response["someKey"] (map-like access)
|
||||||
|
// OUT: {
|
||||||
|
// "id": str,
|
||||||
|
// "dst_username": str,
|
||||||
|
// "amount": null / float,
|
||||||
|
// "status": false/true
|
||||||
|
//}
|
||||||
|
return if (response.status == 200) {
|
||||||
|
response.data
|
||||||
|
} else {
|
||||||
|
response.data?.get("detail")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user