diff --git a/src/main/kotlin/main/vp_server_integration/Utils.kt b/src/main/kotlin/main/vp_server_integration/Utils.kt index aa19358..d1db836 100644 --- a/src/main/kotlin/main/vp_server_integration/Utils.kt +++ b/src/main/kotlin/main/vp_server_integration/Utils.kt @@ -16,13 +16,13 @@ class MyLogger(debugEnabled: Boolean, private val loggerOriginal: java.util.logg fun info(message: String) { if (debug_enabled) { - loggerSpigot.info(message) + loggerSpigot.info("[DBG] $message") } } fun error(message: String) { if (debug_enabled) { - loggerSpigot.severe(message) + loggerSpigot.severe("[DBG] message") } } } diff --git a/src/main/kotlin/main/vp_server_integration/Vp_server_integration.kt b/src/main/kotlin/main/vp_server_integration/Vp_server_integration.kt index 7d18e60..440384e 100644 --- a/src/main/kotlin/main/vp_server_integration/Vp_server_integration.kt +++ b/src/main/kotlin/main/vp_server_integration/Vp_server_integration.kt @@ -82,9 +82,42 @@ class Vp_server_integration() : JavaPlugin(), CommandExecutor { val configFile = File(dataFolder, "config.properties") if (!configFile.exists()) { - // Create default config file - configFile.writeText( - """ + createDefaultConfig(configFile) + } + + // Load properties from file + val properties = Properties() + FileInputStream(configFile).use { properties.load(it) } + + // Parse configuration values + DEBUG_FLAG = properties.getProperty("debug", false.toString()).replace("'", "").toBoolean() + USERNAME = properties.getProperty("username", DEFAULT_USERNAME).replace("'", "") + USER_TOKEN = properties.getProperty("user_token", DEFAULT_USER_TOKEN).replace("'", "") + 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).replace("'", "") + 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).replace("'", "") + COMMAND_REMOVE_ERROR = properties.getProperty("command_remove_error", DEFAULT_COMMAND_REMOVE_ERROR).replace("'", "") + COURSE_MODE = properties.getProperty("course_mode", DEFAULT_COURSE_MODE).replace("'", "") + 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).replace("'", "") + COURSE_COMMISSION = properties.getProperty("course_commission", DEFAULT_COURSE_COMMISSION.toString()).replace("'", "").toFloat() + + logger.info("Configuration loaded successfully - Username: $USERNAME, API URL: $USER_API_URL") + } catch (e: Exception) { + logger.severe("Failed to load configuration, using defaults: ${e.message}") + USERNAME = DEFAULT_USERNAME + USER_TOKEN = DEFAULT_USER_TOKEN + USER_API_URL = DEFAULT_USER_API_URL + } + } + + /** + * Create default configuration file with documentation + */ + private fun createDefaultConfig(configFile: File) { + configFile.writeText( + """ # VPC Integration Configuration # For get extra output debug=false @@ -100,7 +133,6 @@ user_token=$DEFAULT_USER_TOKEN user_api_url='http://127.0.0.1:8010/api/' # -------------------- END ------------------------ - # ---------- Part for work with server ------------ # Which command run to add coins? (will run from console) command_add_coins='$DEFAULT_COMMAND_ADD_COINS' @@ -115,7 +147,6 @@ command_remove_coins='$DEFAULT_COMMAND_REMOVE_COINS' command_remove_error='$DEFAULT_COMMAND_REMOVE_ERROR' # -------------------- END ------------------------ - # --------- Part with configure course ------------ # Which mode use (dynamic/static, dynamic - set course based course_mode=$DEFAULT_COURSE_MODE @@ -133,34 +164,7 @@ course_dynamic_command='$DEFAULT_COURSE_DYNAMIC_COMMAND' course_commission=$DEFAULT_COURSE_COMMISSION # -------------------- END ------------------------ """.trimIndent() - ) - } - - // Load properties - val properties = Properties() - FileInputStream(configFile).use { properties.load(it) } - - DEBUG_FLAG = properties.getProperty("debug", false.toString()).replace("'", "").toBoolean() - USERNAME = properties.getProperty("username", DEFAULT_USERNAME).replace("'", "") - USER_TOKEN = properties.getProperty("user_token", DEFAULT_USER_TOKEN).replace("'", "") - 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).replace("'", "") - 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).replace("'", "") - COMMAND_REMOVE_ERROR = properties.getProperty("command_remove_error", DEFAULT_COMMAND_REMOVE_ERROR).replace("'", "") - COURSE_MODE = properties.getProperty("course_mode", DEFAULT_COURSE_MODE).replace("'", "") - 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).replace("'", "") - COURSE_COMMISSION = properties.getProperty("course_commission", DEFAULT_COURSE_COMMISSION.toString()).replace("'", "").toFloat() - - logger.info("Loaded configuration: username: $USERNAME, user_api_url: $USER_API_URL") - } catch (e: Exception) { - // In Spigot, we use severe() for error logging - logger.severe("Failed to load configuration, using defaults: ${e.message}") - USERNAME = DEFAULT_USERNAME - USER_TOKEN = DEFAULT_USER_TOKEN - USER_API_URL = DEFAULT_USER_API_URL - } + ) } override fun onEnable() { @@ -173,6 +177,7 @@ course_commission=$DEFAULT_COURSE_COMMISSION // Initialize logger LOGGER = MyLogger(DEBUG_FLAG, this.logger) + // Register command executor getCommand("vpi")?.setExecutor(this) // Background checks such a auth, invoice check, ... @@ -192,162 +197,280 @@ course_commission=$DEFAULT_COURSE_COMMISSION override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array): Boolean { if (command.name.equals("vpi", ignoreCase = true)) { if (sender !is Player) { - sender.sendMessage("&cТолько игроки могут выполнять команды.") + sender.sendMessage("&cТолько игроки могут выполнять данные команды.") return true } - if ((args.size == 3 || args.size == 4) && args[0] == "convert") { - LOGGER.error("Step 1") - val vpcUsername = DataManager.getPlayerVPCUsername(sender.name) - if (vpcUsername == null) { - Utils.send(sender, "&cНеобходимо авторизоваться через: /vpi auth") - return true - } - LOGGER.error("Step 2") - val direction = args[1] - val amount = abs(args[2].toDouble()) - if (!listOf("vpc", "lc").contains(direction)) { - Utils.send(sender, "&cНе существует такого направление, правильные: vpc/lc") - return true - } - LOGGER.error("Step 3") - val course: Double // VPC to LC - if (COURSE_MODE == "static") { - course = COURSE_STATIC_VALUE - } else { - if (COURSE_DYNAMIC_COMMAND == "baltop force") { - LOGGER.error("Step 4.1") - CommandCapture.execute("baltop force") - val globalBalance = TotalBalanceModules.getEssentialsBalance() - LOGGER.info("globalBalance PRE CHECK: $globalBalance") - LOGGER.error("Step 4.1.2") - val vpcUser = VpcApi.user_in_db(username=USERNAME) - LOGGER.error("Step 4.1.3") - val vpcBalance: Double - LOGGER.error("Step 4.1.4") - if (vpcUser == null) { - throw Exception("null vpcUser") - } - LOGGER.error("Step 4.1.6") - LOGGER.info(vpcUser["balance"].toString()) - vpcBalance = vpcUser["balance"].toString().toDouble() - LOGGER.error("Step 4.1.7") - LOGGER.info("globalBalance: $globalBalance") - LOGGER.info("vpcBalance: $vpcBalance") - course = globalBalance/vpcBalance - LOGGER.info("course: $course") - } else { - LOGGER.error("Step 4.2") - val globalBalance = CommandCapture.execute(COURSE_DYNAMIC_COMMAND).toString().toDouble() - LOGGER.error("$globalBalance") - val vpcUser = VpcApi.user_in_db(username=USERNAME) - val vpcBalance: Double - if (vpcUser == null) { - throw Exception("null vpcUser") - } - vpcBalance = vpcUser["balance"].toString().toDouble() - course = globalBalance/vpcBalance - } + when { + // Handle currency conversion command + (args.size == 3 || args.size == 4) && args[0] == "convert" -> { + handleCurrencyConversion(sender, args) } - if (direction == "vpc") { - val amountVPC: Double = amount/course * (1 - COURSE_COMMISSION/100) - if (args.size == 4) { - LOGGER.error("Step 5") - val result: String = CommandCapture.execute(COMMAND_REMOVE_COINS.replace("%player%", sender.name).replace("%amount%", amount.toString()))[0] - LOGGER.error("Step 6") - if (result.contains(COMMAND_REMOVE_ERROR)) { - Utils.send(sender, "&cОшибка, возможно недостаточно средств.") - return true - } - LOGGER.error("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 - } - LOGGER.error("amount: $amount") - LOGGER.error("course: $course") - val amountLC: Double = amount*course * (1 + COURSE_COMMISSION/100) - LOGGER.error("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) + // Show usage for convert command + args.isNotEmpty() && args[0] == "convert" -> { + Utils.send(sender, "/vpi convert <сумма>") } - } else if (args.isNotEmpty() && args[0] == "convert") { - Utils.send(sender, "/vpi convert <сумма>") - } else if (args.size == 2 && args[0] == "auth") { - val vpcUsername = DataManager.getPlayerVPCUsername(sender.name) - if (vpcUsername != null) { - Utils.send(sender, "Вы уже авторизованы!") - return true + // Handle authentication command + args.size == 2 && args[0] == "auth" -> { + handleAuthentication(sender, args) } - 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.error("Adding to lists") - synchronized(TO_AUTH_PLAYERS) { - TO_AUTH_PLAYERS[sender.name] = vpc_username - TO_AUTH_PLAYERS_INVOICES[sender.name] = invoice_id.toString() - } - LOGGER.error("TO_AUTH_PLAYERS: $TO_AUTH_PLAYERS") - LOGGER.error("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") { - Utils.send(sender, "/vpi auth <ник>") + // Show usage for auth command + args.isNotEmpty() && args[0] == "auth" -> { + Utils.send(sender, "/vpi auth <ник>") + } - } else { - Utils.send(sender, """Использование команд: - /vpi auth <ник> - Авторизация - /vpi convert <куда: vpc/lc> <сумма> - Обмен VPC на локальную валюту или наоборот - - Почему 'VPC-I'? Потому что это интеграция на конечном сервере - 'VPC Integration' - - Соглашение: voidproject.del.pw/vpc_agreement - Группа ТГ: @void_project_mc - Группа ДС: discord.gg/zwNt5DJj6J""".trimIndent()) + // Show general help + else -> { + showHelpMenu(sender) + } } - return true } return false } + /** + * Handle currency conversion logic + */ + private fun handleCurrencyConversion(sender: Player, args: Array) { + LOGGER.info("Starting currency conversion process - Step 1") + + // Check if player is authenticated + val vpcUsername = DataManager.getPlayerVPCUsername(sender.name) + if (vpcUsername == null) { + Utils.send(sender, "&cВы должны авторизоваться сначала: /vpi auth") + return + } + + LOGGER.info("Player is authenticated - Step 2") + + val direction = args[1] + val amount = abs(args[2].toDouble()) + + // Validate conversion direction + if (!listOf("vpc", "lc").contains(direction)) { + Utils.send(sender, "&cНеверное направление. Используйте: vpc or lc") + return + } + + LOGGER.info("Valid direction provided - Step 3") + + // Calculate exchange rate + val course = calculateExchangeRate() + LOGGER.info("Exchange rate calculated: $course") + if (course.isInfinite()) { + LOGGER.error("Zero global balance?") + Utils.send(sender, "&cПроизошла ошибка при расчёте курса. Обратитесь к администратору или повторите позже.") + return + } else if (course == 0.0) { + LOGGER.error("Infinite global balance?") + Utils.send(sender, "&cПроизошла ошибка при расчёте курса. Обратитесь к администратору или повторите позже.") + return + } + + when (direction) { + "vpc" -> handleVpcConversion(sender, amount, course, args) + "lc" -> handleLcConversion(sender, amount, course, args) + } + } + + /** + * Calculate exchange rate based on configuration + */ + private fun calculateExchangeRate(): Double { + return if (COURSE_MODE == "static") { + COURSE_STATIC_VALUE + } else { + calculateDynamicRate() + } + } + + /** + * Calculate dynamic exchange rate + */ + private fun calculateDynamicRate(): Double { + return if (COURSE_DYNAMIC_COMMAND == "baltop force") { + LOGGER.info("Calculating dynamic rate using baltop - Step 4.1") + CommandCapture.execute("baltop force") + val globalBalance = TotalBalanceModules.getEssentialsBalance() + LOGGER.info("Global balance: $globalBalance") + + val vpcUser = VpcApi.user_in_db(username = USERNAME) + if (vpcUser == null) { + throw Exception("VPC user not found") + } + + val vpcBalance = vpcUser["balance"].toString().toDouble() + LOGGER.info("VPC balance: $vpcBalance") + + val rate = globalBalance / vpcBalance + LOGGER.info("Calculated exchange rate: $rate") + rate + } else { + LOGGER.info("Calculating dynamic rate using custom command - Step 4.2") + val globalBalance = CommandCapture.execute(COURSE_DYNAMIC_COMMAND).toString().toDouble() + val vpcUser = VpcApi.user_in_db(username = USERNAME) + + if (vpcUser == null) { + throw Exception("VPC user not found") + } + + val vpcBalance = vpcUser["balance"].toString().toDouble() + globalBalance / vpcBalance + } + } + + /** + * Handle VPC to local currency conversion + */ + private fun handleVpcConversion(sender: Player, amount: Double, course: Double, args: Array) { + val amountVPC = amount / course * (1 - COURSE_COMMISSION / 100) + val vpcUsername = DataManager.getPlayerVPCUsername(sender.name) + if (vpcUsername == null) { + Utils.send(sender, "&cВы должны авторизоваться сначала: /vpi auth") + return + } + + if (args.size == 4) { + // Execute confirmed conversion + LOGGER.info("Executing confirmed conversion - Step 5") + val result = CommandCapture.execute( + COMMAND_REMOVE_COINS + .replace("%player%", sender.name) + .replace("%amount%", amount.toString()) + )[0] + + LOGGER.info("Command executed - Step 6") + if (result.contains(COMMAND_REMOVE_ERROR)) { + Utils.send(sender, "&cОшибка. Возможно недостаточно средств.") + return + } + + LOGGER.info("Transferring coins to VPC - Step 8") + VpcApi.transfer_coins(vpcUsername, amountVPC) + } else { + // Show confirmation prompt + 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) + } + } + + /** + * Handle local currency to VPC conversion + */ + private fun handleLcConversion(sender: Player, amount: Double, course: Double, args: Array) { + // Check if there's an existing pending invoice + if (sender.name in TO_PAY_INVOICES) { + handleExistingInvoice(sender) + return + } + + LOGGER.info("Amount: $amount, Course: $course") + val amountLC = amount * course * (1 + COURSE_COMMISSION / 100) + LOGGER.info("Converted amount: $amountLC") + + // Create new invoice + val invoiceId = VpcApi.create_invoice(amount).toString() + TO_PAY_INVOICES[sender.name] = invoiceId + INVOICES_AMOUNT[invoiceId] = amountLC + + sendPaymentPrompt(sender, amount, amountLC, invoiceId) + } + + /** + * Handle existing payment invoice + */ + private fun handleExistingInvoice(sender: Player) { + val invoiceId = TO_PAY_INVOICES[sender.name] + val amount = INVOICES_AMOUNT[invoiceId] + if (amount == null) return + + val course = if (COURSE_MODE == "static") COURSE_STATIC_VALUE else calculateDynamicRate() + val amountLC = amount * course * (1 + COURSE_COMMISSION / 100) + + sendPaymentPrompt(sender, amount, amountLC, invoiceId) + } + + /** + * Send payment prompt to player + */ + private fun sendPaymentPrompt(sender: Player, amount: Double, amountLC: Double, invoiceId: String?) { + val colored = ChatColor.translateAlternateColorCodes('&', + "${PREFIX}Нажмите здесь, чтобы конвертировать &6$amount VPC &3d &6${String.format("%.4f", amountLC)}") + val message = TextComponent(colored) + message.clickEvent = ClickEvent(ClickEvent.Action.RUN_COMMAND, "/vpc pay $USERNAME $amount $invoiceId") + message.hoverEvent = HoverEvent(HoverEvent.Action.SHOW_TEXT, + ComponentBuilder("/vpc pay $USERNAME $amount $invoiceId").create()) + sender.spigot().sendMessage(message) + } + + /** + * Handle player authentication process + */ + private fun handleAuthentication(sender: Player, args: Array) { + val vpcUsername = DataManager.getPlayerVPCUsername(sender.name) + if (vpcUsername != null) { + Utils.send(sender, "&cВы уже авторизованы!") + return + } + + // Check if authentication is already in progress + if (sender.name in TO_AUTH_PLAYERS) { + val invoiceId = TO_AUTH_PLAYERS_INVOICES[sender.name] + sendAuthPrompt(sender, invoiceId ?: "") + return + } + + // Start new authentication process + val invoiceId = VpcApi.create_invoice(0.001) + val vpcUsernameInput = args[1] + + LOGGER.info("Adding player to authentication lists") + synchronized(TO_AUTH_PLAYERS) { + TO_AUTH_PLAYERS[sender.name] = vpcUsernameInput + TO_AUTH_PLAYERS_INVOICES[sender.name] = invoiceId.toString() + } + + LOGGER.info("Authentication lists updated") + sendAuthPrompt(sender, invoiceId.toString()) + } + + /** + * Send authentication prompt to player + */ + private fun sendAuthPrompt(sender: Player, invoiceId: String) { + val colored = ChatColor.translateAlternateColorCodes('&', + "${PREFIX}Нажмите, чтобы перевести &60.001 VPC&3 для авторизации") + val message = TextComponent(colored) + message.clickEvent = ClickEvent(ClickEvent.Action.RUN_COMMAND, "/vpc pay $USERNAME 0.001 $invoiceId") + message.hoverEvent = HoverEvent(HoverEvent.Action.SHOW_TEXT, + ComponentBuilder("/vpc pay $USERNAME 0.001 $invoiceId").create()) + sender.spigot().sendMessage(message) + } + + /** + * Display help menu to player + */ + private fun showHelpMenu(sender: CommandSender) { + Utils.send(sender, """Использование команд: + /vpi auth <ник> - Авторизация + /vpi convert <куда: vpc/lc> <сумма> - Обмен VPC на локальную валюту или наоборот + + Почему 'VPC-I'? Потому что это интеграция на конечном сервере - 'VPC Integration' + + Соглашение: voidproject.del.pw/vpc_agreement + Группа ТГ: @void_project_mc + Группа ДС: discord.gg/zwNt5DJj6J""".trimIndent()) + } + override fun onTabComplete(sender: CommandSender, command: Command, alias: String, args: Array): List { val completions = mutableListOf() if (command.name.equals("vpi", ignoreCase = true)) { @@ -371,92 +494,106 @@ course_commission=$DEFAULT_COURSE_COMMISSION return completions.filter { it.startsWith(args.lastOrNull()?.lowercase() ?: "") }.sorted() } - + /** + * Start background checks for invoice processing + */ 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<*, *>? + processAuthenticationInvoices() + processPaymentInvoices() + } + }.runTaskTimerAsynchronously(this, 0L, 20L) + } - // Add null safety checks - if (result != null) { - val status = result["status"] - LOGGER.error("Invoice ${entry.value} status: $status") + /** + * Process authentication invoices in background + */ + private fun processAuthenticationInvoices() { + 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<*, *>? - if (status != null && status.toString() == "true") { - LOGGER.error("DELETE!!!!!!!!!!!!!!!!") - VpcApi.delete_invoice(entry.value) + if (result != null) { + val status = result["status"] + LOGGER.info("Auth invoice ${entry.value} status: $status") - DataManager.setPlayerVPCUsername(entry.key, TO_AUTH_PLAYERS[entry.key].toString()) - Utils.send(Bukkit.getPlayer(entry.key.toString()), "&aВы успешно авторизованы!") + if (status != null && status.toString() == "true") { + LOGGER.info("Processing successful authentication") + VpcApi.delete_invoice(entry.value) - // 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.error("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.error("Invoice ${entry.value} status: $status") - - if (status != null && status.toString() == "true") { - LOGGER.error("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.error("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.error("Error processing invoice ${entry.value}: ${e.message}") - e.printStackTrace() + DataManager.setPlayerVPCUsername(entry.key, TO_AUTH_PLAYERS[entry.key].toString()) + Utils.send(Bukkit.getPlayer(entry.key.toString()), "&aУспешная авторизация!") + + // Clean up tracking maps + TO_AUTH_PLAYERS.remove(entry.key) + iterator.remove() } + } else { + logger.warning("Received null result for auth invoice ${entry.value}") } + } catch (e: Exception) { + LOGGER.error("Error processing auth invoice ${entry.value}: ${e.message}") + e.printStackTrace() } } } - }.runTaskTimerAsynchronously(this, 0L, 20L) // Changed to 20L (1 second) for better performance + } + } + + /** + * Process payment invoices in background + */ + private fun processPaymentInvoices() { + 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<*, *>? + + if (result != null) { + val status = result["status"] + LOGGER.info("Payment invoice ${entry.value} status: $status") + + if (status != null && status.toString() == "true") { + LOGGER.info("Processing successful payment") + VpcApi.delete_invoice(entry.value) + + val amountLC = INVOICES_AMOUNT[entry.value] + + // Execute coin addition 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.error("Error executing coin addition command: ${e.message}") + e.printStackTrace() + } + }) + + // Clean up tracking maps + INVOICES_AMOUNT.remove(entry.key) + iterator.remove() + } + } else { + logger.warning("Received null result for payment invoice ${entry.value}") + } + } catch (e: Exception) { + LOGGER.error("Error processing payment invoice ${entry.value}: ${e.message}") + e.printStackTrace() + } + } + } + } } }