9
0
mirror of https://github.com/Auxilor/EcoMenus.git synced 2025-12-30 04:19:23 +00:00

Initial project outline

This commit is contained in:
Auxilor
2023-06-03 16:55:58 +01:00
parent 712ec82be3
commit bb10e33db6
15 changed files with 405 additions and 3 deletions

View File

@@ -35,9 +35,10 @@ allprojects {
}
dependencies {
compileOnly("com.willfp:eco:6.53.0")
compileOnly("com.willfp:eco:6.63.0")
compileOnly("org.jetbrains:annotations:23.0.0")
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.7.10")
implementation("com.willfp:ecomponent:1.4.1")
}
java {
@@ -46,6 +47,10 @@ allprojects {
}
tasks {
shadowJar {
relocate("com.willfp.ecomponent", "com.willfp.ecomenus.ecomponent")
}
compileKotlin {
kotlinOptions {
jvmTarget = "17"
@@ -73,3 +78,16 @@ allprojects {
}
}
}
tasks {
clean {
doLast {
file("${rootDir}/bin").deleteRecursively()
}
}
shadowJar {
destinationDirectory.set(file("$rootDir/bin/"))
archiveFileName.set("${project.name} v${project.version}.jar")
}
}

View File

@@ -1,7 +1,23 @@
package com.willfp.ecomenus
import com.willfp.eco.core.EcoPlugin
import com.willfp.ecomenus.config.ConfigCategory
import com.willfp.ecomenus.menus.EcoMenus
class EcoMenusPlugin : EcoPlugin() {
private val categories = setOf<ConfigCategory>(
EcoMenus
)
override fun handleEnable() {
for (category in categories) {
category.copyConfigs(this)
}
}
override fun handleReload() {
for (category in categories) {
category.reload(this)
}
}
}

View File

@@ -0,0 +1,21 @@
package com.willfp.ecomenus.commands
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.command.impl.PluginCommand
import com.willfp.ecomenus.menus.EcoMenu
import org.bukkit.entity.Player
class DynamicMenuCommand(
plugin: EcoPlugin,
private val menu: EcoMenu,
commandName: String
) : PluginCommand(
plugin,
commandName,
"ecomenus.menus.${menu.id}.command",
true
) {
override fun onExecute(sender: Player, args: List<String>) {
menu.menu.open(sender)
}
}

View File

@@ -0,0 +1,39 @@
package com.willfp.ecomenus.components
import com.willfp.eco.core.gui.GUIComponent
import com.willfp.eco.core.gui.menu.MenuBuilder
import com.willfp.eco.core.gui.menu.MenuLayer
interface PositionedComponent : GUIComponent {
val row: Int
val column: Int
val isEnabled: Boolean
get() = true
val rowSize: Int
get() = 1
val columnSize: Int
get() = 1
override fun getRows() = rowSize
override fun getColumns() = columnSize
}
fun MenuBuilder.addComponent(
component: PositionedComponent
) = if (component.isEnabled) addComponent(
component.row,
component.column,
component
) else this
fun MenuBuilder.addComponent(
layer: MenuLayer,
component: PositionedComponent
) = if (component.isEnabled) addComponent(
layer,
component.row,
component.column,
component
) else this

View File

@@ -0,0 +1,32 @@
package com.willfp.ecomenus.components.impl
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.eco.core.gui.menu.Menu
import com.willfp.eco.core.gui.onLeftClick
import com.willfp.eco.core.gui.slot
import com.willfp.eco.core.items.Items
import com.willfp.eco.core.items.builder.ItemStackBuilder
import com.willfp.ecomenus.components.PositionedComponent
import org.bukkit.entity.Player
class BackButton(
config: Config,
goBack: (Player, Menu) -> Unit
) : PositionedComponent {
private val slot = slot(
ItemStackBuilder(Items.lookup(config.getString("item")))
.setDisplayName(config.getFormattedString("name"))
.build()
) {
onLeftClick { _, event, _, menu ->
goBack(event.whoClicked as Player, menu)
}
}
override val row: Int = config.getInt("location.row")
override val column: Int = config.getInt("location.column")
override val isEnabled = config.getBool("enabled")
override fun getSlotAt(row: Int, column: Int) = slot
}

View File

@@ -0,0 +1,26 @@
package com.willfp.ecomenus.components.impl
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.eco.core.gui.slot
import com.willfp.eco.core.items.Items
import com.willfp.eco.core.items.builder.ItemStackBuilder
import com.willfp.ecomenus.components.PositionedComponent
class CloseButton(
config: Config
) : PositionedComponent {
private val slot = slot(
ItemStackBuilder(Items.lookup(config.getString("item")))
.setDisplayName(config.getFormattedString("name"))
.build()
) {
onLeftClick { event, _ -> event.whoClicked.closeInventory() }
}
override val row: Int = config.getInt("location.row")
override val column: Int = config.getInt("location.column")
override val isEnabled = config.getBool("enabled")
override fun getSlotAt(row: Int, column: Int) = slot
}

View File

@@ -0,0 +1,58 @@
package com.willfp.ecomenus.config
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.eco.core.config.readConfig
import java.util.zip.ZipFile
abstract class ConfigCategory(
val directory: String
) {
abstract fun clear(plugin: EcoPlugin)
abstract fun acceptConfig(plugin: EcoPlugin, id: String, config: Config)
internal fun reload(plugin: EcoPlugin) {
this.clear(plugin)
for (config in this.fetchConfigs(plugin)) {
this.acceptConfig(plugin, config.id, config.config)
}
}
internal fun copyConfigs(plugin: EcoPlugin) {
val folder = plugin.dataFolder.resolve(this.directory)
if (!folder.exists()) {
getDefaultConfigNames(plugin).forEach { configName ->
FoundConfig(configName, this.directory, plugin).copy()
}
}
}
private fun getDefaultConfigNames(plugin: EcoPlugin): Collection<String> {
val files = mutableListOf<String>()
try {
ZipFile(plugin.file).use { zipFile ->
zipFile.entries().asSequence()
.filter { it.name.startsWith("${this.directory}/") }
.mapTo(files) { it.name.removePrefix("${this.directory}/") }
}
} catch (_: Exception) {
// Sometimes, ZipFile likes to completely fail. No idea why, but here's the 'solution'!
}
return files.filter { it.endsWith(".yml") }.map { it.removeSuffix(".yml") }
}
private fun fetchConfigs(plugin: EcoPlugin): Set<IdentifiedConfig> {
return plugin.dataFolder.resolve(directory)
.walk()
.filter { it.isFile && it.name.endsWith(".yml") && it.nameWithoutExtension != "_example" }
.map { file ->
val id = file.nameWithoutExtension
val config = file.readConfig()
IdentifiedConfig(config, id)
}.toSet()
}
}

View File

@@ -0,0 +1,27 @@
package com.willfp.ecomenus.config
import com.willfp.eco.core.EcoPlugin
import java.io.FileOutputStream
class FoundConfig(
name: String,
directory: String,
private val plugin: EcoPlugin
) {
private val source = plugin::class.java.classLoader
private val resourcePath = "$directory/$name.yml"
fun copy() {
val inputStream = source.getResourceAsStream(resourcePath) ?: return
val outFile = plugin.dataFolder.resolve(resourcePath)
if (!outFile.exists()) {
outFile.parentFile.mkdirs()
FileOutputStream(outFile).use { outStream ->
inputStream.copyTo(outStream)
}
}
inputStream.close()
}
}

View File

@@ -0,0 +1,8 @@
package com.willfp.ecomenus.config
import com.willfp.eco.core.config.interfaces.Config
internal data class IdentifiedConfig(
val config: Config,
val id: String
)

View File

@@ -0,0 +1,22 @@
package com.willfp.ecomenus.menus
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.eco.core.registry.KRegistrable
import com.willfp.ecomenus.commands.DynamicMenuCommand
class EcoMenu(
private val plugin: EcoPlugin,
override val id: String,
val config: Config
) : KRegistrable {
val menu = buildMenu(plugin, config)
private val commandName = config.getStringOrNull("command")
init {
if (commandName != null) {
DynamicMenuCommand(plugin, this, commandName).register()
}
}
}

View File

@@ -0,0 +1,26 @@
package com.willfp.ecomenus.menus
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.eco.core.registry.Registry
import com.willfp.ecomenus.config.ConfigCategory
object EcoMenus : ConfigCategory("menus") {
private val registry = Registry<EcoMenu>()
override fun clear(plugin: EcoPlugin) {
registry.clear()
}
override fun acceptConfig(plugin: EcoPlugin, id: String, config: Config) {
registry.register(EcoMenu(plugin, id, config))
}
fun values(): Collection<EcoMenu> {
return registry.values()
}
operator fun get(id: String): EcoMenu? {
return registry.get(id)
}
}

View File

@@ -0,0 +1,69 @@
package com.willfp.ecomenus.menus
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.eco.core.gui.menu
import com.willfp.eco.core.gui.menu.Menu
import com.willfp.eco.core.gui.slot.ConfigSlot
import com.willfp.eco.core.gui.slot.FillerMask
import com.willfp.eco.core.gui.slot.MaskItems
import com.willfp.ecomenus.components.addComponent
import com.willfp.ecomenus.components.impl.BackButton
import com.willfp.ecomenus.components.impl.CloseButton
import com.willfp.ecomponent.menuStateVar
import org.bukkit.entity.Player
import java.util.Stack
val Menu.previousMenus by menuStateVar<Stack<Menu>>("previous-menu", Stack())
fun <T> Stack<T>.popOrNull(): T? =
if (this.empty()) null else this.pop()
fun Menu.open(
player: Player,
from: Menu? = null
) {
this.open(player)
if (from != null) {
this.previousMenus[player] += from
}
}
fun Menu.close(player: Player) =
this.previousMenus[player].popOrNull()?.open(player) ?: player.closeInventory()
fun buildMenu(plugin: EcoPlugin, config: Config): Menu {
val mask = FillerMask(
MaskItems.fromItemNames(plugin.configYml.getStrings("mask.materials")),
*plugin.configYml.getStrings("mask.pattern").toTypedArray()
)
return menu(mask.rows) {
title = config.getFormattedString("title")
setMask(mask)
addComponent(
CloseButton(
plugin.configYml.getSubsection("stats-gui.close")
)
)
addComponent(
BackButton(
plugin.configYml.getSubsection("stats-gui.back")
) { player, menu ->
menu.close(player)
}
)
// TODO: Implement Slots
for (test in plugin.configYml.getSubsections("stats-gui.custom-slots")) {
setSlot(
test.getInt("row"),
test.getInt("column"),
ConfigSlot(test)
)
}
}
}

View File

@@ -1,5 +1,5 @@
options:
bstats-id: 0
resource-id: 0
color: "&a"
color: "#FCEDDA"
uses-reflective-reload: false

View File

@@ -1,5 +1,5 @@
messages:
prefix: "&a&lEcoMenus &f» "
prefix: "&#FCEDDA&lEcoMenus &f» "
no-permission: "&cYou don't have permission to do this!"
not-player: "&cThis command must be run by a player"
invalid-command: "&cUnknown subcommand!"

View File

@@ -0,0 +1,40 @@
# The title of the GUI
title: "Example GUI"
command: examplemenu # (Optional) The command to open the GUI, if not set, there will be no command.
mask:
# The way the mask works is by having a list of materials
# And then a pattern to use those materials.
# The size of the GUI is the size of the mask.
# The pattern is the rows in the GUI
# Each line must be 9 long, and the amount of rows should be the amount of rows in the GUI
# A zero represents nothing
# A 1 represents the first material
# A 2 represents the second material
# And so on, you can add up to 9.
materials:
- gray_stained_glass_pane
- black_stained_glass_pane
pattern:
- "211101112"
- "211111112"
- "210000012"
- "210010012"
- "211111112"
- "211101112"
# Options for the close item
close:
item: barrier
name: "&cClose"
enabled: false
location:
row: 6
column: 5
# Custom GUI slots; see here for a how-to: https://plugins.auxilor.io/all-plugins/custom-gui-slots
custom-slots: [ ]