logic
package me.spartacus04.instantrestock

import org.bukkit.persistence.PersistentDataType
import org.bukkit.persistence.PersistentDataAdapterContext

/**
 * Custom PersistentDataType implementation for converting between ByteArray and IntArray.
 */
class TradesDataType : PersistentDataType<ByteArray, IntArray> {
    /**
     * Returns the primitive type of the data.
     *
     * @return The primitive type, which is ByteArray in this case.
     */
    override fun getPrimitiveType(): Class<ByteArray> {
        return ByteArray::class.java
    }

    /**
     * Returns the complex type of the data.
     *
     * @return The complex type, which is IntArray in this case.
     */
    override fun getComplexType(): Class<IntArray> {
        return IntArray::class.java
    }

    /**
     * Converts the complex type (IntArray) to the primitive type (ByteArray).
     *
     * @param complex The complex type to convert.
     * @param context The PersistentDataAdapterContext.
     * @return The converted primitive type (ByteArray).
     */
    override fun toPrimitive(complex: IntArray, context: PersistentDataAdapterContext): ByteArray {
        val bytes = ByteArray(complex.size)

        for (i in complex.indices) {
            bytes[i] = complex[i].toByte()
        }

        return bytes
    }

    /**
     * Converts the primitive type (ByteArray) to the complex type (IntArray).
     *
     * @param primitive The primitive type to convert.
     * @param context The PersistentDataAdapterContext.
     * @return The converted complex type (IntArray).
     */
    override fun fromPrimitive(primitive: ByteArray, context: PersistentDataAdapterContext): IntArray {
        val ints = IntArray(primitive.size)

        for (i in primitive.indices) {
            ints[i] = primitive[i].toInt()
        }

        return ints
    }
}
package me.spartacus04.instantrestock

import me.spartacus04.colosseum.ColosseumPlugin
import me.spartacus04.colosseum.config.FileBind
import me.spartacus04.instantrestock.commands.MainCommand
import me.spartacus04.instantrestock.config.Config
import me.spartacus04.instantrestock.config.FieldLanguageMode
import me.spartacus04.instantrestock.listeners.PlayerJoinEvent
import me.spartacus04.instantrestock.listeners.VillagerEvent
import org.bstats.bukkit.Metrics
import org.bukkit.NamespacedKey

@Suppress("unused")
class InstantRestock : ColosseumPlugin() {
    override fun onEnable() {
        INSTANCE = this
        CONFIG = run {
            if (!dataFolder.exists()) dataFolder.mkdirs()
            FileBind.create(Config::class.java)
        }

        KEY = NamespacedKey(this, "instant_restock")

        buildI18nManager {
            loadInternalLanguageDirectory("langs")
            loadExternalLanguageFiles(dataFolder.resolve("lang.json"), "custom", "langs/en_US")
            setDefaultLocale("en_us")
            this.setLanguagesToLower(true)

            if(CONFIG.LANG == FieldLanguageMode.CUSTOM) {
                this.forceLanguage("custom")
            }
        }

        registerCommands {
            addCommands(MainCommand::class.java)
        }

        VillagerEvent(this).register()
        PlayerJoinEvent(this).register()

        if(CONFIG.ALLOW_METRICS)
            Metrics(this, 16589)

        if(CONFIG.CHECK_UPDATE)
            checkForUpdates("spartacus04/InstantRestock") {
                if(it != description.version) {
                    colosseumLogger.infoI18n(this, "update-available")
                    colosseumLogger.url("https://modrinth.com/plugin/infinite-villager-trading")
                }
            }

        colosseumLogger.confirm("Enabled InstantRestock!")
    }

    override fun onDisable() {
        colosseumLogger.warn("Disabled InstantRestock!")
    }

    companion object {
        lateinit var INSTANCE: InstantRestock
        lateinit var CONFIG : Config
        lateinit var KEY: NamespacedKey
    }
}
package me.spartacus04.instantrestock.listeners

import me.spartacus04.colosseum.ColosseumPlugin
import me.spartacus04.colosseum.listeners.ColosseumListener
import me.spartacus04.instantrestock.InstantRestock.Companion.CONFIG
import me.spartacus04.instantrestock.InstantRestock.Companion.KEY
import me.spartacus04.instantrestock.TradesDataType
import org.bukkit.entity.AbstractVillager
import org.bukkit.entity.EntityType
import org.bukkit.entity.Villager
import org.bukkit.event.EventHandler
import org.bukkit.event.entity.VillagerAcquireTradeEvent
import org.bukkit.event.player.PlayerInteractAtEntityEvent

