diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/PylonCore.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/PylonCore.kt index ebd25476c..cb86f95f1 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/PylonCore.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/PylonCore.kt @@ -10,11 +10,11 @@ import io.github.pylonmc.pylon.core.addon.PylonAddon import io.github.pylonmc.pylon.core.block.* import io.github.pylonmc.pylon.core.block.base.* import io.github.pylonmc.pylon.core.resourcepack.block.BlockTextureEngine -import io.github.pylonmc.pylon.core.block.waila.Waila import io.github.pylonmc.pylon.core.command.ROOT_COMMAND import io.github.pylonmc.pylon.core.command.ROOT_COMMAND_PY_ALIAS import io.github.pylonmc.pylon.core.config.Config import io.github.pylonmc.pylon.core.config.ConfigSection +import io.github.pylonmc.pylon.core.config.PylonConfig import io.github.pylonmc.pylon.core.content.debug.DebugWaxedWeatheredCutCopperStairs import io.github.pylonmc.pylon.core.content.fluid.* import io.github.pylonmc.pylon.core.content.guide.PylonGuide @@ -22,6 +22,8 @@ import io.github.pylonmc.pylon.core.entity.EntityListener import io.github.pylonmc.pylon.core.entity.EntityStorage import io.github.pylonmc.pylon.core.entity.PylonEntity import io.github.pylonmc.pylon.core.fluid.connecting.ConnectingService +import io.github.pylonmc.pylon.core.guide.button.PageButton +import io.github.pylonmc.pylon.core.guide.pages.SettingsPage import io.github.pylonmc.pylon.core.i18n.PylonTranslator import io.github.pylonmc.pylon.core.item.PylonItem import io.github.pylonmc.pylon.core.item.PylonItemListener @@ -31,8 +33,11 @@ import io.github.pylonmc.pylon.core.recipe.ConfigurableRecipeType import io.github.pylonmc.pylon.core.recipe.PylonRecipeListener import io.github.pylonmc.pylon.core.recipe.RecipeType import io.github.pylonmc.pylon.core.registry.PylonRegistry +import io.github.pylonmc.pylon.core.resourcepack.armor.ArmorTextureConfig import io.github.pylonmc.pylon.core.resourcepack.block.BlockTextureConfig import io.github.pylonmc.pylon.core.resourcepack.armor.ArmorTextureEngine +import io.github.pylonmc.pylon.core.waila.Waila +import io.github.pylonmc.pylon.core.waila.WailaConfig import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents import kotlinx.coroutines.delay @@ -44,7 +49,6 @@ import org.bukkit.Bukkit import org.bukkit.Material import org.bukkit.NamespacedKey import org.bukkit.configuration.file.YamlConfiguration -import org.bukkit.entity.BlockDisplay import org.bukkit.entity.Interaction import org.bukkit.entity.ItemDisplay import org.bukkit.permissions.Permission @@ -72,8 +76,6 @@ object PylonCore : JavaPlugin(), PylonAddon { val packetEvents = PacketEvents.getAPI() packetEvents.init() - packetEvents.eventManager.registerListener(ArmorTextureEngine, PacketListenerPriority.HIGHEST) - val entityLibPlatform = SpigotEntityLibPlatform(this) entityLibPlatform.entityIdProvider = EntityIdProvider { uuid, type -> Bukkit.getUnsafe().nextEntityId() } val entityLibSettings = APIConfig(packetEvents) @@ -98,7 +100,6 @@ object PylonCore : JavaPlugin(), PylonAddon { Bukkit.getPluginManager().registerEvents(MultiblockCache, this) Bukkit.getPluginManager().registerEvents(EntityStorage, this) Bukkit.getPluginManager().registerEvents(EntityListener, this) - Bukkit.getPluginManager().registerEvents(Waila, this) Bukkit.getPluginManager().registerEvents(Research, this) Bukkit.getPluginManager().registerEvents(PylonGuiBlock, this) Bukkit.getPluginManager().registerEvents(PylonEntityHolderBlock, this) @@ -110,11 +111,28 @@ object PylonCore : JavaPlugin(), PylonAddon { Bukkit.getPluginManager().registerEvents(PylonTickingBlock, this) Bukkit.getPluginManager().registerEvents(PylonGuide, this) - if (BlockTextureConfig.customBlockTexturesEnabled) { + if (WailaConfig.wailaEnabled) { + PylonGuide.settingsPage.addSetting(PageButton(SettingsPage.wailaSettings)) + Bukkit.getPluginManager().registerEvents(Waila, this) + } + + PylonGuide.settingsPage.addSetting(PageButton(SettingsPage.resourcePackSettings)) + + if (ArmorTextureConfig.armorTexturesEnabled) { + SettingsPage.resourcePackSettings.addSetting(SettingsPage.armorTextureSetting) + packetEvents.eventManager.registerListener(ArmorTextureEngine, PacketListenerPriority.HIGHEST) + } + + if (BlockTextureConfig.blockTexturesEnabled) { + SettingsPage.resourcePackSettings.addSetting(PageButton(SettingsPage.blockTextureSettings)) Bukkit.getPluginManager().registerEvents(BlockTextureEngine, this) BlockTextureEngine.updateOccludingCacheJob.start() } + if (PylonConfig.researchesEnabled) { + PylonGuide.settingsPage.addSetting(SettingsPage.researchEffects) + } + Bukkit.getScheduler().runTaskTimer( this, MultiblockCache.MultiblockChecker, diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/block/PhantomBlock.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/block/PhantomBlock.kt index 82191ab34..7527d1594 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/block/PhantomBlock.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/block/PhantomBlock.kt @@ -2,12 +2,12 @@ package io.github.pylonmc.pylon.core.block import io.github.pylonmc.pylon.core.block.context.BlockBreakContext import io.github.pylonmc.pylon.core.block.context.BlockCreateContext -import io.github.pylonmc.pylon.core.block.waila.WailaConfig import io.github.pylonmc.pylon.core.datatypes.PylonSerializers import io.github.pylonmc.pylon.core.i18n.PylonArgument import io.github.pylonmc.pylon.core.item.PylonItem import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder import io.github.pylonmc.pylon.core.util.pylonKey +import io.github.pylonmc.pylon.core.waila.WailaDisplay import net.kyori.adventure.bossbar.BossBar import org.bukkit.Material import org.bukkit.NamespacedKey @@ -50,8 +50,8 @@ class PhantomBlock( throw UnsupportedOperationException("Phantom block cannot be loaded") } - override fun getWaila(player: Player): WailaConfig? { - return WailaConfig( + override fun getWaila(player: Player): WailaDisplay? { + return WailaDisplay( text = defaultWailaTranslationKey.arguments(PylonArgument.of("block", erroredBlockKey.toString())), color = BossBar.Color.RED ) diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/block/PylonBlock.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/block/PylonBlock.kt index 1afe9fe42..7e9844a34 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/block/PylonBlock.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/block/PylonBlock.kt @@ -9,7 +9,6 @@ import io.github.pylonmc.pylon.core.block.base.PylonEntityHolderBlock import io.github.pylonmc.pylon.core.block.base.PylonGuiBlock import io.github.pylonmc.pylon.core.block.context.BlockBreakContext import io.github.pylonmc.pylon.core.block.context.BlockCreateContext -import io.github.pylonmc.pylon.core.block.waila.WailaConfig import io.github.pylonmc.pylon.core.config.Config import io.github.pylonmc.pylon.core.config.Settings import io.github.pylonmc.pylon.core.content.debug.DebugWaxedWeatheredCutCopperStairs @@ -21,6 +20,7 @@ import io.github.pylonmc.pylon.core.resourcepack.block.BlockTextureConfig import io.github.pylonmc.pylon.core.util.position.BlockPosition import io.github.pylonmc.pylon.core.util.position.position import io.github.pylonmc.pylon.core.util.pylonKey +import io.github.pylonmc.pylon.core.waila.WailaDisplay import io.github.retrooper.packetevents.util.SpigotConversionUtil import me.tofaa.entitylib.meta.display.ItemDisplayMeta import me.tofaa.entitylib.wrapper.WrapperEntity @@ -90,7 +90,7 @@ open class PylonBlock internal constructor(val block: Block) { * you can use [updateBlockTexture] to change the entity's item to reflect the lit/unlit state. */ val blockTextureEntity: WrapperEntity? by lazy { - if (!BlockTextureConfig.customBlockTexturesEnabled || disableBlockTextureEntity) { + if (!BlockTextureConfig.blockTexturesEnabled || disableBlockTextureEntity) { null } else { val entity = WrapperEntity(EntityTypes.ITEM_DISPLAY) @@ -201,8 +201,8 @@ open class PylonBlock internal constructor(val block: Block) { * * @return the WAILA configuration, or null if WAILA should not be shown for this block. */ - open fun getWaila(player: Player): WailaConfig? { - return WailaConfig(defaultWailaTranslationKey) + open fun getWaila(player: Player): WailaDisplay? { + return WailaDisplay(defaultWailaTranslationKey) } /** diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/block/waila/Waila.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/block/waila/Waila.kt deleted file mode 100644 index 0f2d3a3a1..000000000 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/block/waila/Waila.kt +++ /dev/null @@ -1,174 +0,0 @@ -package io.github.pylonmc.pylon.core.block.waila - -import com.github.shynixn.mccoroutine.bukkit.launch -import com.github.shynixn.mccoroutine.bukkit.ticks -import io.github.pylonmc.pylon.core.PylonCore -import io.github.pylonmc.pylon.core.block.BlockStorage -import io.github.pylonmc.pylon.core.config.PylonConfig -import io.github.pylonmc.pylon.core.entity.EntityStorage -import io.github.pylonmc.pylon.core.util.position.BlockPosition -import io.github.pylonmc.pylon.core.util.position.position -import io.github.pylonmc.pylon.core.util.pylonKey -import kotlinx.coroutines.Job -import kotlinx.coroutines.delay -import net.kyori.adventure.bossbar.BossBar -import net.kyori.adventure.text.Component -import org.bukkit.attribute.Attribute -import org.bukkit.entity.Player -import org.bukkit.event.EventHandler -import org.bukkit.event.EventPriority -import org.bukkit.event.Listener -import org.bukkit.event.player.PlayerJoinEvent -import org.bukkit.event.player.PlayerQuitEvent -import org.bukkit.persistence.PersistentDataType -import java.util.UUID - -/** - * Handles WAILAs (the text that displays a block's name when looking - * at the block). - * - * You should not need to use this if you just want to change the WAILA of - * a [io.github.pylonmc.pylon.core.block.PylonBlock]. For that, see - * [io.github.pylonmc.pylon.core.block.PylonBlock.getWaila] - */ -class Waila private constructor(private val player: Player, private val job: Job) { - - private val bossbar = BossBar.bossBar( - Component.empty(), - 1F, - BossBar.Color.WHITE, - BossBar.Overlay.PROGRESS - ) - - private fun on() { - val bossbars = player.activeBossBars() - for (bossbar in bossbars) { - player.hideBossBar(bossbar) - } - player.showBossBar(this.bossbar) - for (bossbar in bossbars) { - player.showBossBar(bossbar) - } - } - - private fun off() { - player.hideBossBar(bossbar) - } - - private fun destroy() { - off() - job.cancel() - } - - private fun updateDisplay() { - val entityReach = player.getAttribute(Attribute.ENTITY_INTERACTION_RANGE)?.value ?: 3 - val targetEntity = player.rayTraceEntities(entityReach.toInt())?.hitEntity - if (targetEntity != null) { - val config = try { - EntityStorage.get(targetEntity)?.getWaila(player) - } catch (e: Exception) { - e.printStackTrace() - off() - return - } - if (config != null) { - config.apply(bossbar) - on() - } else { - off() - } - } else { - val blockReach = player.getAttribute(Attribute.BLOCK_INTERACTION_RANGE)?.value ?: 4.5 - val block = player.rayTraceBlocks(blockReach)?.hitBlock - if (block == null) { - off() - return - } - val config = try { - overrides[block.position]?.invoke(player) ?: block.let(BlockStorage::get)?.getWaila(player) - } catch (e: Exception) { - e.printStackTrace() - off() - return - } - if (config != null) { - config.apply(bossbar) - on() - } else { - off() - } - } - } - - companion object : Listener { - - private val wailaKey = pylonKey("waila") - private val wailas = mutableMapOf() - - private val overrides = mutableMapOf WailaConfig?>() - - /** - * Forcibly adds a WAILA display for the given player. - */ - @JvmStatic - fun addPlayer(player: Player) { - wailas[player.uniqueId] = Waila(player, PylonCore.launch { - delay(1.ticks) - val waila = wailas[player.uniqueId]!! - while (true) { - waila.updateDisplay() - delay(PylonConfig.wailaTickInterval.ticks) - } - }) - } - - /** - * Forcibly removes a WAILA display for the given player. - */ - @JvmStatic - fun removePlayer(player: Player) { - wailas.remove(player.uniqueId)?.destroy() - } - - @JvmStatic - var Player.wailaEnabled: Boolean - get() = this.persistentDataContainer.getOrDefault(wailaKey, PersistentDataType.BOOLEAN, true) - set(value) { - this.persistentDataContainer.set(wailaKey, PersistentDataType.BOOLEAN, value) - if (value) { - addPlayer(this) - } else { - removePlayer(this) - } - } - - /** - * Adds a WAILA override for the given position. This will always show the - * provided WAILA config when a WAILA-enabled player looks at the block at - * the given position, regardless of the block type or even if the block is - * not a Pylon block. - */ - @JvmStatic - fun addWailaOverride(position: BlockPosition, provider: (Player) -> WailaConfig?) { - overrides[position] = provider - } - - @JvmStatic - fun removeWailaOverride(position: BlockPosition) { - overrides.remove(position) - } - - @EventHandler(priority = EventPriority.MONITOR) - private fun onPlayerJoin(event: PlayerJoinEvent) { - val player = event.player - if (player.wailaEnabled) { - addPlayer(player) - } - } - - @EventHandler(priority = EventPriority.MONITOR) - private fun onPlayerQuit(event: PlayerQuitEvent) { - removePlayer(event.player) - } - } -} \ No newline at end of file diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/block/waila/WailaConfig.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/block/waila/WailaConfig.kt deleted file mode 100644 index 13974f089..000000000 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/block/waila/WailaConfig.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.pylonmc.pylon.core.block.waila - -import net.kyori.adventure.bossbar.BossBar -import net.kyori.adventure.text.Component - -/** - * The configuration for a WAILA bossbar (the bar shown at the top of your - * screen when looking at a block). - */ -@JvmRecord -data class WailaConfig @JvmOverloads constructor( - val text: Component, - val color: BossBar.Color = BossBar.Color.WHITE, - val style: BossBar.Overlay = BossBar.Overlay.PROGRESS, - val progress: Float = 1F -) { - - @JvmSynthetic - internal fun apply(bar: BossBar) { - bar.name(text) - bar.color(color) - bar.overlay(style) - bar.progress(progress) - } -} \ No newline at end of file diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/command/PylonCommand.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/command/PylonCommand.kt index 1970d8bd3..7a06bd141 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/command/PylonCommand.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/command/PylonCommand.kt @@ -12,7 +12,6 @@ import io.github.pylonmc.pylon.core.PylonCore import io.github.pylonmc.pylon.core.addon.PylonAddon import io.github.pylonmc.pylon.core.block.BlockStorage import io.github.pylonmc.pylon.core.block.PylonBlockSchema -import io.github.pylonmc.pylon.core.block.waila.Waila.Companion.wailaEnabled import io.github.pylonmc.pylon.core.content.debug.DebugWaxedWeatheredCutCopperStairs import io.github.pylonmc.pylon.core.content.guide.PylonGuide import io.github.pylonmc.pylon.core.entity.display.transform.Rotation @@ -33,6 +32,8 @@ import io.github.pylonmc.pylon.core.gametest.GameTestConfig import io.github.pylonmc.pylon.core.util.mergeGlobalConfig import io.github.pylonmc.pylon.core.util.position.BlockPosition import io.github.pylonmc.pylon.core.util.vanillaDisplayName +import io.github.pylonmc.pylon.core.waila.Waila.Companion.wailaConfig +import io.github.pylonmc.pylon.core.waila.WailaConfig import io.papermc.paper.command.brigadier.CommandSourceStack import io.papermc.paper.command.brigadier.argument.ArgumentTypes import io.papermc.paper.command.brigadier.argument.resolvers.BlockPositionResolver @@ -172,8 +173,9 @@ private val waila = buildCommand("waila") { permission("pylon.command.waila") executesWithPlayer { player -> PylonMetrics.onCommandRun("/py waila") - player.wailaEnabled = !player.wailaEnabled - player.sendFeedback(if (player.wailaEnabled) "waila.enabled" else "waila.disabled") + val config = player.wailaConfig + config.enabled = !config.enabled + player.sendFeedback(if (config.enabled) "waila.enabled" else "waila.disabled") } } @@ -516,7 +518,9 @@ internal val ROOT_COMMAND = buildCommand("pylon") { then(debug) then(key) then(setblock) - then(waila) + if (WailaConfig.wailaEnabled) { + then(waila) + } then(gametest) then(research) then(exposeRecipeConfig) diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/config/PylonConfig.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/config/PylonConfig.kt index 88baf6011..63415f05a 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/config/PylonConfig.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/config/PylonConfig.kt @@ -19,9 +19,6 @@ object PylonConfig { @JvmStatic val allowedBlockErrors = config.getOrThrow("allowed-block-errors", ConfigAdapter.INT) - @JvmStatic - val wailaTickInterval = config.getOrThrow("waila-tick-interval", ConfigAdapter.INT) - @JvmStatic val allowedEntityErrors = config.getOrThrow("allowed-entity-errors", ConfigAdapter.INT) diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/config/adapter/ConfigAdapter.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/config/adapter/ConfigAdapter.kt index b83349d19..84cf384a4 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/config/adapter/ConfigAdapter.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/config/adapter/ConfigAdapter.kt @@ -94,6 +94,7 @@ interface ConfigAdapter { @JvmField val ITEM_TAG = ItemTagConfigAdapter @JvmField val WEIGHTED_SET = WeightedSetConfigAdapter @JvmField val CULLING_PRESET = CullingPresetConfigAdapter + @JvmField val WAILA_DISPLAY = WailaDisplayConfigAdapter // @formatter:on } } diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/config/adapter/WailaDisplayConfigAdapter.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/config/adapter/WailaDisplayConfigAdapter.kt new file mode 100644 index 000000000..4f8edc58b --- /dev/null +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/config/adapter/WailaDisplayConfigAdapter.kt @@ -0,0 +1,18 @@ +package io.github.pylonmc.pylon.core.config.adapter + +import io.github.pylonmc.pylon.core.waila.WailaDisplay +import net.kyori.adventure.bossbar.BossBar +import net.kyori.adventure.text.Component + +object WailaDisplayConfigAdapter : ConfigAdapter { + override val type = WailaDisplay::class.java + + override fun convert(value: Any): WailaDisplay { + val map = MapConfigAdapter.STRING_TO_ANY.convert(value) + val text = Component.translatable(ConfigAdapter.STRING.convert(map["text"] ?: throw IllegalArgumentException("WailaDisplay is missing 'text' field"))) + val color = ConfigAdapter.ENUM.from().convert(map["color"] ?: throw IllegalArgumentException("WailaDisplay is missing 'color' field")) + val overlay = ConfigAdapter.ENUM.from().convert(map["overlay"] ?: throw IllegalArgumentException("WailaDisplay is missing 'overlay' field")) + val progress = ConfigAdapter.FLOAT.convert(map["progress"] ?: throw IllegalArgumentException("WailaDisplay is missing 'progress' field")) + return WailaDisplay(text, color, overlay, progress) + } +} \ No newline at end of file diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/fluid/FluidPipeConnector.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/fluid/FluidPipeConnector.kt index 7829b59a9..3af399b9c 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/fluid/FluidPipeConnector.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/fluid/FluidPipeConnector.kt @@ -5,12 +5,12 @@ import io.github.pylonmc.pylon.core.block.base.PylonEntityHolderBlock import io.github.pylonmc.pylon.core.block.context.BlockBreakContext import io.github.pylonmc.pylon.core.block.context.BlockBreakContext.PlayerBreak import io.github.pylonmc.pylon.core.block.context.BlockCreateContext -import io.github.pylonmc.pylon.core.block.waila.WailaConfig import io.github.pylonmc.pylon.core.entity.EntityStorage import io.github.pylonmc.pylon.core.fluid.FluidPointType import io.github.pylonmc.pylon.core.i18n.PylonArgument import io.github.pylonmc.pylon.core.item.PylonItem import io.github.pylonmc.pylon.core.util.pylonKey +import io.github.pylonmc.pylon.core.waila.WailaDisplay import org.bukkit.block.Block import org.bukkit.entity.Player import org.bukkit.inventory.ItemStack @@ -51,8 +51,8 @@ class FluidPipeConnector : PylonBlock, PylonEntityHolderBlock { super.onBreak(drops, context) } - override fun getWaila(player: Player): WailaConfig? - = WailaConfig(defaultWailaTranslationKey.arguments(PylonArgument.of("pipe", this.pipe.stack.effectiveName()))) + override fun getWaila(player: Player): WailaDisplay? + = WailaDisplay(defaultWailaTranslationKey.arguments(PylonArgument.of("pipe", this.pipe.stack.effectiveName()))) val pipe: PylonItem get() { diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/fluid/FluidPipeMarker.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/fluid/FluidPipeMarker.kt index d27276c0f..edae0eb75 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/fluid/FluidPipeMarker.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/fluid/FluidPipeMarker.kt @@ -6,11 +6,11 @@ import io.github.pylonmc.pylon.core.block.context.BlockBreakContext import io.github.pylonmc.pylon.core.block.context.BlockBreakContext.PlayerBreak import io.github.pylonmc.pylon.core.block.context.BlockBreakContext.PluginBreak import io.github.pylonmc.pylon.core.block.context.BlockCreateContext -import io.github.pylonmc.pylon.core.block.waila.WailaConfig import io.github.pylonmc.pylon.core.datatypes.PylonSerializers import io.github.pylonmc.pylon.core.entity.EntityStorage import io.github.pylonmc.pylon.core.i18n.PylonArgument import io.github.pylonmc.pylon.core.util.pylonKey +import io.github.pylonmc.pylon.core.waila.WailaDisplay import org.bukkit.block.Block import org.bukkit.entity.Player import org.bukkit.inventory.ItemStack @@ -69,8 +69,8 @@ class FluidPipeMarker : PylonBlock, PylonBreakHandler { } } - override fun getWaila(player: Player): WailaConfig? - = WailaConfig(defaultWailaTranslationKey.arguments(PylonArgument.of("pipe", getPipeDisplay()!!.pipe.stack.effectiveName()))) + override fun getWaila(player: Player): WailaDisplay? + = WailaDisplay(defaultWailaTranslationKey.arguments(PylonArgument.of("pipe", getPipeDisplay()!!.pipe.stack.effectiveName()))) override fun getDropItem(context: BlockBreakContext) = null diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/guide/PylonGuide.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/guide/PylonGuide.kt index 94ffd9073..c9cc9ade3 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/guide/PylonGuide.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/content/guide/PylonGuide.kt @@ -1,6 +1,7 @@ package io.github.pylonmc.pylon.core.content.guide import io.github.pylonmc.pylon.core.config.PylonConfig +import io.github.pylonmc.pylon.core.guide.button.PageButton import io.github.pylonmc.pylon.core.guide.pages.InfoPage import io.github.pylonmc.pylon.core.guide.pages.RootPage import io.github.pylonmc.pylon.core.guide.pages.SearchItemsAndFluidsPage @@ -90,7 +91,7 @@ class PylonGuide(stack: ItemStack) : PylonItem(stack), PylonInteractor { val searchItemsAndFluidsPage = SearchItemsAndFluidsPage() @JvmStatic - val settingsPageAndInfoPage = SettingsPage() + val settingsPage = SettingsPage() /** * Lowest priority to avoid another plugin saving the players data or doing something diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/datatypes/PlayerWailaConfigPersistentDataType.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/datatypes/PlayerWailaConfigPersistentDataType.kt new file mode 100644 index 000000000..fafa287d8 --- /dev/null +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/datatypes/PlayerWailaConfigPersistentDataType.kt @@ -0,0 +1,38 @@ +package io.github.pylonmc.pylon.core.datatypes + +import io.github.pylonmc.pylon.core.util.pylonKey +import io.github.pylonmc.pylon.core.waila.PlayerWailaConfig +import org.bukkit.persistence.PersistentDataAdapterContext +import org.bukkit.persistence.PersistentDataContainer +import org.bukkit.persistence.PersistentDataType + +object PlayerWailaConfigPersistentDataType : PersistentDataType { + val enabledKey = pylonKey("waila_enabled") + val vanillaWailaEnabledKey = pylonKey("waila_vanilla_enabled") + val typeKey = pylonKey("waila_type") + + override fun getPrimitiveType(): Class = PersistentDataContainer::class.java + + override fun getComplexType(): Class = PlayerWailaConfig::class.java + + override fun fromPrimitive( + primitive: PersistentDataContainer, + context: PersistentDataAdapterContext + ): PlayerWailaConfig { + val enabled = primitive.get(enabledKey, PylonSerializers.BOOLEAN)!! + val vanillaWailaEnabled = primitive.get(vanillaWailaEnabledKey, PylonSerializers.BOOLEAN)!! + val type = primitive.get(typeKey, PylonSerializers.WAILA_TYPE)!! + return PlayerWailaConfig(enabled, vanillaWailaEnabled, type) + } + + override fun toPrimitive( + complex: PlayerWailaConfig, + context: PersistentDataAdapterContext + ): PersistentDataContainer { + val pdc = context.newPersistentDataContainer() + pdc.set(enabledKey, PylonSerializers.BOOLEAN, complex.enabled) + pdc.set(vanillaWailaEnabledKey, PylonSerializers.BOOLEAN, complex.vanillaWailaEnabled) + pdc.set(typeKey, PylonSerializers.WAILA_TYPE, complex.type) + return pdc + } +} \ No newline at end of file diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/datatypes/PylonSerializers.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/datatypes/PylonSerializers.kt index 89724f2e4..d05b284df 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/datatypes/PylonSerializers.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/datatypes/PylonSerializers.kt @@ -2,6 +2,7 @@ package io.github.pylonmc.pylon.core.datatypes import io.github.pylonmc.pylon.core.fluid.PylonFluid import io.github.pylonmc.pylon.core.registry.PylonRegistry +import io.github.pylonmc.pylon.core.waila.Waila import org.bukkit.Material import org.bukkit.Registry import org.bukkit.block.BlockFace @@ -116,4 +117,10 @@ object PylonSerializers { @JvmField val FLUID_CONNECTION_POINT = FluidConnectionPointDataType + + @JvmSynthetic + internal val WAILA_TYPE = EnumPersistentDataType(Waila.Type::class.java) + + @JvmSynthetic + internal val PLAYER_WAILA_CONFIG = PlayerWailaConfigPersistentDataType } diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/entity/PylonEntity.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/entity/PylonEntity.kt index 4264d6024..dc2d1c6bc 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/entity/PylonEntity.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/entity/PylonEntity.kt @@ -1,13 +1,13 @@ package io.github.pylonmc.pylon.core.entity import io.github.pylonmc.pylon.core.PylonCore -import io.github.pylonmc.pylon.core.block.waila.WailaConfig import io.github.pylonmc.pylon.core.config.Config import io.github.pylonmc.pylon.core.config.Settings import io.github.pylonmc.pylon.core.content.debug.DebugWaxedWeatheredCutCopperStairs import io.github.pylonmc.pylon.core.datatypes.PylonSerializers import io.github.pylonmc.pylon.core.registry.PylonRegistry import io.github.pylonmc.pylon.core.util.pylonKey +import io.github.pylonmc.pylon.core.waila.WailaDisplay import org.bukkit.NamespacedKey import org.bukkit.entity.Entity import org.bukkit.entity.Player @@ -42,7 +42,7 @@ abstract class PylonEntity(val entity: E) { * * @return the WAILA configuration, or null if WAILA should not be shown for this block. */ - open fun getWaila(player: Player): WailaConfig? = null + open fun getWaila(player: Player): WailaDisplay? = null /** * Called when debug info is requested for the entity by someone diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/event/PylonBlockWailaEvent.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/event/PylonBlockWailaEvent.kt new file mode 100644 index 000000000..30510a1b0 --- /dev/null +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/event/PylonBlockWailaEvent.kt @@ -0,0 +1,28 @@ +package io.github.pylonmc.pylon.core.event + +import io.github.pylonmc.pylon.core.waila.WailaDisplay +import org.bukkit.block.Block +import org.bukkit.entity.Player +import org.bukkit.event.Cancellable +import org.bukkit.event.HandlerList +import org.bukkit.event.player.PlayerEvent + +class PylonBlockWailaEvent( + player: Player, + val block: Block, + var display: WailaDisplay? +) : PlayerEvent(player), Cancellable { + private var cancelled = false + + override fun isCancelled(): Boolean = cancelled + override fun setCancelled(cancel: Boolean) { + cancelled = cancel + } + + override fun getHandlers(): HandlerList = handlerList + + companion object { + @JvmStatic + val handlerList: HandlerList = HandlerList() + } +} \ No newline at end of file diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/event/PylonEntityWailaEvent.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/event/PylonEntityWailaEvent.kt new file mode 100644 index 000000000..59ee2ee51 --- /dev/null +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/event/PylonEntityWailaEvent.kt @@ -0,0 +1,28 @@ +package io.github.pylonmc.pylon.core.event + +import io.github.pylonmc.pylon.core.waila.WailaDisplay +import org.bukkit.entity.Entity +import org.bukkit.entity.Player +import org.bukkit.event.Cancellable +import org.bukkit.event.HandlerList +import org.bukkit.event.player.PlayerEvent + +class PylonEntityWailaEvent( + player: Player, + val block: Entity, + var display: WailaDisplay? +) : PlayerEvent(player), Cancellable { + private var cancelled = false + + override fun isCancelled(): Boolean = cancelled + override fun setCancelled(cancel: Boolean) { + cancelled = cancel + } + + override fun getHandlers(): HandlerList = handlerList + + companion object { + @JvmStatic + val handlerList: HandlerList = HandlerList() + } +} \ No newline at end of file diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/CullingPresetButton.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/CullingPresetButton.kt deleted file mode 100644 index d0602fbb6..000000000 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/CullingPresetButton.kt +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.pylonmc.pylon.core.guide.button - -import io.github.pylonmc.pylon.core.resourcepack.block.BlockTextureEngine.cullingPreset -import io.github.pylonmc.pylon.core.config.PylonConfig -import io.github.pylonmc.pylon.core.i18n.PylonArgument -import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder -import io.github.pylonmc.pylon.core.resourcepack.block.BlockTextureConfig -import net.kyori.adventure.text.Component -import org.bukkit.entity.Player -import org.bukkit.event.inventory.ClickType -import org.bukkit.event.inventory.InventoryClickEvent -import xyz.xenondevs.invui.item.ItemProvider -import xyz.xenondevs.invui.item.impl.AbstractItem - -class CullingPresetButton : AbstractItem() { - override fun getItemProvider(player: Player): ItemProvider? { - val preset = player.cullingPreset - return ItemStackBuilder.of(preset.material) - .name(Component.translatable("pylon.pyloncore.guide.button.culling-preset.${preset.id}.name")) - .lore( - Component.translatable("pylon.pyloncore.guide.button.culling-preset.${preset.id}.lore") - .arguments( - PylonArgument.of("hiddenInterval", preset.hiddenInterval), - PylonArgument.of("visibleInterval", preset.visibleInterval), - PylonArgument.of("alwaysShowRadius", preset.alwaysShowRadius), - PylonArgument.of("cullRadius", preset.cullRadius), - PylonArgument.of("maxOccludingCount", preset.maxOccludingCount) - ) - ) - } - - override fun handleClick(clickType: ClickType, player: Player, event: InventoryClickEvent) { - val presets = BlockTextureConfig.cullingPresets.values.toMutableList() - presets.sortBy { it.index } - val currentIndex = presets.indexOfFirst { it.id == player.cullingPreset.id } - val nextIndex = (currentIndex + 1) % presets.size - player.cullingPreset = presets[nextIndex] - notifyWindows() - } -} \ No newline at end of file diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleArmorTexturesButton.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleArmorTexturesButton.kt deleted file mode 100644 index d404f4c88..000000000 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleArmorTexturesButton.kt +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.pylonmc.pylon.core.guide.button - -import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder -import io.github.pylonmc.pylon.core.resourcepack.armor.ArmorTextureEngine.hasCustomArmorTextures -import net.kyori.adventure.text.Component -import org.bukkit.Material -import org.bukkit.entity.Player -import org.bukkit.event.inventory.ClickType -import org.bukkit.event.inventory.InventoryClickEvent -import xyz.xenondevs.invui.item.impl.AbstractItem - -class ToggleArmorTexturesButton : AbstractItem() { - override fun getItemProvider(player: Player) = ItemStackBuilder.of(if (player.hasCustomArmorTextures) Material.LIME_CONCRETE else Material.RED_CONCRETE) - .name(Component.translatable("pylon.pyloncore.guide.button.toggle-armor-textures.name")) - .lore(Component.translatable("pylon.pyloncore.guide.button.toggle-armor-textures.lore")) - - override fun handleClick(clickType: ClickType, player: Player, event: InventoryClickEvent) { - player.hasCustomArmorTextures = !player.hasCustomArmorTextures - notifyWindows() - } -} \ No newline at end of file diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleBlockTexturesButton.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleBlockTexturesButton.kt deleted file mode 100644 index f15a5fcc1..000000000 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleBlockTexturesButton.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.pylonmc.pylon.core.guide.button - -import io.github.pylonmc.pylon.core.resourcepack.block.BlockTextureEngine.hasCustomBlockTextures -import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder -import io.github.pylonmc.pylon.core.resourcepack.block.BlockTextureEngine -import net.kyori.adventure.text.Component -import org.bukkit.Material -import org.bukkit.entity.Player -import org.bukkit.event.inventory.ClickType -import org.bukkit.event.inventory.InventoryClickEvent -import xyz.xenondevs.invui.item.impl.AbstractItem - -class ToggleBlockTexturesButton : AbstractItem() { - override fun getItemProvider(player: Player) = ItemStackBuilder.of(if (player.hasCustomBlockTextures) Material.LIME_CONCRETE else Material.RED_CONCRETE) - .name(Component.translatable("pylon.pyloncore.guide.button.toggle-block-textures.name")) - .lore(Component.translatable("pylon.pyloncore.guide.button.toggle-block-textures.lore")) - - override fun handleClick(clickType: ClickType, player: Player, event: InventoryClickEvent) { - player.hasCustomBlockTextures = !player.hasCustomBlockTextures - if (player.hasCustomBlockTextures) { - BlockTextureEngine.launchBlockTextureJob(player) - } - notifyWindows() - } -} \ No newline at end of file diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleWailaButton.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleWailaButton.kt deleted file mode 100644 index 3bebde6d8..000000000 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/ToggleWailaButton.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.pylonmc.pylon.core.guide.button - -import io.github.pylonmc.pylon.core.block.waila.Waila.Companion.wailaEnabled -import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder -import net.kyori.adventure.text.Component -import org.bukkit.Material -import org.bukkit.entity.Player -import org.bukkit.event.inventory.ClickType -import org.bukkit.event.inventory.InventoryClickEvent -import xyz.xenondevs.invui.item.impl.AbstractItem - -/** - * A settings button for toggling the WAILA overlay. - */ -class ToggleWailaButton : AbstractItem() { - - override fun getItemProvider(player: Player) = ItemStackBuilder.of(if (player.wailaEnabled) Material.LIME_CONCRETE else Material.RED_CONCRETE) - .name(Component.translatable("pylon.pyloncore.guide.button.toggle-waila.name")) - .lore(Component.translatable("pylon.pyloncore.guide.button.toggle-waila.lore")) - - override fun handleClick(clickType: ClickType, player: Player, event: InventoryClickEvent) { - player.wailaEnabled = !player.wailaEnabled - notifyWindows() - } -} \ No newline at end of file diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/setting/CycleSettingButton.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/setting/CycleSettingButton.kt new file mode 100644 index 000000000..3ada38eeb --- /dev/null +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/setting/CycleSettingButton.kt @@ -0,0 +1,41 @@ +package io.github.pylonmc.pylon.core.guide.button.setting + +import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.ComponentLike +import org.bukkit.Material +import org.bukkit.NamespacedKey +import org.bukkit.entity.Player +import org.bukkit.event.inventory.ClickType +import org.bukkit.event.inventory.InventoryClickEvent +import org.bukkit.inventory.ItemStack +import xyz.xenondevs.invui.item.ItemProvider +import xyz.xenondevs.invui.item.impl.AbstractItem + +data class CycleSettingButton ( + val key: NamespacedKey, + val sortedValues: List, + val identifier: (S) -> String, + + val getter: (Player) -> S, + val setter: (Player, S) -> Unit, + + val decorator: (Player, S) -> ItemStack, + val argumentProvider: (Player, S) -> MutableList = { _, _ -> mutableListOf() } +) : AbstractItem() { + override fun getItemProvider(player: Player): ItemProvider? { + val setting = getter(player) + val identifier = identifier(setting) + return ItemStackBuilder.of(decorator(player, setting)) + .name(Component.translatable("pylon.${key.namespace}.guide.button.${key.key}.${identifier}.name")) + .lore(Component.translatable("pylon.${key.namespace}.guide.button.${key.key}.${identifier}.lore") + .arguments(argumentProvider(player, setting))) + } + + override fun handleClick(clickType: ClickType, player: Player, event: InventoryClickEvent) { + val currentIndex = sortedValues.indexOfFirst { identifier(it) == identifier(getter(player)) } + val nextIndex = (currentIndex + 1) % sortedValues.size + setter(player, sortedValues[nextIndex]) + notifyWindows() + } +} \ No newline at end of file diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/setting/NumericSettingButton.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/setting/NumericSettingButton.kt new file mode 100644 index 000000000..75ab16ff1 --- /dev/null +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/setting/NumericSettingButton.kt @@ -0,0 +1,46 @@ +package io.github.pylonmc.pylon.core.guide.button.setting + +import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.ComponentLike +import org.bukkit.Material +import org.bukkit.NamespacedKey +import org.bukkit.entity.Player +import org.bukkit.event.inventory.ClickType +import org.bukkit.event.inventory.InventoryClickEvent +import org.bukkit.inventory.ItemStack +import xyz.xenondevs.invui.item.ItemProvider +import xyz.xenondevs.invui.item.impl.AbstractItem + +data class NumericSettingButton( + val key: NamespacedKey, + + val min: N, + val max: N, + val step: N, + val shiftStep: N, + val type: (Number) -> N, + + val getter: (Player) -> N, + val setter: (Player, N) -> Unit, + + val decorator: (Player, N) -> ItemStack, + val argumentProvider: (Player, N) -> MutableList = { _, _ -> mutableListOf() } +) : AbstractItem() { + override fun getItemProvider(player: Player): ItemProvider? { + val setting = getter(player) + return ItemStackBuilder.of(decorator(player, setting)) + .name(Component.translatable("pylon.${key.namespace}.guide.button.${key.key}.name")) + .lore(Component.translatable("pylon.${key.namespace}.guide.button.${key.key}.lore") + .arguments(argumentProvider(player, setting))) + } + + override fun handleClick(clickType: ClickType, player: Player, event: InventoryClickEvent) { + var value = getter(player).toDouble() + val step = if (clickType.isShiftClick) shiftStep.toDouble() else step.toDouble() + value += if (clickType.isLeftClick) step else -step + value = value.coerceIn(min.toDouble(), max.toDouble()) + setter(player, type(value)) + notifyWindows() + } +} diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/setting/ToggleSettingButton.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/setting/ToggleSettingButton.kt new file mode 100644 index 000000000..803abc80e --- /dev/null +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/button/setting/ToggleSettingButton.kt @@ -0,0 +1,26 @@ +package io.github.pylonmc.pylon.core.guide.button.setting + +import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder +import net.kyori.adventure.text.Component +import org.bukkit.Material +import org.bukkit.NamespacedKey +import org.bukkit.entity.Player +import org.bukkit.event.inventory.ClickType +import org.bukkit.event.inventory.InventoryClickEvent +import xyz.xenondevs.invui.item.impl.AbstractItem + +data class ToggleSettingButton( + val key: NamespacedKey, + val toggle: (Player) -> Unit, + val isToggled: (Player) -> Boolean +) : AbstractItem() { + override fun getItemProvider(player: Player) = ItemStackBuilder.of(if (isToggled(player)) Material.LIME_CONCRETE else Material.RED_CONCRETE) + .name(Component.translatable("pylon.${key.namespace}.guide.button.${key.key}.name")) + .lore(Component.translatable("pylon.${key.namespace}.guide.button.${key.key}.lore")) + .addCustomModelDataString("${key}_${if (isToggled(player)) "on" else "off"}") + + override fun handleClick(clickType: ClickType, player: Player, event: InventoryClickEvent) { + toggle(player) + notifyWindows() + } +} diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/pages/RootPage.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/pages/RootPage.kt index 05d3f4687..08007fffb 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/pages/RootPage.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/pages/RootPage.kt @@ -38,7 +38,7 @@ class RootPage() : SimpleStaticGuidePage( "x x x x x x x x x", ) .addIngredient('#', GuiItems.background()) - .addIngredient('e', PageButton(PylonGuide.settingsPageAndInfoPage)) + .addIngredient('e', PageButton(PylonGuide.settingsPage)) .addIngredient('s', PageButton(PylonGuide.searchItemsAndFluidsPage)) .addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL) .addPageChangeHandler { _, newPage -> saveCurrentPage(player, newPage) } diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/pages/SettingsPage.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/pages/SettingsPage.kt index 62bab2904..7b1572016 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/pages/SettingsPage.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/guide/pages/SettingsPage.kt @@ -2,48 +2,144 @@ package io.github.pylonmc.pylon.core.guide.pages import io.github.pylonmc.pylon.core.content.guide.PylonGuide import io.github.pylonmc.pylon.core.guide.button.BackButton -import io.github.pylonmc.pylon.core.guide.button.CullingPresetButton import io.github.pylonmc.pylon.core.guide.button.PageButton -import io.github.pylonmc.pylon.core.guide.button.ToggleArmorTexturesButton -import io.github.pylonmc.pylon.core.guide.button.ToggleBlockTexturesButton -import io.github.pylonmc.pylon.core.guide.button.ToggleWailaButton +import io.github.pylonmc.pylon.core.guide.button.setting.CycleSettingButton +import io.github.pylonmc.pylon.core.guide.button.setting.ToggleSettingButton import io.github.pylonmc.pylon.core.guide.pages.base.SimpleStaticGuidePage +import io.github.pylonmc.pylon.core.i18n.PylonArgument +import io.github.pylonmc.pylon.core.item.builder.ItemStackBuilder +import io.github.pylonmc.pylon.core.item.research.Research.Companion.researchEffects +import io.github.pylonmc.pylon.core.resourcepack.armor.ArmorTextureConfig +import io.github.pylonmc.pylon.core.resourcepack.armor.ArmorTextureEngine.hasCustomArmorTextures +import io.github.pylonmc.pylon.core.resourcepack.block.BlockTextureConfig +import io.github.pylonmc.pylon.core.resourcepack.block.BlockTextureEngine.cullingPreset +import io.github.pylonmc.pylon.core.resourcepack.block.BlockTextureEngine.hasCustomBlockTextures import io.github.pylonmc.pylon.core.util.gui.GuiItems import io.github.pylonmc.pylon.core.util.pylonKey +import io.github.pylonmc.pylon.core.waila.Waila.Companion.wailaConfig +import io.github.pylonmc.pylon.core.waila.WailaConfig +import io.papermc.paper.datacomponent.item.ItemLore.lore +import jdk.internal.misc.PreviewFeatures.isEnabled import org.bukkit.Material +import org.bukkit.NamespacedKey import org.bukkit.entity.Player import xyz.xenondevs.invui.gui.Gui import xyz.xenondevs.invui.gui.PagedGui +import xyz.xenondevs.invui.gui.structure.Markers +import xyz.xenondevs.invui.item.Item /** * Contains buttons to change settings. */ -class SettingsPage : SimpleStaticGuidePage( - pylonKey("settings_and_info"), - Material.COMPARATOR -) { +class SettingsPage( + key: NamespacedKey = pylonKey("settings"), + material: Material = Material.COMPARATOR, + buttons: MutableList = mutableListOf(), +) : SimpleStaticGuidePage(key, material, buttons) { override fun getGui(player: Player): Gui { val buttons = buttonSupplier.get() val gui = PagedGui.items() .setStructure( "# b # # # # # s #", "# # # # # # # # #", - "# w t c a . . . #", + "# x x x x x x x #", "# # # # # # # # #", ) .addIngredient('#', GuiItems.background()) .addIngredient('b', BackButton()) .addIngredient('s', PageButton(PylonGuide.searchItemsAndFluidsPage)) - .addIngredient('w', ToggleWailaButton()) - .addIngredient('t', ToggleBlockTexturesButton()) - .addIngredient('c', CullingPresetButton()) - .addIngredient('a', ToggleArmorTexturesButton()) + .addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL) .addPageChangeHandler { _, newPage -> saveCurrentPage(player, newPage) } + .setContent(buttons.filter { it !is PageButton || it.page !is SettingsPage || it.page.buttons.isNotEmpty() }) + return gui.build().apply { loadCurrentPage(player, this) } + } - for (button in buttons) { - gui.addContent(button) + fun addSetting(item: Item) { + buttons.add(item) + buttons.sortBy { if (it is PageButton) 0 else 1 } + } + + companion object { + @JvmStatic + val wailaSettings = SettingsPage( + pylonKey("waila_settings"), + Material.SPYGLASS + ).apply { + addSetting(ToggleSettingButton( + pylonKey("toggle-waila"), + toggle = { player -> player.wailaConfig.enabled = !player.wailaConfig.enabled }, + isToggled = { player -> player.wailaConfig.enabled } + )) + addSetting(ToggleSettingButton( + pylonKey("toggle-vanilla-waila"), + toggle = { player -> player.wailaConfig.vanillaWailaEnabled = !player.wailaConfig.vanillaWailaEnabled }, + isToggled = { player -> player.wailaConfig.vanillaWailaEnabled } + )) + if (WailaConfig.enabledTypes.size > 1) { + addSetting(CycleSettingButton( + pylonKey("cycle-waila-type"), + WailaConfig.enabledTypes, + identifier = { type -> type.name.lowercase() }, + getter = { player -> player.wailaConfig.type }, + setter = { player, type -> player.wailaConfig.type = type }, + decorator = { player, type -> ItemStackBuilder.of(Material.PAPER) + .addCustomModelDataString("waila_type=${type.name.lowercase()}") + .build() + } + )) + } } - return gui.build().apply { loadCurrentPage(player, this) } + @JvmStatic + val resourcePackSettings = SettingsPage( + pylonKey("resource_pack_settings"), + Material.PAINTING + ) + + @JvmStatic + val blockTextureSettings = SettingsPage( + pylonKey("block_texture_settings"), + Material.BOOKSHELF + ).apply { + if (!BlockTextureConfig.blockTexturesForced) { + addSetting(ToggleSettingButton( + pylonKey("toggle-block-textures"), + toggle = { player -> player.hasCustomBlockTextures = !player.hasCustomBlockTextures }, + isToggled = { player -> player.hasCustomBlockTextures } + )) + } + addSetting(CycleSettingButton( + pylonKey("cycle-culling-preset"), + BlockTextureConfig.cullingPresets.values.sortedBy { it.index }, + identifier = { preset -> preset.id }, + getter = { player -> player.cullingPreset }, + setter = { player, preset -> player.cullingPreset = preset }, + decorator = { player, preset -> ItemStackBuilder.of(preset.material) + .addCustomModelDataString("culling_preset=${preset.id}") + .build() + }, + argumentProvider = { player, preset -> mutableListOf( + PylonArgument.of("hiddenInterval", preset.hiddenInterval), + PylonArgument.of("visibleInterval", preset.visibleInterval), + PylonArgument.of("alwaysShowRadius", preset.alwaysShowRadius), + PylonArgument.of("cullRadius", preset.cullRadius), + PylonArgument.of("maxOccludingCount", preset.maxOccludingCount) + )} + )) + } + + @JvmStatic + val armorTextureSetting = ToggleSettingButton( + pylonKey("toggle-armor-textures"), + toggle = { player -> player.hasCustomArmorTextures = !player.hasCustomArmorTextures }, + isToggled = { player -> player.hasCustomArmorTextures }, + ) + + @JvmStatic + val researchEffects = ToggleSettingButton( + pylonKey("toggle-research-effects"), + toggle = { player -> player.researchEffects = !player.researchEffects }, + isToggled = { player -> player.researchEffects } + ) } } \ No newline at end of file diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/research/Research.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/research/Research.kt index c4df78a80..45e32d474 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/research/Research.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/item/research/Research.kt @@ -93,7 +93,7 @@ data class Research( ) } - if (effects) { + if (effects && player.researchEffects) { val multiplier = (cost?.toDouble() ?: 0.0) * PylonConfig.researchMultiplierConfettiAmount val amount = (PylonConfig.researchBaseConfettiAmount * multiplier).toInt() val spawnedConfetti = min(amount, PylonConfig.researchMaxConfettiAmount) @@ -138,12 +138,16 @@ data class Research( companion object : Listener { private val researchesKey = pylonKey("researches") private val researchPointsKey = pylonKey("research_points") + private val researchEffectsKey = pylonKey("research_effects") private val researchesType = PylonSerializers.SET.setTypeFrom(PylonSerializers.KEYED.keyedTypeFrom(PylonRegistry.RESEARCHES::getOrThrow)) @JvmStatic var Player.researchPoints: Long by persistentData(researchPointsKey, PylonSerializers.LONG, 0) + @JvmStatic + var Player.researchEffects: Boolean by persistentData(researchEffectsKey, PylonSerializers.BOOLEAN, true) + @JvmStatic fun getResearches(player: OfflinePlayer): Set { val researches = player.persistentDataContainer.get(researchesKey, researchesType) diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/resourcepack/armor/ArmorTextureConfig.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/resourcepack/armor/ArmorTextureConfig.kt new file mode 100644 index 000000000..9cc8d3c4f --- /dev/null +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/resourcepack/armor/ArmorTextureConfig.kt @@ -0,0 +1,17 @@ +package io.github.pylonmc.pylon.core.resourcepack.armor + +import io.github.pylonmc.pylon.core.PylonCore +import io.github.pylonmc.pylon.core.config.Config +import io.github.pylonmc.pylon.core.config.adapter.ConfigAdapter + +object ArmorTextureConfig { + + private val config = Config(PylonCore, "config.yml") + + @JvmStatic + val armorTexturesEnabled = config.getOrThrow("custom-armor-textures.enabled", ConfigAdapter.BOOLEAN) + + @JvmStatic + val armorTexturesForced = config.getOrThrow("custom-armor-textures.force", ConfigAdapter.BOOLEAN) + +} \ No newline at end of file diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/resourcepack/block/BlockTextureConfig.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/resourcepack/block/BlockTextureConfig.kt index c97113f8e..6054af98e 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/resourcepack/block/BlockTextureConfig.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/resourcepack/block/BlockTextureConfig.kt @@ -9,7 +9,10 @@ object BlockTextureConfig { private val config = Config(PylonCore, "config.yml") @JvmStatic - val customBlockTexturesEnabled = config.getOrThrow("custom-block-textures.enabled", ConfigAdapter.BOOLEAN) + val blockTexturesEnabled = config.getOrThrow("custom-block-textures.enabled", ConfigAdapter.BOOLEAN) + + @JvmStatic + val blockTexturesForced = config.getOrThrow("custom-block-textures.force", ConfigAdapter.BOOLEAN) @JvmStatic val occludingCacheRefreshInterval = config.getOrThrow("custom-block-textures.culling.occluding-cache-refresh-interval", ConfigAdapter.INT) diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/resourcepack/block/BlockTextureEngine.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/resourcepack/block/BlockTextureEngine.kt index 7064e4c6d..eb9af0d3d 100644 --- a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/resourcepack/block/BlockTextureEngine.kt +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/resourcepack/block/BlockTextureEngine.kt @@ -12,7 +12,6 @@ import io.github.pylonmc.pylon.core.util.Octree import io.github.pylonmc.pylon.core.util.position.BlockPosition import io.github.pylonmc.pylon.core.util.position.ChunkPosition import io.github.pylonmc.pylon.core.util.pylonKey -import io.papermc.paper.command.brigadier.argument.ArgumentTypes.uuid import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Job import kotlinx.coroutines.delay @@ -87,8 +86,8 @@ object BlockTextureEngine : Listener { @JvmStatic var Player.hasCustomBlockTextures: Boolean - get() = this.persistentDataContainer.getOrDefault(customBlockTexturesKey, PersistentDataType.BOOLEAN, true) - set(value) = this.persistentDataContainer.set(customBlockTexturesKey, PersistentDataType.BOOLEAN, value) + get() = (this.persistentDataContainer.getOrDefault(customBlockTexturesKey, PersistentDataType.BOOLEAN, true) || BlockTextureConfig.blockTexturesForced) + set(value) = this.persistentDataContainer.set(customBlockTexturesKey, PersistentDataType.BOOLEAN, value || BlockTextureConfig.blockTexturesForced) @JvmStatic var Player.cullingPreset: CullingPreset @@ -99,13 +98,13 @@ object BlockTextureEngine : Listener { @JvmSynthetic internal fun insert(block: PylonBlock) { - if (!BlockTextureConfig.customBlockTexturesEnabled || block.disableBlockTextureEntity) return + if (!BlockTextureConfig.blockTexturesEnabled || block.disableBlockTextureEntity) return getOctree(block.block.world).insert(block) } @JvmSynthetic internal fun remove(block: PylonBlock) { - if (!BlockTextureConfig.customBlockTexturesEnabled || block.disableBlockTextureEntity) return + if (!BlockTextureConfig.blockTexturesEnabled || block.disableBlockTextureEntity) return getOctree(block.block.world).remove(block) block.blockTextureEntity?.let { for (viewer in it.viewers.toSet()) { @@ -116,7 +115,7 @@ object BlockTextureEngine : Listener { @JvmSynthetic internal fun getOctree(world: World): Octree { - check(BlockTextureConfig.customBlockTexturesEnabled) { "Tried to access BlockTextureEngine octree while custom block textures are disabled" } + check(BlockTextureConfig.blockTexturesEnabled) { "Tried to access BlockTextureEngine octree while custom block textures are disabled" } val border = world.worldBorder return octrees.getOrPut(world.uid) { @@ -134,7 +133,7 @@ object BlockTextureEngine : Listener { @JvmSynthetic internal fun launchBlockTextureJob(player: Player) { val uuid = player.uniqueId - if (!BlockTextureConfig.customBlockTexturesEnabled || !player.hasCustomBlockTextures || jobs.containsKey(uuid)) return + if (!BlockTextureConfig.blockTexturesEnabled || !player.hasCustomBlockTextures || jobs.containsKey(uuid)) return jobs[uuid] = PylonCore.launch(PylonCore.asyncDispatcher) { val visible = mutableSetOf() diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/waila/PlayerWailaConfig.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/waila/PlayerWailaConfig.kt new file mode 100644 index 000000000..55cd1457c --- /dev/null +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/waila/PlayerWailaConfig.kt @@ -0,0 +1,34 @@ +package io.github.pylonmc.pylon.core.waila + +import io.github.pylonmc.pylon.core.waila.Waila.Companion.wailaConfig +import org.bukkit.entity.Player + +class PlayerWailaConfig { + var player: Player? = null + + var enabled: Boolean = true + set(value) { + field = value + player?.wailaConfig = this + } + + var vanillaWailaEnabled: Boolean = false + set(value) { + field = value + player?.wailaConfig = this + } + + var type: Waila.Type = WailaConfig.defaultType + set(value) { + field = value + player?.wailaConfig = this + } + + constructor() + + constructor(enabled: Boolean, vanillaWailaEnabled: Boolean, type: Waila.Type) { + this.enabled = enabled + this.vanillaWailaEnabled = vanillaWailaEnabled + this.type = type + } +} \ No newline at end of file diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/waila/Waila.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/waila/Waila.kt new file mode 100644 index 000000000..86ad460eb --- /dev/null +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/waila/Waila.kt @@ -0,0 +1,279 @@ +package io.github.pylonmc.pylon.core.waila + +import com.github.shynixn.mccoroutine.bukkit.launch +import com.github.shynixn.mccoroutine.bukkit.ticks +import io.github.pylonmc.pylon.core.PylonCore +import io.github.pylonmc.pylon.core.block.BlockStorage +import io.github.pylonmc.pylon.core.block.PylonBlock +import io.github.pylonmc.pylon.core.datatypes.PylonSerializers +import io.github.pylonmc.pylon.core.entity.EntityStorage +import io.github.pylonmc.pylon.core.entity.PylonEntity +import io.github.pylonmc.pylon.core.event.PylonBlockWailaEvent +import io.github.pylonmc.pylon.core.event.PylonEntityWailaEvent +import io.github.pylonmc.pylon.core.i18n.PylonArgument +import io.github.pylonmc.pylon.core.util.position.BlockPosition +import io.github.pylonmc.pylon.core.util.position.position +import io.github.pylonmc.pylon.core.util.pylonKey +import io.papermc.paper.raytracing.RayTraceTarget +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import net.kyori.adventure.bossbar.BossBar +import net.kyori.adventure.text.Component +import org.bukkit.attribute.Attribute +import org.bukkit.entity.Entity +import org.bukkit.entity.Player +import org.bukkit.event.EventHandler +import org.bukkit.event.EventPriority +import org.bukkit.event.Listener +import org.bukkit.event.player.PlayerJoinEvent +import org.bukkit.event.player.PlayerQuitEvent +import java.util.UUID +import kotlin.math.max + +/** + * Handles WAILAs (the text that displays a block's name when looking + * at the block). + * + * If you want to change the WAILA display for your [PylonBlock] or [PylonEntity], see + * [PylonBlock.getWaila] and [PylonEntity.getWaila], if you need to change the WAILA + * display for a different block/entity, see [addWailaOverride]. + */ +class Waila private constructor(private val player: Player, playerConfig: PlayerWailaConfig, private val job: Job) { + + private var config = playerConfig + set(value) { + if (field.type != value.type) { + hide() + } + field = value + } + + private val bossBar = BossBar.bossBar( + Component.empty(), + WailaConfig.defaultDisplay.progress, + WailaConfig.defaultDisplay.color, + WailaConfig.defaultDisplay.overlay + ) + + private fun send(display: WailaDisplay) { + when (config.type) { + Type.BOSSBAR -> { + player.hideBossBar(bossBar) + val color = if (display.color in WailaConfig.allowedBossBarColors) { + display.color + } else { + WailaConfig.defaultDisplay.color + } + val overlay = if (display.overlay in WailaConfig.allowedBossBarOverlays) { + display.overlay + } else { + WailaConfig.defaultDisplay.overlay + } + + bossBar.name(display.text) + bossBar.color(color) + bossBar.overlay(overlay) + bossBar.progress(display.progress) + player.showBossBar(bossBar) + } + Type.ACTIONBAR -> player.sendActionBar(display.text) + } + } + + private fun hide() { + when (config.type) { + Type.BOSSBAR -> { + if (player.activeBossBars().contains(bossBar)) { + player.hideBossBar(bossBar) + } + } + Type.ACTIONBAR -> player.sendActionBar(Component.empty()) + } + } + + private fun destroy() { + hide() + job.cancel() + } + + private fun updateDisplay() { + val entityReach = player.getAttribute(Attribute.ENTITY_INTERACTION_RANGE)?.value ?: 3.0 + val blockReach = player.getAttribute(Attribute.BLOCK_INTERACTION_RANGE)?.value ?: 4.5 + + val rayTraceResult = player.world.rayTrace { builder -> + builder.start(player.eyeLocation) + builder.direction(player.eyeLocation.direction) + builder.maxDistance(max(entityReach, blockReach)) + builder.entityFilter { entity -> entity != player && entity.location.distanceSquared(player.location) <= entityReach * entityReach } + builder.blockFilter { block -> block.location.distanceSquared(player.location) <= blockReach * blockReach } + builder.targets(RayTraceTarget.ENTITY, RayTraceTarget.BLOCK) + } + + if (rayTraceResult == null) { + hide() + return + } + + rayTraceResult.hitEntity?.let { entity -> + try { + var display = entityOverrides[entity.uniqueId]?.invoke(player) + ?: entity.let(EntityStorage::get)?.getWaila(player) + + if (display == null && player.wailaConfig.vanillaWailaEnabled) { + display = WailaDisplay(Component.translatable(entity.type.translationKey())) + } + + if (display != null) { + val event = PylonEntityWailaEvent(player, entity, display) + event.callEvent() + if (!event.isCancelled && event.display != null) { + send(event.display!!) + } else { + hide() + } + } else { + hide() + } + } catch(e: Exception) { + e.printStackTrace() + hide() + } + } + + rayTraceResult.hitBlock?.let { block -> + try { + var display = blockOverrides[block.position]?.invoke(player) + ?: block.let(BlockStorage::get)?.getWaila(player) + + if (display == null && player.wailaConfig.vanillaWailaEnabled) { + display = WailaDisplay(Component.translatable(block.type.translationKey())) + } + + if (display != null) { + val event = PylonBlockWailaEvent(player, block, display) + event.callEvent() + if (!event.isCancelled && event.display != null) { + send(event.display!!) + } else { + hide() + } + } else { + hide() + } + } catch(e: Exception) { + e.printStackTrace() + hide() + } + } + } + + enum class Type { + BOSSBAR, + ACTIONBAR + } + + companion object : Listener { + + private val wailaKey = pylonKey("waila") + private val wailas = mutableMapOf() + + private val blockOverrides = mutableMapOf WailaDisplay?>() + private val entityOverrides = mutableMapOf WailaDisplay?>() + + /** + * Forcibly adds a WAILA display for the given player. + */ + @JvmStatic + fun addPlayer(player: Player, config: PlayerWailaConfig = player.wailaConfig) { + if (wailas.containsKey(player.uniqueId) || !config.enabled) { + return + } + + wailas[player.uniqueId] = Waila(player, config, PylonCore.launch { + delay(1.ticks) + val waila = wailas[player.uniqueId]!! + while (true) { + waila.updateDisplay() + delay(WailaConfig.tickInterval.ticks) + } + }) + } + + /** + * Forcibly removes a WAILA display for the given player. + */ + @JvmStatic + fun removePlayer(player: Player) { + wailas.remove(player.uniqueId)?.destroy() + } + + @JvmStatic + var Player.wailaConfig: PlayerWailaConfig + get() = this.persistentDataContainer.getOrDefault(wailaKey, PylonSerializers.PLAYER_WAILA_CONFIG, PlayerWailaConfig()).apply { + player = this@wailaConfig + if (!WailaConfig.enabledTypes.contains(type)) { + sendMessage(Component.translatable("pylon.pyloncore.message.waila.type-disabled").arguments( + PylonArgument.of("type", type.name.lowercase()) + )) + type = WailaConfig.defaultType + } + } + set(value) { + this.persistentDataContainer.set(wailaKey, PylonSerializers.PLAYER_WAILA_CONFIG, value) + if (value.enabled) { + if (!wailas.containsKey(uniqueId)) { + addPlayer(this, value) + } else { + wailas[this.uniqueId]?.config = value + } + } else { + removePlayer(this) + } + } + + /** + * Adds a WAILA override for the given position. This will always show the + * provided WAILA config when a WAILA-enabled player looks at the block at + * the given position, regardless of the block type or even if the block is + * not a Pylon block. + */ + @JvmStatic + fun addWailaOverride(position: BlockPosition, provider: (Player) -> WailaDisplay?) { + blockOverrides[position] = provider + } + + @JvmStatic + fun addWailaOverride(entity: Entity, provider: (Player) -> WailaDisplay?) { + entityOverrides[entity.uniqueId] = provider + } + + @JvmStatic + fun removeWailaOverride(position: BlockPosition) { + blockOverrides.remove(position) + } + + @JvmStatic + fun removeWailaOverride(entity: Entity) { + entityOverrides.remove(entity.uniqueId) + } + + @EventHandler(priority = EventPriority.MONITOR) + private fun onPlayerJoin(event: PlayerJoinEvent) { + val player = event.player + + // TODO: Remove this migration code in a future version + if (player.persistentDataContainer.has(wailaKey, PylonSerializers.BOOLEAN)) { + player.persistentDataContainer.remove(wailaKey) + } + + if (player.wailaConfig.enabled) { + addPlayer(player) + } + } + + @EventHandler(priority = EventPriority.MONITOR) + private fun onPlayerQuit(event: PlayerQuitEvent) { + removePlayer(event.player) + } + } +} \ No newline at end of file diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/waila/WailaConfig.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/waila/WailaConfig.kt new file mode 100644 index 000000000..caaa605f7 --- /dev/null +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/waila/WailaConfig.kt @@ -0,0 +1,43 @@ +package io.github.pylonmc.pylon.core.waila + +import io.github.pylonmc.pylon.core.PylonCore +import io.github.pylonmc.pylon.core.config.Config +import io.github.pylonmc.pylon.core.config.adapter.ConfigAdapter +import net.kyori.adventure.bossbar.BossBar + +object WailaConfig { + private val config = Config(PylonCore, "config.yml") + + @JvmStatic + val wailaEnabled + get() = tickInterval > 0 && enabledTypes.isNotEmpty() + + @JvmStatic + val tickInterval = config.getOrThrow("waila.tick-interval", ConfigAdapter.INT) + + @JvmStatic + val enabledTypes = config.getOrThrow("waila.enabled-types", ConfigAdapter.LIST.from(ConfigAdapter.ENUM.from(Waila.Type::class.java))) + + @JvmStatic + val defaultType = config.getOrThrow("waila.default-type", ConfigAdapter.ENUM.from(Waila.Type::class.java)).apply { + if (!enabledTypes.contains(this)) { + throw IllegalStateException("Default Waila type $this is not in the list of enabled types: $enabledTypes") + } + } + + @JvmStatic + val allowedBossBarColors = config.getOrThrow("waila.bossbar.allowed-colors", ConfigAdapter.SET.from(ConfigAdapter.ENUM.from(BossBar.Color::class.java))) + + @JvmStatic + val allowedBossBarOverlays = config.getOrThrow("waila.bossbar.allowed-overlays", ConfigAdapter.SET.from(ConfigAdapter.ENUM.from(BossBar.Overlay::class.java))) + + @JvmStatic + val defaultDisplay = config.getOrThrow("waila.default-display.bossbar", ConfigAdapter.WAILA_DISPLAY).apply { + if (!allowedBossBarColors.contains(color)) { + throw IllegalStateException("Default bossbar color $color is not in the list of allowed colors: $allowedBossBarColors") + } + if (!allowedBossBarOverlays.contains(overlay)) { + throw IllegalStateException("Default bossbar overlay $overlay is not in the list of allowed overlays: $allowedBossBarOverlays") + } + } +} \ No newline at end of file diff --git a/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/waila/WailaDisplay.kt b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/waila/WailaDisplay.kt new file mode 100644 index 000000000..d2f9bc900 --- /dev/null +++ b/pylon-core/src/main/kotlin/io/github/pylonmc/pylon/core/waila/WailaDisplay.kt @@ -0,0 +1,16 @@ +package io.github.pylonmc.pylon.core.waila + +import net.kyori.adventure.bossbar.BossBar +import net.kyori.adventure.text.Component + +/** + * The configuration for a WAILA bossbar (the bar shown at the top of your + * screen when looking at a block). + */ +@JvmRecord +data class WailaDisplay @JvmOverloads constructor( + val text: Component, + val color: BossBar.Color = WailaConfig.defaultDisplay.color, + val overlay: BossBar.Overlay = WailaConfig.defaultDisplay.overlay, + val progress: Float = WailaConfig.defaultDisplay.progress +) \ No newline at end of file diff --git a/pylon-core/src/main/resources/config.yml b/pylon-core/src/main/resources/config.yml index c77b9db8a..c9dbfe667 100644 --- a/pylon-core/src/main/resources/config.yml +++ b/pylon-core/src/main/resources/config.yml @@ -11,9 +11,6 @@ allowed-block-errors: 5 # The number of errors a entity can throw before it is disabled allowed-entity-errors: 5 -# The interval (in Minecraft ticks), between WAILA checks -waila-tick-interval: 1 - # The interval (in Minecraft ticks) between fluid network ticks fluid-tick-interval: 5 @@ -21,6 +18,38 @@ block-data-autosave-interval-seconds: 60 entity-data-autosave-interval-seconds: 60 +waila: + # The interval (in Minecraft ticks), between WAILA checks + # Set to 0 to disable WAILA globally + tick-interval: 1 + + # The WAILA display types players can choose between + enabled-types: + - actionbar + - bossbar + + # The default display type for players, if you allow multiple types, they can change between them in the guide settings + # Note: If you default to actionbar, or enforce it, and have a long tick-interval, the WAILA may not be visible all the time + # as actionbar messages fade after a few seconds + default-type: bossbar + + # The default bossbar display options if the block doesn't specify one + # There are no defaults for actionbar, as it has no options + default-display: + bossbar: + text: "" + color: white + overlay: progress + progress: 1.0 + + # Here you can control what bossbar colors & overlays can be used within the WAILA + # Some servers modify the textures for both in resource packs, so they may want to + # prevent them being used by the WAILA. + # If any colors/overlays not in these lists are used, they will default to the values in default-display + bossbar: + allowed-colors: [ pink, blue, red, green, yellow, purple, white ] + allowed-overlays: [ progress, notched_6, notched_10, notched_12, notched_20 ] + research: enabled: true # The interval (in Minecraft ticks) between when Pylon checks for unresearched items in inventories @@ -90,12 +119,24 @@ metrics-save-interval-ticks: 6000 # those items can't be crafted. disabled-items: [] -# Should Pylon send item displays to the client for custom block textures? -# If a player has no resource pack that adds textures installed, the entities will appear invisible. -# Players can also disable this on a per-player basis, and can configure with culling preset they want. +custom-armor-textures: + # Should Pylon support modifying the equipment component of armor items to use custom armor textures? + # If a player has no resource pack that adds textures installed, and has armor textures enabled the armor will appear invisible. + # By default, armor textures are disabled for players, but they can enable it themselves in the guide settings. + enabled: true + + # If true, players will not be able to disable custom armor textures and it will default to enabled + force: false + custom-block-textures: + # Should Pylon send item displays to the client for custom block textures? + # If a player has no resource pack that adds textures installed, the entities will appear invisible. + # Players can also disable this on a per-player basis, and can configure with culling preset they want. enabled: true + # If true, players will not be able to disable custom block textures themselves + force: false + # Custom Block Textures uses ray tracing to cull entities not visible to the player. # There are 4 default presets to choose from, each with different client performance and visual quality tradeoffs. # It is not recommended to change these settings (excluding default-preset) unless you know what you're doing diff --git a/pylon-core/src/main/resources/lang/en.yml b/pylon-core/src/main/resources/lang/en.yml index 550c9b931..24a98f5c1 100644 --- a/pylon-core/src/main/resources/lang/en.yml +++ b/pylon-core/src/main/resources/lang/en.yml @@ -95,6 +95,9 @@ message: ingredients-page: item: "%item_ingredients_page_amount%x %item_ingredients_page_item%" + waila: + type-disabled: "This server no longer permits the your previously set %type% WAILA display type, it has been reset to default" + gui: scroll: up: "Scroll up" @@ -146,7 +149,17 @@ guide: error: "Item failed to load" toggle-waila: name: "Toggle WAILA" - lore: " WAILA is the bossbar at the top of your screen that shows when you look at a Pylon block" + lore: " WAILA is a display that shows information about the pylon block/entity you're looking at" + toggle-vanilla-waila: + name: "Toggle Vanilla WAILA" + lore: " Should the WAILA display vanilla block/entity information as well?" + cycle-waila-type: + bossbar: + name: "WAILA Display Type: Bossbar (Click to cycle)" + lore: " The WAILA is displayed as a bossbar at the top of your screen" + actionbar: + name: "WAILA Display Type: Actionbar (Click to cycle)" + lore: " The WAILA is displayed on the actionbar above your hotbar" intermediates: name: "↑ Inputs ↑" lore: |- @@ -158,7 +171,7 @@ guide: toggle-block-textures: name: "Toggle Custom Block Textures" lore: " If you have a resource-pack that adds Pylon block textures, enable this to use them, if you don't you can safely disable this, however be aware that this can have negative effects on your fps with large numbers of Pylon blocks" - culling-preset: + cycle-culling-preset: disabled: name: "Culling Preset: No Culling (Click to cycle)" lore: " All Pylon Block entities within view distance will be sent to the client, this can have a large negative effect on fps depending on how many there are, and how good your device is" @@ -174,6 +187,9 @@ guide: toggle-armor-textures: name: "Toggle Custom Armor Textures" lore: " If you have a resource pack that adds worn (not item) Pylon armor textures, enable this to use them, otherwise leave it disabled" + toggle-research-effects: + name: "Toggle Research Effects" + lore: " Should sounds play and confetti appear when you research something new?" recipe: cooking: "Cooking time: %time%" @@ -184,7 +200,10 @@ guide: research-items: "Research items" root: "Pylon guide" search: "Search" - settings_and_info: "Settings & info" + settings: "Settings" + waila_settings: "WAILA settings" + resource_pack_settings: "Resource pack settings" + block_texture_settings: "Block texture settings" item_recipes: "Item recipes" fluid_recipes: "Fluid recipes" item_usages: "Item usages"