Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ interface RebarAddon : Keyed {
@ApiStatus.NonExtendable
fun registerWithRebar() {
if (!Bukkit.getPluginManager().isPluginEnabled("Rebar")) {
throw IllegalStateException("Rebar is not installed or not enabled")
throw IllegalStateException("Rebar is not installed or not enabled (if Rebar is installed, has it errored?)")
}

RebarRegistry.ADDONS.register(this)
Expand Down
12 changes: 12 additions & 0 deletions rebar/src/main/kotlin/io/github/pylonmc/rebar/config/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,16 @@ class Config(
fun save() {
internalConfig.save(file)
}

override fun modifyException(exception: Exception): Exception = when (exception) {
is KeyNotFoundException -> KeyNotFoundException(
exception.key,
"Key '${exception.key}' not found in config file ${file.absolutePath}"
)

else -> RuntimeException(
"An error occurred while reading config file ${file.absolutePath} (CHECK THE SUB-EXCEPTION BEFORE REPORTING)",
exception
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ open class ConfigSection(val internalSection: ConfigurationSection) {
}

fun getSectionOrThrow(key: String): ConfigSection =
getSection(key) ?: throw KeyNotFoundException(internalSection.currentPath, key)
getSection(key) ?: throw modifyException(KeyNotFoundException(getKeyPath(key)))

/**
* Returns null if the key does not exist or if the value cannot be converted to the desired type.
Expand All @@ -70,21 +70,27 @@ open class ConfigSection(val internalSection: ConfigurationSection) {
*/
fun <T> getOrThrow(key: String, adapter: ConfigAdapter<T>): T {
val value = cache.getOrPut(key) {
val value = internalSection.get(key) ?: throw KeyNotFoundException(internalSection.currentPath, key)
val value = internalSection.get(key) ?: throw modifyException(KeyNotFoundException(getKeyPath(key)))
try {
adapter.convert(value)
} catch (e: KeyNotFoundException) {
val exception = modifyException(KeyNotFoundException("$key.${e.key.removePrefix("$key.")}"))
exception.stackTrace = e.stackTrace
throw exception
} catch (e: Exception) {
throw IllegalArgumentException(
"Failed to convert value '$value' to type ${adapter.type} for key '$key' in section '${internalSection.currentPath}'",
e
throw modifyException(
IllegalArgumentException(
"Failed to convert value '$value' to type ${adapter.type} for key '${getKeyPath(key)}'",
e
)
)
}
}

fun getClass(type: Type): Class<*> = when (type) {
is Class<*> -> type
is ParameterizedType -> getClass(type.rawType)
else -> throw IllegalArgumentException("Unsupported type: $type")
else -> throw modifyException(IllegalArgumentException("Unsupported type: $type"))
}

@Suppress("UNCHECKED_CAST")
Expand All @@ -97,8 +103,8 @@ open class ConfigSection(val internalSection: ConfigurationSection) {
cache.remove(key)
}

fun createSection(key: String): ConfigSection
= ConfigSection(internalSection.createSection(key)).also { sectionCache[key] = it }
fun createSection(key: String): ConfigSection =
ConfigSection(internalSection.createSection(key)).also { sectionCache[key] = it }

/**
* 'Merges' [other] with this ConfigSection by copying all of its keys into this ConfigSection.
Expand All @@ -120,9 +126,17 @@ open class ConfigSection(val internalSection: ConfigurationSection) {
}
}

private fun getKeyPath(key: String): String =
if (internalSection.currentPath.isNullOrEmpty()) key else "${internalSection.currentPath}.$key"

/**
* This exists so that [Config] can add more context to exceptions thrown by this class without having to override every method.
* The default implementation is just `throw exception`.
*/
protected open fun modifyException(exception: Exception): Exception = exception

/**
* Thrown when a key is not found.
*/
class KeyNotFoundException(path: String?, key: String) :
Exception(if (!path.isNullOrEmpty()) "Config key not found: $path.$key" else "Config key not found: $key")
class KeyNotFoundException(val key: String, message: String = "Config key not found: $key") : RuntimeException(message)
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ interface ConfigAdapter<T> {
@JvmField val WEIGHTED_SET = WeightedSetConfigAdapter
@JvmField val CULLING_PRESET = CullingPresetConfigAdapter
@JvmField val WAILA_DISPLAY = WailaDisplayConfigAdapter
@JvmField val CONFIG_SECTION = ConfigSectionConfigAdapter
// @formatter:on
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.github.pylonmc.rebar.config.adapter

import io.github.pylonmc.rebar.config.ConfigSection
import org.bukkit.configuration.ConfigurationSection
import org.bukkit.configuration.MemoryConfiguration

object ConfigSectionConfigAdapter : ConfigAdapter<ConfigSection> {

override val type = ConfigSection::class.java

override fun convert(value: Any): ConfigSection {
val section = if (value is ConfigurationSection) {
value
} else {
val memoryConfig = MemoryConfiguration()
for ((key, value) in MapConfigAdapter.STRING_TO_ANY.convert(value)) {
memoryConfig.set(key, value)
}
memoryConfig
}
return ConfigSection(section)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ object CullingPresetConfigAdapter : ConfigAdapter<CullingPreset> {
override val type: Type = CullingPreset::class.java

override fun convert(value: Any): CullingPreset {
val map = MapConfigAdapter.STRING_TO_ANY.convert(value)
val section = ConfigAdapter.CONFIG_SECTION.convert(value)
return CullingPreset(
index = ConfigAdapter.INT.convert(map["index"] ?: throw IllegalArgumentException("Culling preset is missing 'index' field")),
id = ConfigAdapter.STRING.convert(map["id"] ?: throw IllegalArgumentException("Culling preset is missing 'id' field")),
material = ConfigAdapter.MATERIAL.convert(map["material"] ?: throw IllegalArgumentException("Culling preset is missing 'material' field")),
updateInterval = ConfigAdapter.INT.convert(map["update-interval"] ?: throw IllegalArgumentException("Culling preset is missing 'update-interval' field")),
hiddenInterval = map["hidden-interval"]?.let { ConfigAdapter.INT.convert(it) } ?: 1,
visibleInterval = map["visible-interval"]?.let { ConfigAdapter.INT.convert(it) } ?: 20,
alwaysShowRadius = map["always-show-radius"]?.let { ConfigAdapter.INT.convert(it) } ?: 16,
cullRadius = map["cull-radius"]?.let { ConfigAdapter.INT.convert(it) } ?: 64,
maxOccludingCount = map["max-occluding-count"]?.let { ConfigAdapter.INT.convert(it) } ?: 3
index = section.getOrThrow("index", ConfigAdapter.INT),
id = section.getOrThrow("id", ConfigAdapter.STRING),
material = section.getOrThrow("material", ConfigAdapter.MATERIAL),
updateInterval = section.getOrThrow("update-interval", ConfigAdapter.INT),
hiddenInterval = section.get("hidden-interval", ConfigAdapter.INT, 1),
visibleInterval = section.get("visible-interval", ConfigAdapter.INT, 20),
alwaysShowRadius = section.get("always-show-radius", ConfigAdapter.INT, 16),
cullRadius = section.get("cull-radius", ConfigAdapter.INT, 64),
maxOccludingCount = section.get("max-occluding-count", ConfigAdapter.INT, 3)
)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.github.pylonmc.rebar.config.adapter

import io.github.pylonmc.rebar.config.ConfigSection
import io.github.pylonmc.rebar.util.RandomizedSound
import net.kyori.adventure.key.Key
import net.kyori.adventure.sound.Sound
Expand All @@ -10,40 +11,36 @@ object RandomizedSoundConfigAdapter : ConfigAdapter<RandomizedSound> {
override val type: Type = RandomizedSound::class.java

override fun convert(value: Any): RandomizedSound {
val map = MapConfigAdapter.STRING_TO_ANY.convert(value)
val section = ConfigAdapter.CONFIG_SECTION.convert(value)
val keys = mutableListOf<Key>()
if (map.containsKey("sound")) {
keys.add(Key.key(map["sound"]!! as String))
} else if (map.containsKey("sounds")) {
for (element in map["sounds"] as List<*>) {
keys.add(Key.key(element as String) )
}
} else {
throw IllegalArgumentException("No 'sound' or 'sounds' field found in section: $value")
section.get("sound", ConfigAdapter.NAMESPACED_KEY)?.let(keys::add)
section.get("sounds", ConfigAdapter.LIST.from(ConfigAdapter.NAMESPACED_KEY))?.let(keys::addAll)

if (keys.isEmpty()) {
section.get("sound", ConfigAdapter.STRING) // will report the error
throw AssertionError()
}

return RandomizedSound(
keys,
ConfigAdapter.ENUM.from<Sound.Source>().convert(map["source"] ?: throw IllegalArgumentException("Sound is missing 'source' field")),
getRange(map, "volume"),
getRange(map, "pitch")
section.getOrThrow("source", ConfigAdapter.ENUM.from<Sound.Source>()),
getRange(section, "volume"),
getRange(section, "pitch")
)
}

private fun getRange(section: Map<String, Any>, key: String): Pair<Double, Double> {
val range = section[key] ?: throw IllegalArgumentException("Sound is missing '$key' field")
if (range is ConfigurationSection || range is Map<*, *>) {
val range = MapConfigAdapter.STRING_TO_ANY.convert(range)
return Pair(
ConfigAdapter.DOUBLE.convert(range["min"] ?: throw IllegalArgumentException("Sound is missing '$key.min' field")),
ConfigAdapter.DOUBLE.convert(range["max"] ?: throw IllegalArgumentException("Sound is missing '$key.max' field"))
)
} else if (range is List<*>) {
return Pair(ConfigAdapter.DOUBLE.convert(range[0]!!), ConfigAdapter.DOUBLE.convert(range[1]!!))
} else {
try {
private fun getRange(section: ConfigSection, key: String): Pair<Double, Double> {
return when (val range = section.getOrThrow(key, ConfigAdapter.ANY)) {
is ConfigurationSection, is Map<*, *> -> {
val range = ConfigAdapter.CONFIG_SECTION.convert(range)
range.getOrThrow("min", ConfigAdapter.DOUBLE) to range.getOrThrow("max", ConfigAdapter.DOUBLE)
}

is List<*> -> ConfigAdapter.DOUBLE.convert(range[0]!!) to ConfigAdapter.DOUBLE.convert(range[1]!!)

else -> try {
val value = range.toString().toDouble()
return Pair(value, value)
Pair(value, value)
} catch (_: Throwable) {
throw IllegalArgumentException("Sound '$key' field is not a valid number or range: $range")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
package io.github.pylonmc.rebar.config.adapter

import net.kyori.adventure.key.Key
import net.kyori.adventure.sound.Sound
import net.kyori.adventure.sound.Sound
import java.lang.reflect.Type

object SoundConfigAdapter : ConfigAdapter<Sound> {
override val type: Type = Sound::class.java

override fun convert(value: Any): Sound {
val map = MapConfigAdapter.STRING_TO_ANY.convert(value)
val section = ConfigAdapter.CONFIG_SECTION.convert(value)
return Sound.sound(
Key.key(map["name"] as String),
ConfigAdapter.ENUM.from<Sound.Source>().convert(map["source"] ?: throw IllegalArgumentException("Sound is missing 'source' field")),
ConfigAdapter.FLOAT.convert(map["volume"] ?: throw IllegalArgumentException("Sound is missing 'volume' field")),
ConfigAdapter.FLOAT.convert(map["pitch"] ?: throw IllegalArgumentException("Sound is missing 'pitch' field"))
section.getOrThrow("name", ConfigAdapter.NAMESPACED_KEY),
section.getOrThrow("source", ConfigAdapter.ENUM.from<Sound.Source>()),
section.getOrThrow("volume", ConfigAdapter.FLOAT),
section.getOrThrow("pitch", ConfigAdapter.FLOAT)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ object WailaDisplayConfigAdapter : ConfigAdapter<WailaDisplay> {
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<BossBar.Color>().convert(map["color"] ?: throw IllegalArgumentException("WailaDisplay is missing 'color' field"))
val overlay = ConfigAdapter.ENUM.from<BossBar.Overlay>().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)
val section = ConfigAdapter.CONFIG_SECTION.convert(value)
return WailaDisplay(
text = Component.translatable(section.getOrThrow("text", ConfigAdapter.STRING)),
color = section.getOrThrow("color", ConfigAdapter.ENUM.from<BossBar.Color>()),
overlay = section.getOrThrow("overlay", ConfigAdapter.ENUM.from<BossBar.Overlay>()),
progress = section.getOrThrow("progress", ConfigAdapter.FLOAT)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ class WeightedSetConfigAdapter<E>(private val elementAdapter: ConfigAdapter<E>)
override fun convert(value: Any): WeightedSet<E> {
return if (value is List<*>) {
value.mapTo(WeightedSet()) {
val map = MapConfigAdapter.STRING_TO_ANY.convert(it!!)
val element = elementAdapter.convert(map["value"] ?: throw IllegalArgumentException("Missing 'value' key in weighted set element"))
val weight = ConfigAdapter.FLOAT.convert(map["weight"] ?: 1f)
val section = ConfigAdapter.CONFIG_SECTION.convert(it!!)
val element = section.getOrThrow("value", elementAdapter)
val weight = section.get("weight", ConfigAdapter.FLOAT, 1f)
WeightedSet.Element(element, weight)
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ open class RebarFluid(
for (locale in addon.languages) {
val translationKey = "${key.namespace}.fluid.${key.key}"
if (!addon.translator.canTranslate(translationKey, locale)) {
Rebar.logger.warning("${key.namespace} is missing a translation key for fluid ${key.key} (locale: ${locale.displayName} | expected translation key: $translationKey")
Rebar.logger.warning("${key.namespace} is missing a translation key for fluid ${key.key} (locale: ${locale.displayName} | expected translation key: $translationKey)")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ open class RebarItem(val stack: ItemStack) : Keyed {
ItemStackBuilder.nameKey(
schema.key
)
}"
})"
)
}
}
Expand Down