new gui and new saving system

This commit is contained in:
rattatwinko
2025-04-27 20:25:47 +02:00
parent 1b31c125f9
commit c6ee292a8b
6 changed files with 263 additions and 222 deletions

View File

@@ -1,244 +1,259 @@
package org.server_info.MOTD
import MOTDGuiHandler
import org.bukkit.Bukkit
import org.bukkit.ChatColor
import org.bukkit.configuration.file.YamlConfiguration
import org.bukkit.plugin.java.JavaPlugin
import org.bukkit.configuration.ConfigurationSection
import java.io.File
import java.util.*
class MOTD : JavaPlugin() {
private var quotesTask: Int = -1
private val quotesList = ArrayList<QuoteItem>()
// Quote data class to store text and color
private val quotes = ArrayList<QuoteItem>()
private var currentQuoteIndex = 0
private var quoteTaskId = -1
data class QuoteItem(val text: String, val color: String)
override fun onEnable() {
// Save default config if it doesn't exist
// Create default config if it doesn't exist
saveDefaultConfig()
// Initialize quotes system
initializeQuotes()
// Register command executor
getCommand("motd")?.setExecutor(MOTDCommand(this))
// Register GUI handler
val guiHandler = MOTDGuiHandler(this)
server.pluginManager.registerEvents(guiHandler, this)
logger.info("MOTD Plugin enabled!")
}
override fun onDisable() {
// Cancel task if running
if (quoteTaskId != -1) {
Bukkit.getScheduler().cancelTask(quoteTaskId)
}
// Save quotes on shutdown
saveQuotes()
logger.info("MOTD Plugin disabled!")
}
// Initialize quotes system
fun initializeQuotes() {
// Make sure the data folder exists
if (!dataFolder.exists()) {
dataFolder.mkdirs()
}
// Load quotes from config
loadQuotes()
// Register commands
getCommand("motd")?.setExecutor(MOTDCommand(this))
// Register GUI handler
server.pluginManager.registerEvents(MOTDGuiHandler(this), this)
// Schedule the MOTD update task
// Start the task to change quotes
startQuoteTask()
logger.info("MOTD Quote Plugin enabled! Loaded ${quotesList.size} quotes.")
}
override fun onDisable() {
// Cancel the task when the plugin is disabled
if (quotesTask != -1) {
Bukkit.getScheduler().cancelTask(quotesTask)
}
logger.info("MOTD Quote Plugin disabled!")
}
// Save quotes to config
fun saveQuotes() {
try {
val config = YamlConfiguration()
val quotesList = ArrayList<Map<String, String>>()
private fun loadQuotes() {
// Clear existing quotes
quotesList.clear()
// Check if we have the new quote format
if (config.contains("quotes") && config.isList("quotes")) {
// Check if it's the old format (simple string list) or new format (list of maps)
val quotesSection = config.getList("quotes")
if (quotesSection != null && quotesSection.isNotEmpty()) {
if (quotesSection[0] is String) {
// Old format - migrate
migrateOldQuotesFormat()
} else {
// New format - load quotes with colors
for (i in 0 until config.getList("quotes")!!.size) {
val quoteSection = config.getConfigurationSection("quotes.$i")
if (quoteSection != null) {
val text = quoteSection.getString("text") ?: continue
val color = quoteSection.getString("color") ?: "§f" // Default to white
quotesList.add(QuoteItem(text, color))
}
}
}
for (quote in quotes) {
val quoteMap = HashMap<String, String>()
quoteMap["text"] = quote.text
quoteMap["color"] = quote.color.replace('§', '&') // Store with & instead of § for readability
quotesList.add(quoteMap)
}
} else {
// Create default quotes
createDefaultQuotes()
}
// If still empty after loading, create defaults
if (quotesList.isEmpty()) {
createDefaultQuotes()
config.set("quotes", quotesList)
// Save to file
val file = File(dataFolder, "quotes.yml")
config.save(file)
// Update the main config to point to this file
val mainConfig = YamlConfiguration.loadConfiguration(File(dataFolder, "config.yml"))
mainConfig.set("quotes_file", "quotes.yml")
mainConfig.save(File(dataFolder, "config.yml"))
logger.info("Saved ${quotes.size} quotes to quotes.yml")
} catch (e: Exception) {
logger.severe("Failed to save quotes: ${e.message}")
e.printStackTrace()
}
}
private fun migrateOldQuotesFormat() {
val oldQuotes = config.getStringList("quotes")
val newQuotesList = ArrayList<Map<String, String>>()
// Load quotes from config
fun loadQuotes() {
try {
val configFile = File(dataFolder, "config.yml")
if (!configFile.exists()) {
saveDefaultConfig()
}
for (quote in oldQuotes) {
newQuotesList.add(mapOf("text" to quote, "color" to "§f"))
val config = YamlConfiguration.loadConfiguration(configFile)
val quotesFile = config.getString("quotes_file", "quotes.yml")
val quotesConfig = File(dataFolder, quotesFile)
if (!quotesConfig.exists()) {
// Create default quotes file if it doesn't exist
val defaultQuotes = ArrayList<Map<String, String>>()
val defaultQuote = HashMap<String, String>()
defaultQuote["text"] = "Welcome to our server!"
defaultQuote["color"] = "&e"
defaultQuotes.add(defaultQuote)
val tempConfig = YamlConfiguration()
tempConfig.set("quotes", defaultQuotes)
tempConfig.save(quotesConfig)
}
// Load quotes
val quotesYaml = YamlConfiguration.loadConfiguration(quotesConfig)
val quotesList = quotesYaml.getMapList("quotes")
quotes.clear()
for (quoteMap in quotesList) {
@Suppress("UNCHECKED_CAST")
val map = quoteMap as? Map<String, String> ?: continue
val text = map["text"] ?: continue
val color = (map["color"] ?: "&f").replace('&', '§')
quotes.add(QuoteItem(text, color))
}
logger.info("Loaded ${quotes.size} quotes from $quotesFile")
} catch (e: Exception) {
logger.severe("Failed to load quotes: ${e.message}")
e.printStackTrace()
// Create a default quote if loading fails
if (quotes.isEmpty()) {
quotes.add(QuoteItem("Welcome to our server!", "§e"))
}
}
// Save in new format
config.set("quotes", newQuotesList)
saveConfig()
// Load the quotes
for (quoteMap in newQuotesList) {
quotesList.add(QuoteItem(quoteMap["text"] ?: "", quoteMap["color"] ?: "§f"))
}
logger.info("Migrated ${oldQuotes.size} quotes to the new format with colors.")
}
private fun createDefaultQuotes() {
val defaultQuotes = listOf(
QuoteItem("Welcome to our Minecraft server!", "§e"),
QuoteItem("Have fun and be respectful to others!", "§a"),
QuoteItem("Check out our website for server rules.", "§b")
)
quotesList.addAll(defaultQuotes)
// Save default quotes to config
saveQuotesToConfig()
logger.info("No quotes found in config, created default quotes list.")
}
// Start the task to change quotes periodically
fun startQuoteTask() {
// Cancel existing task if running
if (quotesTask != -1) {
Bukkit.getScheduler().cancelTask(quotesTask)
if (quoteTaskId != -1) {
Bukkit.getScheduler().cancelTask(quoteTaskId)
quoteTaskId = -1
}
// Get update interval from config (default: 60 minutes)
val intervalMinutes = config.getLong("update-interval-minutes", 60)
// Get update interval from config (in minutes)
val intervalMinutes = config.getInt("update_interval", 15)
val intervalTicks = intervalMinutes * 60 * 20L // Convert to ticks
// Schedule repeating task
quotesTask = Bukkit.getScheduler().scheduleSyncRepeatingTask(
this,
{ updateServerMOTD() },
20L, // Initial delay (1 second)
intervalMinutes * 60 * 20L // Convert minutes to ticks (20 ticks = 1 second)
)
logger.info("MOTD update task scheduled to run every $intervalMinutes minutes.")
}
fun updateServerMOTD() {
if (quotesList.isEmpty()) {
logger.warning("No quotes available to set MOTD!")
if (intervalMinutes <= 0) {
logger.info("Automatic MOTD updates disabled (interval set to 0)")
return
}
// Get a random quote
val quoteItem = getRandomQuote()
quoteTaskId = Bukkit.getScheduler().scheduleSyncRepeatingTask(this, {
updateServerMOTD()
}, intervalTicks, intervalTicks)
// Format the quote with its specific color
val formattedQuote = formatMOTD(quoteItem)
// Set the server MOTD
val server = Bukkit.getServer()
server.motd = formattedQuote
logger.info("MOTD updated: $formattedQuote")
logger.info("MOTD will update every $intervalMinutes minutes")
}
fun getRandomQuote(): QuoteItem {
return if (quotesList.isNotEmpty()) {
quotesList.random()
// Update the server MOTD with the next quote
fun updateServerMOTD() {
if (quotes.isEmpty()) {
logger.warning("No quotes available for MOTD")
return
}
// Select next quote or random based on config
val useRandom = config.getBoolean("random_quotes", true)
if (useRandom) {
currentQuoteIndex = Random().nextInt(quotes.size)
} else {
QuoteItem("No quotes available", "§c")
currentQuoteIndex = (currentQuoteIndex + 1) % quotes.size
}
val quote = quotes[currentQuoteIndex]
// Get the static part of the MOTD from config
val staticPart = ChatColor.translateAlternateColorCodes('&',
config.getString("static_motd", "&6Crossplay Java & Bedrock") ?: "&6Crossplay Java & Bedrock!")
// Combine static part with quote
val fullMotd = "$staticPart\n${quote.color}${quote.text}"
try {
// Update the server MOTD
val server = Bukkit.getServer()
val serverClass = server.javaClass
// Attempt to access Spigot/Paper setMotd method first
try {
val motdMethod = serverClass.getMethod("setMotd", String::class.java)
motdMethod.invoke(server, fullMotd)
logger.info("Updated MOTD: $fullMotd")
return
} catch (e: NoSuchMethodException) {
// Method not found, try other ways
}
// Try to find the method in a different way for different server implementations
for (method in serverClass.methods) {
if (method.name.equals("setMotd", ignoreCase = true) && method.parameterCount == 1) {
method.invoke(server, fullMotd)
logger.info("Updated MOTD: $fullMotd")
return
}
}
// If we get here, warn that the MOTD couldn't be updated
logger.warning("Could not update server MOTD. Server implementation not supported.")
} catch (e: Exception) {
logger.severe("Error updating server MOTD: ${e.message}")
e.printStackTrace()
}
}
private fun formatMOTD(quoteItem: QuoteItem): String {
// Get prefix and suffix from config
val prefix = config.getString("motd-prefix", "") ?: ""
val suffix = config.getString("motd-suffix", "") ?: ""
// Apply the quote's specific color
val coloredQuote = quoteItem.color + quoteItem.text
// Trim and limit the quote length if it's too long
val maxLength = config.getInt("max-motd-length", 50)
var trimmedQuote = coloredQuote
if (trimmedQuote.length > maxLength) {
trimmedQuote = trimmedQuote.substring(0, maxLength - 3) + "..."
}
return prefix + trimmedQuote + suffix
}
fun reloadQuotes() {
reloadConfig()
loadQuotes()
}
// Get the current list of quotes
fun getCurrentQuotes(): List<QuoteItem> {
return quotesList.toList()
return quotes.toList()
}
// Add a new quote
fun addQuote(text: String, color: String): Boolean {
if (text.isBlank()) return false
// Validate color code
val validColor = if (color.startsWith("§") && color.length == 2) {
color
} else {
"§f" // Default to white if invalid
}
// Add to internal list
quotesList.add(QuoteItem(text, validColor))
// Save to config
saveQuotesToConfig()
quotes.add(QuoteItem(text, color))
return true
}
// Remove a quote by index
fun removeQuote(index: Int): Boolean {
if (index < 0 || index >= quotesList.size) return false
quotesList.removeAt(index)
saveQuotesToConfig()
if (index < 0 || index >= quotes.size) return false
quotes.removeAt(index)
return true
}
// Edit an existing quote
fun editQuote(index: Int, text: String, color: String): Boolean {
if (index < 0 || index >= quotesList.size || text.isBlank()) return false
// Validate color code
val validColor = if (color.startsWith("§") && color.length == 2) {
color
} else {
"§f" // Default to white if invalid
}
quotesList[index] = QuoteItem(text, validColor)
saveQuotesToConfig()
if (index < 0 || index >= quotes.size || text.isBlank()) return false
quotes[index] = QuoteItem(text, color)
return true
}
private fun saveQuotesToConfig() {
// Convert quotes to format that can be saved in config
val quotesToSave = ArrayList<Map<String, String>>()
for (quote in quotesList) {
quotesToSave.add(mapOf(
"text" to quote.text,
"color" to quote.color
))
}
// Save to config
config.set("quotes", quotesToSave)
saveConfig()
// Reload quotes from config
fun reloadQuotes() {
loadQuotes()
updateServerMOTD() // Update the MOTD immediately
}
}

View File

@@ -1,5 +1,3 @@
package org.server_info.MOTD
import org.bukkit.Bukkit
import org.bukkit.ChatColor
import org.bukkit.Material
@@ -9,7 +7,7 @@ import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.InventoryCloseEvent
import org.bukkit.inventory.Inventory
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.ItemMeta
import org.server_info.MOTD.MOTD
import java.util.*
import org.bukkit.conversations.*
import org.bukkit.entity.Player
@@ -67,6 +65,13 @@ class MOTDGuiHandler(private val plugin: MOTD) : Listener {
refreshButton.itemMeta = refreshMeta
}
val saveButton = ItemStack(Material.DIAMOND_BLOCK) // New save button
val saveMeta = saveButton.itemMeta
if (saveMeta != null) {
saveMeta.setDisplayName("${ChatColor.BLUE}Save Quotes")
saveButton.itemMeta = saveMeta
}
val exitButton = ItemStack(Material.REDSTONE_BLOCK)
val exitMeta = exitButton.itemMeta
if (exitMeta != null) {
@@ -76,9 +81,10 @@ class MOTDGuiHandler(private val plugin: MOTD) : Listener {
// Set buttons in the last row
val lastRowStart = inventorySize - 9
inventory.setItem(lastRowStart + 2, addButton)
inventory.setItem(lastRowStart + 4, refreshButton)
inventory.setItem(lastRowStart + 6, exitButton)
inventory.setItem(lastRowStart + 1, addButton)
inventory.setItem(lastRowStart + 3, refreshButton)
inventory.setItem(lastRowStart + 5, saveButton) // Add new save button
inventory.setItem(lastRowStart + 7, exitButton)
player.openInventory(inventory)
}
@@ -149,14 +155,22 @@ class MOTDGuiHandler(private val plugin: MOTD) : Listener {
@EventHandler
fun onInventoryClick(event: InventoryClickEvent) {
val player = event.whoClicked as? Player ?: return
val clickedInventory = event.clickedInventory ?: return
val title = event.view.title
// Prevent moving items
if (title == "MOTD Quotes Manager" || title == "Select Quote Color") {
event.isCancelled = true
// Check if clicked in the player inventory area
if (event.clickedInventory == player.inventory) {
return
}
} else {
return
}
val item = event.currentItem ?: return
when (title) {
"MOTD Quotes Manager" -> {
handleMainGuiClick(event, player)
@@ -170,30 +184,43 @@ class MOTDGuiHandler(private val plugin: MOTD) : Listener {
private fun handleMainGuiClick(event: InventoryClickEvent, player: Player) {
val item = event.currentItem ?: return
if (item.type == Material.PAPER) {
// Clicked on a quote
val slotIndex = event.slot
when (item.type) {
Material.PAPER -> {
// Clicked on a quote
val slotIndex = event.slot
if (event.isShiftClick) {
// Delete the quote
if (plugin.removeQuote(slotIndex)) {
player.sendMessage("${ChatColor.GREEN}Quote #${slotIndex + 1} has been deleted.")
openQuotesGui(player) // Refresh the GUI
if (event.isShiftClick) {
// Delete the quote
if (plugin.removeQuote(slotIndex)) {
player.sendMessage("${ChatColor.GREEN}Quote #${slotIndex + 1} has been deleted.")
openQuotesGui(player) // Refresh the GUI
} else {
player.sendMessage("${ChatColor.RED}Failed to delete quote.")
}
} else {
// Edit the quote
openColorSelectorGui(player, slotIndex, "edit")
}
} else {
// Edit the quote
openColorSelectorGui(player, slotIndex, "edit")
}
} else if (item.type == Material.EMERALD_BLOCK) {
// Add new quote
openColorSelectorGui(player, -1, "add")
} else if (item.type == Material.GOLD_BLOCK) {
// Refresh MOTD
plugin.updateServerMOTD()
player.sendMessage("${ChatColor.GREEN}MOTD has been refreshed!")
} else if (item.type == Material.REDSTONE_BLOCK) {
// Close GUI
player.closeInventory()
Material.EMERALD_BLOCK -> {
// Add new quote
openColorSelectorGui(player, -1, "add")
}
Material.GOLD_BLOCK -> {
// Refresh MOTD
plugin.updateServerMOTD()
player.sendMessage("${ChatColor.GREEN}MOTD has been refreshed!")
}
Material.DIAMOND_BLOCK -> {
// Save quotes to config
plugin.saveQuotes()
player.sendMessage("${ChatColor.GREEN}Quotes have been saved to config!")
}
Material.REDSTONE_BLOCK -> {
// Close GUI
player.closeInventory()
}
else -> return
}
}
@@ -212,7 +239,12 @@ class MOTDGuiHandler(private val plugin: MOTD) : Listener {
val displayName = meta.displayName
if (displayName.isNotEmpty()) {
val colorCode = "§" + displayName[0].toString().replace("§", "")
val colorCode = if (displayName.startsWith("§")) {
"§" + displayName[1].toString()
} else {
"§f" // Default to white if something goes wrong
}
val session = editingSessions[player.uniqueId] ?: return
player.closeInventory()
@@ -275,20 +307,14 @@ class MOTDGuiHandler(private val plugin: MOTD) : Listener {
fun onInventoryClose(event: InventoryCloseEvent) {
val player = event.player as? Player ?: return
// If the inventory being closed is the color selector but not via clicking a color option,
// remove the editing session
// If the inventory being closed is the color selector and not caused by clicking a color option
if (event.view.title == "Select Quote Color") {
// This check is a bit simplistic; in a production plugin you'd want to be more careful
// to differentiate between programmatic closes (which should keep the session) and
// user-initiated closes (which should clear it)
if (!editingSessions.containsKey(player.uniqueId)) {
Bukkit.getScheduler().runTaskLater(plugin, Runnable {
if (player.openInventory.title != "MOTD Quotes Manager")
if (player.openInventory.title != "MOTD Quotes Manager") {
editingSessions.remove(player.uniqueId)
}
}, 1)
}
// Clean up the session if not transitioning to another GUI
Bukkit.getScheduler().runTaskLater(plugin, Runnable {
if (player.openInventory.title != "MOTD Quotes Manager") {
editingSessions.remove(player.uniqueId)
}
}, 1)
}
}
}

Binary file not shown.