internal class VillagerEvent(private val plugin: ColosseumPlugin) : ColosseumListener(plugin) {
    @EventHandler
    fun onPlayerInteractAtEntityEvent(e: PlayerInteractAtEntityEvent) {
        if(e.rightClicked !is AbstractVillager) return

        val merchant = e.rightClicked as AbstractVillager

        if(CONFIG.UNINSTALL_MODE) {
            if(merchant.persistentDataContainer.has(KEY, TradesDataType())) {
                restoreVillagerTrades(merchant)
                merchant.persistentDataContainer.remove(KEY)
            }

            return
        }

        if(merchant.type == EntityType.WANDERING_TRADER && !CONFIG.ALLOW_TRAVELLING_MERCHANTS) {
            if(merchant.persistentDataContainer.has(KEY, TradesDataType())) {
                restoreVillagerTrades(merchant)
                merchant.persistentDataContainer.remove(KEY)
            }

            return
        }

        if(merchant.type == EntityType.VILLAGER) {
            if(CONFIG.VILLAGER_BLACKLIST.contains(getProfessionKey(merchant as Villager).uppercase())) {
                if(merchant.persistentDataContainer.has(KEY, TradesDataType())) {
                    restoreVillagerTrades(merchant)
                    merchant.persistentDataContainer.remove(KEY)
                }

                return
            }
        }

        if(!merchant.persistentDataContainer.has(KEY, TradesDataType())) {
            saveVillagerTrades(merchant)
        }

        setMaxTrades(merchant, CONFIG.MAX_TRADES)
    }

    @EventHandler
    fun onVillagerUpgrade(e: VillagerAcquireTradeEvent) {
        val merchant = e.entity

        if(CONFIG.UNINSTALL_MODE) return
        if(merchant.type == EntityType.WANDERING_TRADER && !CONFIG.ALLOW_TRAVELLING_MERCHANTS) return

        if(merchant.persistentDataContainer.has(KEY, TradesDataType())) {
            restoreVillagerTrades(merchant)
            saveVillagerTrades(merchant)

            setMaxTrades(merchant, CONFIG.MAX_TRADES)
        }
    }

    private fun saveVillagerTrades(villager: AbstractVillager) {
        villager.persistentDataContainer.set(KEY, TradesDataType(), villager.recipes.map {
            it.maxUses
        }.toIntArray())
    }

    private fun restoreVillagerTrades(villager: AbstractVillager) {
        if(!villager.persistentDataContainer.has(KEY, TradesDataType())) return
        val trades = villager.persistentDataContainer.get(KEY, TradesDataType()) ?: return
        villager.recipes.forEachIndexed { i, r ->
            try {
                r.maxUses = trades[i]
            }
            catch (_: Exception) {
                return@forEachIndexed
            }
        }
    }

    private fun setMaxTrades(villager: AbstractVillager, maxTrades: Int) {
        villager.recipes.forEach {
            it.maxUses = if(maxTrades <= 0) {
                Int.MAX_VALUE
            } else {
                maxTrades
            }

            if(CONFIG.MAX_TRADES == Int.MAX_VALUE) it.uses = 0
            if(plugin.serverVersion >= "1.18" && CONFIG.DISABLE_PRICE_PENALTY) it.demand = 0
        }
    }

    /**
     * In API versions 1.20.6 and earlier, Villager.Profession is a class.
     * In versions 1.21 and later, it is an interface.
     * This method uses reflection to get the profession.key.key field of the villager.
     *
     * @param villager The villager to get the profession of.
     * @return The profession name of the villager.
     */
    private fun getProfessionKey(villager: Villager): String {
        val profession = villager::class.java.getMethod("getProfession").invoke(villager)
        val key = profession::class.java.getMethod("getKey").invoke(profession)
        val keyKey = key::class.java.getMethod("getKey").invoke(key)
        return keyKey.toString()
    }
}
package me.spartacus04.instantrestock.listeners

import me.spartacus04.colosseum.ColosseumPlugin
import me.spartacus04.colosseum.i18n.sendI18nInfo
import me.spartacus04.colosseum.listeners.ColosseumListener
import me.spartacus04.colosseum.logging.sendUrl
import me.spartacus04.instantrestock.InstantRestock.Companion.CONFIG
import org.bukkit.event.EventHandler
import org.bukkit.event.player.PlayerJoinEvent

