Ещё сырые наброски

This commit is contained in:
justuser-31 2025-11-23 15:37:35 +03:00
parent 298b961588
commit 1f00d04e87
6 changed files with 349 additions and 19 deletions

20
pom.xml
View File

@ -35,7 +35,7 @@
</execution>
</executions>
<configuration>
<jvmTarget>${java.version}</jvmTarget>
<jvmTarget>1.8</jvmTarget>
</configuration>
</plugin>
<plugin>
@ -106,10 +106,10 @@
<configuration>
<target>
<echo message="UPLOADING file to ${ftps.username}@${ftps.host} ${srv.ftps.remote.directory}"/>
<echo message="curl -k --ssl-reqd --ftp-create-dirs -T target/vpSecure-1.0.jar -u ${ftps.username}:${ftps.password} ftp://${ftps.host}:${ftps.port}/${srv.ftps.remote.directory}"/>
<echo message="curl -k --ssl-reqd --ftp-create-dirs -T target/vp_server_integration-1.0.jar ${ftps.username}:${ftps.password} ftp://${ftps.host}:${ftps.port}/${srv.ftps.remote.directory}"/>
<exec executable="sh">
<arg value="-c"/>
<arg value="curl -k --ssl-reqd --ftp-create-dirs -T target/vpSecure-1.0.jar -u ${ftps.username}:${ftps.password} ftp://${ftps.host}:${ftps.port}/${srv.ftps.remote.directory}"/>
<arg value="curl -k --ssl-reqd --ftp-create-dirs -T target/vp_server_integration-1.0.jar -u ${ftps.username}:${ftps.password} ftp://${ftps.host}:${ftps.port}/${srv.ftps.remote.directory}"/>
</exec>
</target>
</configuration>
@ -124,12 +124,18 @@
<phase>install</phase>
<configuration>
<target>
<echo message="SENDING restart request to ${srv.restart.endpoint}"/>
<echo message="curl -k -X POST -H 'Authorization: Bearer ${minihost.token}' ${srv.restart.endpoint}"/>
<echo message="SENDING reload plugin request to ${srv.restart.endpoint}"/>-->
<echo message="curl -k -X POST -H 'Authorization: Bearer ${minihost.token}' ${srv.command.endpoint} -d 'plugman reload vp_server_integration'"/>-->
<exec executable="sh">
<arg value="-c"/>
<arg value="curl -k -X POST -H 'Authorization: Bearer ${minihost.token}' ${srv.restart.endpoint}"/>
<arg value="-c"/>
<arg value="curl -k -X POST -H 'Authorization: Bearer ${minihost.token}' ${srv.command.endpoint} -d 'plugman reload vp_server_integration'"/>
</exec>
<!-- <echo message="SENDING restart request to ${srv.restart.endpoint}"/>-->
<!-- <echo message="curl -k -X POST -H 'Authorization: Bearer ${minihost.token}' ${srv.restart.endpoint}"/>-->
<!-- <exec executable="sh">-->
<!-- <arg value="-c"/>-->
<!-- <arg value="curl -k -X POST -H 'Authorization: Bearer ${minihost.token}' ${srv.restart.endpoint}"/>-->
<!-- </exec>-->
</target>
</configuration>
<goals>

View File

@ -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<out String>) = 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<org.bukkit.permissions.PermissionAttachmentInfo>()
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<String> {
val output = mutableListOf<String>()
val customSender = createCapturingSender { message ->
output.add(message)
}
Bukkit.dispatchCommand(customSender, command)
return output
}
}
}

View File

@ -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<String> = 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
}
}
}

View File

@ -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<out String>): 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<String>("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 <vpc/lc> <сумма>")
} 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<out String>): List<String> {
val completions = mutableListOf<String>()
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()
}
}

View File

@ -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()
)

View File

@ -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