diff --git a/pom.xml b/pom.xml
index 03f4569..02b0217 100644
--- a/pom.xml
+++ b/pom.xml
@@ -35,7 +35,7 @@
- ${java.version}
+ 1.8
@@ -106,10 +106,10 @@
-
+
-
+
@@ -124,12 +124,18 @@
install
-
-
+ -->
+ -->
-
-
+
+
+
+
+
+
+
+
diff --git a/src/main/kotlin/main/vp_server_integration/CommandCapture.kt b/src/main/kotlin/main/vp_server_integration/CommandCapture.kt
new file mode 100644
index 0000000..3930347
--- /dev/null
+++ b/src/main/kotlin/main/vp_server_integration/CommandCapture.kt
@@ -0,0 +1,49 @@
+package main.vp_server_integration
+
+// -------------- Capture output ----------------
+// Here is implemented capture of output command (for dynamic course)
+// ----------------------------------------------
+
+import org.bukkit.Bukkit
+import org.bukkit.command.CommandSender
+
+class CommandCapture {
+ companion object {
+ private fun createCapturingSender(onMessage: (String) -> Unit): CommandSender {
+ return object : CommandSender {
+ override fun sendMessage(message: String) = onMessage(message)
+ override fun sendMessage(messages: Array) = messages.forEach(onMessage)
+ override fun getName() = "VPCIntegrationCapture"
+ override fun isOp() = true
+ override fun setOp(value: Boolean) {}
+ override fun hasPermission(permission: String) = true
+ override fun hasPermission(permission: org.bukkit.permissions.Permission) = true
+ override fun getServer() = Bukkit.getServer()
+ override fun addAttachment(plugin: org.bukkit.plugin.Plugin) = null
+ override fun addAttachment(plugin: org.bukkit.plugin.Plugin, ticks: Int) = null
+ override fun addAttachment(plugin: org.bukkit.plugin.Plugin, name: String, value: Boolean) = null
+ override fun addAttachment(plugin: org.bukkit.plugin.Plugin, name: String, value: Boolean, ticks: Int) =
+ null
+
+ override fun removeAttachment(attachment: org.bukkit.permissions.PermissionAttachment) {}
+ override fun recalculatePermissions() {}
+ override fun getEffectivePermissions() = emptySet()
+ override fun isPermissionSet(name: String) = true
+ override fun isPermissionSet(perm: org.bukkit.permissions.Permission) = true
+ override fun spigot() = object : CommandSender.Spigot() {}
+ }
+ }
+
+ fun execute(command: String): List {
+ val output = mutableListOf()
+
+ val customSender = createCapturingSender { message ->
+ output.add(message)
+ }
+
+ Bukkit.dispatchCommand(customSender, command)
+
+ return output
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/main/vp_server_integration/TotalBalanceModules.kt b/src/main/kotlin/main/vp_server_integration/TotalBalanceModules.kt
new file mode 100644
index 0000000..83990a8
--- /dev/null
+++ b/src/main/kotlin/main/vp_server_integration/TotalBalanceModules.kt
@@ -0,0 +1,24 @@
+package main.vp_server_integration
+
+import main.vp_server_integration.Vp_server_integration.Companion.LOGGER
+
+class TotalBalanceModules {
+ companion object {
+ fun getEssentialsBalance(): Float {
+ val output: List = CommandCapture.execute("baltop")
+ var total: Float = 0.0f
+ var startOfList: Boolean = false
+ for (el in output) {
+ if (startOfList) {
+ LOGGER.info(el.split(" ")[2].replace(Regex("[^0-9.]"), ""))
+ total += el.split(" ")[2].replace(Regex("[^0-9.]"), "").toFloat()
+ } else if (el.contains("1.")) {
+ startOfList = true
+ LOGGER.info(el.split(" ")[2].replace(Regex("[^0-9.]"), ""))
+ total += el.split(" ")[2].replace(Regex("[^0-9.]"), "").toFloat()
+ }
+ }
+ return total
+ }
+ }
+}
\ No newline at end of file
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 0b23f4b..e893fcd 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
@@ -1,29 +1,162 @@
package main.vp_server_integration
+// ---------------- MAIN FILE -------------------
+// Here you will see the main logic
+// ----------------------------------------------
+
+import main.vp_server_integration.VpcApi
import org.bukkit.plugin.java.JavaPlugin
import org.bukkit.Bukkit
import java.util.logging.Logger
+import org.bukkit.command.PluginCommand
+import org.bukkit.command.Command
+import org.bukkit.command.CommandExecutor
+import org.bukkit.command.CommandSender
+import org.bukkit.entity.Player
+import net.md_5.bungee.api.chat.ComponentBuilder
+import net.md_5.bungee.api.chat.HoverEvent
+import net.md_5.bungee.api.chat.ClickEvent
+import net.md_5.bungee.api.chat.TextComponent
+import org.bukkit.ChatColor
+import org.bukkit.permissions.Permission
+import org.bukkit.permissions.PermissionAttachment
+import org.bukkit.permissions.PermissionAttachmentInfo
+import org.bukkit.plugin.Plugin
+import java.io.File
+import java.io.FileInputStream
+import java.nio.file.Files
+import java.util.Properties
+import java.util.UUID
+import kotlin.collections.get
+import kotlin.math.abs
-class Vp_server_integration : JavaPlugin() {
+class Vp_server_integration() : JavaPlugin(), CommandExecutor {
companion object {
lateinit var LOGGER: Logger
lateinit var SERVER: org.bukkit.Server
- var PLUGIN_API_PORT: Int = 8010
lateinit var USERNAME: String
- lateinit var TOKEN: String
+ lateinit var USER_TOKEN: String
lateinit var USER_API_URL: String
+ lateinit var COMMAND_ADD_COINS: String
+ lateinit var COMMAND_REMOVE_MODE: String
+ lateinit var COMMAND_REMOVE_COINS: String
+ lateinit var COMMAND_REMOVE_ERROR: String
+ lateinit var COURSE_MODE: String
+ var COURSE_STATIC_VALUE: Float = DEFAULT_COURSE_STATIC_VALUE
+ lateinit var COURSE_DYNAMIC_COMMAND: String
+ var COURSE_COMMISSION: Float = DEFAULT_COURSE_COMMISSION
+ const val DEFAULT_USERNAME: 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_TOKEN: String = "test"
- const val COLOR_CHAR: Char = '&'
+ const val DEFAULT_COMMAND_ADD_COINS: String = "eco give %player% %amount%"
+ const val DEFAULT_COMMAND_REMOVE_MODE: String = "console"
+ const val DEFAULT_COMMAND_REMOVE_COINS: String = "eco take %player% %amount%"
+ const val DEFAULT_COMMAND_REMOVE_ERROR: String = "Error:"
+ const val DEFAULT_COURSE_MODE: String = "dynamic"
+ const val DEFAULT_COURSE_STATIC_VALUE: Float = 1000.0f
+ const val DEFAULT_COURSE_DYNAMIC_COMMAND: String = "baltop force"
+ const val DEFAULT_COURSE_COMMISSION: Float = 5.0f
+ }
+
+ private fun loadConfiguration() {
+ try {
+ // In Spigot, we use getDataFolder() instead of dataDirectory
+ val dataFolder = getDataFolder()
+ if (!dataFolder.exists()) {
+ dataFolder.mkdirs()
+ }
+
+ val configFile = File(dataFolder, "config.properties")
+ if (!configFile.exists()) {
+ // Create default config file
+ configFile.writeText(
+ """
+# VPC Integration Configuration
+
+# ---------- Part for work with UserAPI -----------
+# Username from your VPC wallet
+username=$DEFAULT_USERNAME
+
+# Token for UserAPI
+user_token=$DEFAULT_USER_TOKEN
+
+# UserAPI URL
+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'
+
+# From who we will run remove coins command? (player/console)
+command_remove_mode=$DEFAULT_COMMAND_REMOVE_MODE
+
+# Which command run to remove coins?
+command_remove_coins='$DEFAULT_COMMAND_REMOVE_COINS'
+
+# What shouldn't be in response after executing command
+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
+
+# Course VPC to LC (Local Currency)
+course_static_value=$DEFAULT_COURSE_STATIC_VALUE
+
+# Which command will run for getting global balance
+# If command not produce CLEAR Float/Int like 32.15 - do not set this
+# For UNCLEAR global balance we need module
+# Supported modules: baltop (Essentials)
+course_dynamic_command='$DEFAULT_COURSE_DYNAMIC_COMMAND'
+
+# For dynamic course recommended 5% and higher (avoid dupe), for static course you can set 0%
+course_commission=$DEFAULT_COURSE_COMMISSION
+# -------------------- END ------------------------
+""".trimIndent()
+ )
+ }
+
+ // Load properties
+ val properties = Properties()
+ FileInputStream(configFile).use { properties.load(it) }
+
+ USERNAME = properties.getProperty("username", DEFAULT_USERNAME)
+ USER_TOKEN = properties.getProperty("user_token", DEFAULT_USER_TOKEN)
+ USER_API_URL = properties.getProperty("user_api_url", DEFAULT_USER_API_URL)
+ COMMAND_ADD_COINS = properties.getProperty("command_add_coins", DEFAULT_COMMAND_ADD_COINS)
+ COMMAND_REMOVE_MODE = properties.getProperty("command_remove_mode", DEFAULT_COMMAND_REMOVE_MODE)
+ COMMAND_REMOVE_COINS = properties.getProperty("command_remove_coins", DEFAULT_COMMAND_REMOVE_COINS)
+ COMMAND_REMOVE_ERROR = properties.getProperty("command_remove_error", DEFAULT_COMMAND_REMOVE_ERROR)
+ COURSE_MODE = properties.getProperty("course_mode", DEFAULT_COURSE_MODE)
+ COURSE_STATIC_VALUE = properties.getProperty("course_static_value", DEFAULT_COURSE_STATIC_VALUE.toString()).toFloat()
+ COURSE_DYNAMIC_COMMAND = properties.getProperty("course_dynamic_command", DEFAULT_COURSE_DYNAMIC_COMMAND)
+ COURSE_COMMISSION = properties.getProperty("course_commission", DEFAULT_COURSE_COMMISSION.toString()).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() {
// Initialize logger
LOGGER = this.logger
-
// Get server instance
SERVER = Bukkit.getServer()
+ // Load configuration
+ loadConfiguration()
+
+ getCommand("vpi")?.setExecutor(this)
+
// Plugin startup logic
LOGGER.info("VP Server Integration plugin enabled!")
}
@@ -32,4 +165,112 @@ class Vp_server_integration : JavaPlugin() {
// Plugin shutdown logic
LOGGER.info("VP Server Integration plugin disabled!")
}
+
+ 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Только игроки могут выполнять команды.")
+ return true
+ }
+// val mess = CommandCapture.execute("baltop")
+// VpcApi.send(sender, TotalBalanceModules.getEssentialsBalance().toString())
+// return true
+
+ if (args.size == 3 && args[0] == "convert") {
+ val direction = args[1]
+ val amount = abs(args[2].toFloat())
+ if (!listOf("vpc", "lc").contains(direction)) {
+ VpcApi.send(sender, "&cНе существует такого направление, правильные: vpc/lc")
+ return true
+ }
+
+ val course: Float // VPC to LC
+ if (COURSE_MODE == "static") {
+ course = COURSE_STATIC_VALUE
+ } else {
+ if (COURSE_DYNAMIC_COMMAND == "baltop") {
+ val globalBalance = TotalBalanceModules.getEssentialsBalance()
+ val vpcUser = VpcApi.user_in_db(username=USERNAME)
+ val vpcBalance: Float
+ if (vpcUser == null) {
+ throw Exception("null vpcUser")
+ }
+ vpcBalance = vpcUser["balance"] as Float
+ course = globalBalance/vpcBalance
+ } else {
+ val globalBalance = CommandCapture.execute(COURSE_DYNAMIC_COMMAND)[0].toFloat()
+ val vpcUser = VpcApi.user_in_db(username=USERNAME)
+ val vpcBalance: Float
+ if (vpcUser == null) {
+ throw Exception("null vpcUser")
+ }
+ vpcBalance = vpcUser["balance"] as Float
+ course = globalBalance/vpcBalance
+ }
+ }
+
+ if (direction == "vpc") {
+ val result: String = CommandCapture.execute(COURSE_DYNAMIC_COMMAND)[0]
+ if (result.contains(COMMAND_REMOVE_ERROR)) {
+ VpcApi.send(sender, "&cОшибка, возможно недостаточно средств.")
+ return true
+ }
+// VpcApi.transfer_coins()
+ } else if (direction == "lc") {
+
+ }
+ } else if (args.isNotEmpty() && args[0] == "convert") {
+ VpcApi.send(sender, "/vpi convert <сумма>")
+ } else {
+ VpcApi.send(
+ sender, """Использование команд:
+/vpi convert <куда: vpc/lc> <сумма> - Обмен VPC на локальную валюту или наоборот
+
+Почему 'VPC-I'? Потому что это интеграция на конечном сервере - 'VPC Integration'
+
+Соглашение: voidproject.del.pw/vpc_agreement
+Группа ТГ: @void_project_mc
+Группа ДС: discord.gg/zwNt5DJj6J
+""".trimIndent()
+ )
+ }
+
+ 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 true
+ }
+ return false
+ }
+
+ override fun onTabComplete(sender: CommandSender, command: Command, alias: String, args: Array): List {
+ val completions = mutableListOf()
+ if (command.name.equals("vpi", ignoreCase = true)) {
+ when (args.size) {
+ 1 -> {
+ completions.addAll(listOf("help", "convert"))
+ }
+ 2 -> {
+ if (args[0].equals("convert", ignoreCase = true)) {
+ completions.add("<куда: vpc/lc>")
+ }
+ }
+ 3 -> {
+ if (args[0].equals("convert", ignoreCase = true)) {
+ completions.add("<сумма>")
+ }
+ }
+ }
+ }
+
+ return completions.filter { it.startsWith(args.lastOrNull()?.lowercase() ?: "") }.sorted()
+ }
}
diff --git a/src/main/kotlin/main/vp_server_integration/VpcApi.kt b/src/main/kotlin/main/vp_server_integration/VpcApi.kt
index a885f81..af9b286 100644
--- a/src/main/kotlin/main/vp_server_integration/VpcApi.kt
+++ b/src/main/kotlin/main/vp_server_integration/VpcApi.kt
@@ -1,9 +1,15 @@
package main.vp_server_integration
+// ----------------- VPC API --------------------
+// Here is implemented calls to VPC UserAPI
+// ----------------------------------------------
+
import com.google.gson.Gson
import com.google.gson.JsonElement
import com.google.gson.JsonParser
import com.google.gson.JsonSyntaxException
+import main.vp_server_integration.Vp_server_integration.Companion.USERNAME
+import main.vp_server_integration.Vp_server_integration.Companion.USER_TOKEN
import org.bukkit.ChatColor
import org.bukkit.command.CommandSender
import org.bukkit.entity.Player
@@ -104,7 +110,7 @@ class VpcApi {
}
}
- fun send(source: CommandSender, message: String, prefix: String = "&9[&bVPC&9] &3") {
+ fun send(source: CommandSender, message: String, prefix: String = "&9[&bVPC&7-&6I&9] &3") {
// Translate color codes for Spigot
val coloredMessage = ChatColor.translateAlternateColorCodes('&', prefix + message)
source.sendMessage(coloredMessage)
@@ -128,14 +134,14 @@ class VpcApi {
// Determine which parameter to use
val userParam = when {
username != null -> "username" to username
- source is Player -> "username" to source.name
+ source is Player -> "mine_name" to source.name
else -> throw IllegalArgumentException("Either source or username must be provided")
}
val response = sendPost(
"user_in_db/", jsonify(
listOf(
- "token", Vp_server_integration.DEFAULT_TOKEN,
+ "token", Vp_server_integration.DEFAULT_USER_TOKEN,
userParam.first, userParam.second
)
)
@@ -149,12 +155,12 @@ class VpcApi {
}
}
- fun transfer_coins(src_username: String, dst_username: String, amount: Float): Any? {
+ fun transfer_coins(dst_username: String, amount: Float): Any? {
val response = sendPost(
"transfer_coins/", jsonify(
listOf(
- "token", Vp_server_integration.DEFAULT_TOKEN,
- "src_username", src_username,
+ "user_token", USER_TOKEN,
+ "src_username", USERNAME,
"dst_username", dst_username,
"amount", amount.toString()
)
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index 38801bd..a3674f5 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -5,3 +5,7 @@ load: STARTUP
authors: [ _SAN5_SkeLet0n_ ]
description: Integrate VPC into your server
website: voidproject.del.pw
+commands:
+ vpi:
+ description: Main command
+ usage: /vpi
\ No newline at end of file