internal class PlayerJoinEvent(private val plugin: ColosseumPlugin) : ColosseumListener(plugin) {
    @EventHandler
    fun onPlayerJoin(e: PlayerJoinEvent) {
        val player = e.player

        if(CONFIG.CHECK_UPDATE && player.hasPermission("instantrestock.notifyupdate"))
            plugin.checkForUpdates("spartacus04/InstantRestock") {
                if(it != plugin.description.version) {
                    player.sendI18nInfo(plugin, "update-available")
                    player.sendUrl(plugin, "https://modrinth.com/plugin/infinite-villager-trading")
                }
            }
    }
}
package me.spartacus04.instantrestock.commands

import me.spartacus04.colosseum.ColosseumPlugin
import me.spartacus04.colosseum.commandHandling.argument.arguments.ArgumentBoolean
import me.spartacus04.colosseum.commandHandling.argument.arguments.ArgumentString
import me.spartacus04.colosseum.commandHandling.command.ColosseumBaseCommand
import me.spartacus04.colosseum.commandHandling.command.ColosseumCommand
import me.spartacus04.colosseum.commandHandling.command.ColosseumNestedCommand
import me.spartacus04.colosseum.commandHandling.exceptions.MalformedArgumentException
import me.spartacus04.colosseum.i18n.sendI18nConfirm
import me.spartacus04.instantrestock.InstantRestock.Companion.CONFIG
import org.bukkit.command.CommandSender

class ConfigCommand(plugin: ColosseumPlugin) : ColosseumNestedCommand(plugin, "config", listOf(
    object : ColosseumCommand(plugin) {
        override val commandData = commandDescriptor("maxTrades") {
            arguments.add(ArgumentString(listOf("infinite", "1000", "100")))
        }

        override fun execute(ctx: CommandContext<CommandSender>) {
            val str = ctx.getArgument<String>(0).trim()

            if(str == "infinite") {
                CONFIG.MAX_TRADES = Int.MAX_VALUE
                CONFIG.save()

                ctx.sender.sendI18nConfirm(plugin, "config-saved")
            } else if (str.toIntOrNull() != null && str.toInt() > 0) {
                CONFIG.MAX_TRADES = str.toInt()
                CONFIG.save()

                ctx.sender.sendI18nConfirm(plugin, "config-saved")
            } else {
                throw MalformedArgumentException(str, "infinite or a positive number")
            }
        }
    },
    object : ColosseumCommand(plugin) {
        override val commandData = commandDescriptor("disablePricePenalty") {
            arguments.add(ArgumentBoolean())
        }

        override fun execute(ctx: CommandContext<CommandSender>) {
            val value = ctx.getArgument<Boolean>(0)

            CONFIG.DISABLE_PRICE_PENALTY = value
            CONFIG.save()

            ctx.sender.sendI18nConfirm(plugin, "config-saved")
        }
    },
    object : ColosseumCommand(plugin) {
        override val commandData = commandDescriptor("allowTravellingMerchants") {
            arguments.add(ArgumentBoolean())
        }

        override fun execute(ctx: CommandContext<CommandSender>) {
            val value = ctx.getArgument<Boolean>(0)

            CONFIG.ALLOW_TRAVELLING_MERCHANTS = value
            CONFIG.save()

            ctx.sender.sendI18nConfirm(plugin, "config-saved")
        }
    },
    object : ColosseumCommand(plugin) {
        override val commandData = commandDescriptor("uninstallMode") {
            arguments.add(ArgumentBoolean())
        }

        override fun execute(ctx: CommandContext<CommandSender>) {
            val value = ctx.getArgument<Boolean>(0)

            CONFIG.UNINSTALL_MODE = value
            CONFIG.save()

            ctx.sender.sendI18nConfirm(plugin, "config-saved")
        }
    },
    object : ColosseumBaseCommand(plugin, "villagerBlackList", listOf(
        object : ColosseumCommand(plugin) {
            override val commandData = commandDescriptor("add") {
                val remainingVillagers = villagerList.map { it.lowercase() }

                arguments.add(ArgumentString(remainingVillagers))
            }

            override fun execute(ctx: CommandContext<CommandSender>) {
                val profession = ctx.getArgument<String>(0).uppercase()

                if(!CONFIG.VILLAGER_BLACKLIST.contains(profession) && villagerList.contains(profession)) {
                    CONFIG.VILLAGER_BLACKLIST.add(profession)
                    CONFIG.save()

                    ctx.sender.sendI18nConfirm(plugin, "config-saved")
                } else {
                    throw MalformedArgumentException(ctx.getArgument<String>(0), "not already blacklisted profession")
                }
            }
        },
        object : ColosseumCommand(plugin) {
            override val commandData = commandDescriptor("remove") {
                val blacklistedVillagers = villagerList.map { it.lowercase() }

                arguments.add(ArgumentString(blacklistedVillagers))
            }

            override fun execute(ctx: CommandContext<CommandSender>) {
                val profession = ctx.getArgument<String>(0).uppercase()

                if(CONFIG.VILLAGER_BLACKLIST.contains(profession) && villagerList.contains(profession)) {
                    CONFIG.VILLAGER_BLACKLIST.remove(profession)
                    CONFIG.save()

                    ctx.sender.sendI18nConfirm(plugin, "config-saved")
                } else {
                    throw MalformedArgumentException(ctx.getArgument<String>(0), "already blacklisted profession")
                }
            }
        },
        object : ColosseumCommand(plugin) {
            override val commandData = commandDescriptor("list") {  }

            override fun execute(ctx: CommandContext<CommandSender>) {
                ctx.sender.sendMessage(CONFIG.VILLAGER_BLACKLIST.joinToString(", "))
            }
        }
    )) {}
)) {
    override val commandData = commandDescriptor("config") {
        permissions = setOf("instantrestock.config")
    }

    companion object {
        val villagerList = listOf(
            "ARMORER", "BUTCHER", "CARTOGRAPHER", "CLERIC", "FARMER", "FISHERMAN",
            "FLETCHER", "LEATHERWORKER", "LIBRARIAN", "MASON", "SHEPHERD", "TOOLSMITH",
            "WEAPONSMITH"
        )
    }
}
package me.spartacus04.instantrestock.commands

import me.spartacus04.colosseum.ColosseumPlugin
import me.spartacus04.colosseum.commandHandling.command.ColosseumBaseCommand

class MainCommand(plugin: ColosseumPlugin) : ColosseumBaseCommand(plugin, "instantrestock", listOf(
    ConfigCommand(plugin),
    ReloadCommand(plugin)
))
package me.spartacus04.instantrestock.commands

import me.spartacus04.colosseum.ColosseumPlugin
import me.spartacus04.colosseum.commandHandling.command.ColosseumCommand
import me.spartacus04.colosseum.i18n.sendI18nConfirm
import me.spartacus04.colosseum.i18n.sendI18nWarn
import me.spartacus04.instantrestock.InstantRestock.Companion.CONFIG
import me.spartacus04.instantrestock.config.FieldLanguageMode
import org.bukkit.command.CommandSender

class ReloadCommand(private val plugin: ColosseumPlugin) : ColosseumCommand(plugin) {
    override val commandData = commandDescriptor("reload") {
        description = "Reloads the plugin configuration"
        permissions = setOf("instantrestock.reload")
    }

    override fun execute(ctx: CommandContext<CommandSender>) {
        ctx.sender.sendI18nWarn(plugin, "reloading")

        CONFIG.read()
        plugin.i18nManager!!.forcedLanguage = if(CONFIG.LANG == FieldLanguageMode.DEFAULT) null else "custom"

        ctx.sender.sendI18nConfirm(plugin, "reloaded")
    }
}
package me.spartacus04.instantrestock.config

import com.google.gson.annotations.SerializedName
import me.spartacus04.colosseum.config.ConfigField
import me.spartacus04.colosseum.config.FileBind
import me.spartacus04.instantrestock.InstantRestock

/**
 * Represents the configuration for the plugin.
 *
 * @property MAX_TRADES The maximum number of trades allowed.
 * @property VILLAGER_BLACKLIST The list of blacklisted villagers.
 * @property DISABLE_PRICE_PENALTY Indicates if the price penalty is disabled.
 * @property UNINSTALL_MODE Indicates if the uninstall mode is enabled.
 * @property ALLOW_TRAVELLING_MERCHANTS Indicates if travelling merchants are allowed.
 * @property CHECK_UPDATE Indicates if checking for updates is allowed.
 * @property ALLOW_METRICS Indicates if metrics are allowed.
 */
@Suppress("PropertyName")
data class Config(
    @ConfigField(
        "Language",
        "The language mode of the plugin",
        "default"
    )
    @SerializedName("lang")
    var LANG: FieldLanguageMode = FieldLanguageMode.DEFAULT,

    @ConfigField(
        "Max trades per villager",
        "The maximum amount of trades a villager can have per restock",
        "0"
    )
    @SerializedName("maxTrades")
    var MAX_TRADES: Int = 0,

    @ConfigField(
        "Villager blacklist",
        "The list of professions that should not be restocked",
        "[]"
    )
    @SerializedName("villagerBlacklist")
    var VILLAGER_BLACKLIST: ArrayList<String> = ArrayList(),

    @ConfigField(
        "Disable price penalty",
        "Indicates if the price penalty should be disabled",
        "true"
    )
    @SerializedName("disablePricePenalty")
    var DISABLE_PRICE_PENALTY: Boolean = true,

    @ConfigField(
        "Uninstall mode",
        "Indicates if the plugin should be in uninstall mode",
        "false"
    )
    @SerializedName("uninstallMode")
    var UNINSTALL_MODE: Boolean = false,

    @ConfigField(
        "Allow travelling merchants",
        "Indicates if travelling merchants are allowed",
        "true"
    )
    @SerializedName("allowTravellingMerchants")
    var ALLOW_TRAVELLING_MERCHANTS: Boolean = true,

    @ConfigField(
        "Check for updates",
        "Indicates if the plugin should check for updates",
        "true"
    )
    @SerializedName("checkUpdate")
    var CHECK_UPDATE: Boolean = true,

    @ConfigField(
        "Allow metrics",
        "Indicates if metrics are allowed",
        "true"
    )
    @SerializedName("allowMetrics")
    var ALLOW_METRICS: Boolean = true
) : FileBind("config.json", Config::class.java, InstantRestock.INSTANCE) {
    @Suppress("unused")
    @SerializedName($$"$schema")
    private val schema = "https://raw.githubusercontent.com/spartacus04/InstantRestock/master/configSchema.json"
}
package me.spartacus04.instantrestock.config

import com.google.gson.annotations.SerializedName

enum class FieldLanguageMode {
    @SerializedName("default")
    DEFAULT,

    @SerializedName("custom")
    CUSTOM;
}
Now for insturctions on what to do with this code:
Make a messages.yml to customize all messages sent by the plugin. The messages.yml should be placed in the plugin's data
And dont change logic and just move around files have these configs

    Max Trades Configuration: Customize the maximum number of trades a villager can offer using the /instantrestock config maxTrades <number/infinite> command.

    Price Penalty Control: Enable or disable the price penalty if the Minecraft version is greater than or equal to 1.18 using the /instantrestock config disablePricePenalty <true/false> command.

    Uninstall Mode: Toggle uninstall mode on or off using the /instantrestock config uninstallMode <true/false> command.

    Travelling Merchants: Enable or disable infinite trades for travelling merchants with the /instantrestock config allowTravellingMerchants <true/false> command.

    Villager Blacklist: Add, remove, or list villagers in the blacklist using the /instantrestock config villagerBlacklist <add/remove/list> <villagertype> command.
Configuration

Use the following commands to configure the plugin:

    /instantrestock config maxTrades <number/infinite>: Set the maximum number of trades a villager can offer.

    /instantrestock config disablePricePenalty <true/false>: Enable or disable the price penalty for Minecraft versions >= 1.18.

    /instantrestock config uninstallMode <true/false>: Toggle uninstall mode on or off.

    /instantrestock config allowTravellingMerchants <true/false>: Enable or disable infinite trades for travelling merchants.

    /instantrestock config villagerBlacklist <add/remove/list> <villagertype>: Manage the villager blacklist.

Reloading the Plugin

    /instantrestock reload: Reload the plugin from the config file.

Permissions

    instantrestock.reload: Allows players to use the /instantrestock reload command.

    instantrestock.config: Allows players to use the configuration commands listed above.
Overview

The InstantRestock plugin is designed to enhance your Minecraft server by providing instant restocking of villager trades. With this plugin, you can easily configure various aspects of trade mechanics to suit your gameplay style.

remove all instances of the old creator because new account instead say StarRinth and update the links to the plugin page to https://modrinth.com/plugin/instarestock
and dont use term infinite villiager trading instead use instarestock and theme color is blue and white no bold and sharp symbols if used and clean like blue for titles and white(white but with a tent of blue) for text.
