diff --git a/src/main/kotlin/com/lambda/config/groups/FormatterConfig.kt b/src/main/kotlin/com/lambda/config/groups/FormatterConfig.kt new file mode 100644 index 000000000..c9d824e08 --- /dev/null +++ b/src/main/kotlin/com/lambda/config/groups/FormatterConfig.kt @@ -0,0 +1,94 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.config.groups + +import com.lambda.util.Describable +import com.lambda.util.NamedEnum +import java.time.format.DateTimeFormatter +import java.util.Locale + +interface FormatterConfig { + val locale: Locale + val separator: String + val prefix: String + val postfix: String + val precision: Int + val format: DateTimeFormatter + + enum class Locales( + override val displayName: String, + override val description: String, + val locale: Locale, + ) : NamedEnum, Describable { + France("France", "Numbers are formatted using a space as the thousands separator and a comma as the decimal separator", Locale.FRANCE), + Germany("Germany", "Numbers are formatted using a dot as the thousands separator and a comma as the decimal separator", Locale.GERMANY), + Italy("Italy", "Numbers are formatted using a comma as the thousands separator and a comma as the decimal separator", Locale.ITALY), + Japan("Japan", "Numbers are formatted using a comma as the thousands separator and a dot as the decimal separator", Locale.JAPAN), + Korea("Korea", "Numbers are formatted using a comma as the thousands separator and a dot as the decimal separator", Locale.KOREA), + UK("United Kingdom", "Numbers are formatted using a comma as the thousands separator and a dot as the decimal separator", Locale.UK), + US("United States", "Numbers are formatted using a comma as the thousands separator and a dot as the decimal separator", Locale.US), + Canada("Canada", "Numbers are formatted using a comma as the thousands separator and a dot as the decimal separator", Locale.CANADA), + Quebec("Québec", "Numbers are formatted using a space as the thousands separator and a comma as the decimal separator", Locale.CANADA_FRENCH); // this the best one :3 + } + + enum class Time( + override val displayName: String, + override val description: String, + val format: DateTimeFormatter, + ) : NamedEnum, Describable { + IsoLocalDate("ISO-8601 Extended", "The ISO date formatter that formats or parses a date without an offset, such as '2011-12-03'", DateTimeFormatter.ISO_LOCAL_DATE), + IsoOffsetDate("ISO-8601 Offset", "The ISO date formatter that formats or parses a date with an offset, such as '2011-12-03+01:00'", DateTimeFormatter.ISO_OFFSET_DATE), + IsoDate("ISO-8601 Date", "The ISO date formatter that formats or parses a date with the offset if available, such as '2011-12-03' or '2011-12-03+01:00'", DateTimeFormatter.ISO_DATE), + IsoLocalTime("ISO-8601 Local Time", "The ISO time formatter that formats or parses a time without an offset, such as '10:15' or '10:15:30'", DateTimeFormatter.ISO_LOCAL_TIME), + IsoOffsetTime("ISO-8601 Offset Time", "The ISO time formatter that formats or parses a time with an offset, such as '10:15+01:00' or '10:15:30+01:00'", DateTimeFormatter.ISO_OFFSET_TIME), + IsoTime("ISO-8601 Time", "The ISO time formatter that formats or parses a time, with the offset if available, such as '10:15', '10:15:30' or '10:15:30+01:00'", DateTimeFormatter.ISO_TIME), + IsoLocalDateTime("ISO-8601 Local Date Time", "The ISO date-time formatter that formats or parses a date-time without an offset, such as '2011-12-03T10:15:30'", DateTimeFormatter.ISO_LOCAL_DATE_TIME), + IsoOffsetDateTime("ISO-8601 Offset Date Time", "The ISO date-time formatter that formats or parses a date-time with an offset, such as '2011-12-03T10:15:30+01:00'", DateTimeFormatter.ISO_OFFSET_DATE_TIME), + IsoZonedDateTime("ISO-8601 Zoned Date Time", "The ISO-like date-time formatter that formats or parses a date-time with offset and zone, such as '2011-12-03T10:15:30+01:00[Europe/Paris]'", DateTimeFormatter.ISO_ZONED_DATE_TIME), + IsoDateTime("ISO-8601 Date Time", "The ISO-like date-time formatter that formats or parses a date-time with the offset and zone if available, such as '2011-12-03T10:15:30', '2011-12-03T10:15:30+01:00' or '2011-12-03T10:15:30+01:00[Europe/Paris]'", DateTimeFormatter.ISO_DATE_TIME), + IsoOrdinalDate("ISO-8601 Ordinal Date", "The ISO date formatter that formats or parses the ordinal date without an offset, such as '2012-337'", DateTimeFormatter.ISO_ORDINAL_DATE), + IsoWeekDate("ISO-8601 Week Date", "The ISO date formatter that formats or parses the week-based date without an offset, such as '2012-W48-6'", DateTimeFormatter.ISO_WEEK_DATE), + IsoInstant("ISO-8601 Instant", "The ISO instant formatter that formats or parses an instant in UTC, such as '2011-12-03T10:15:30Z'", DateTimeFormatter.ISO_INSTANT), + BasicIsoDate("ISO 8601", "The ISO date formatter that formats or parses a date without an offset, such as '20111203'", DateTimeFormatter.BASIC_ISO_DATE), + Rfc1123("RFC 1123", "The RFC-1123 date-time formatter, such as 'Tue, 3 Jun 2008 11:05:30 GMT'", DateTimeFormatter.RFC_1123_DATE_TIME); + } + + // For context, a tuple is an ordered list of identical value types such as a vec3d which is a tuple of doubles + enum class TupleSeparator( + override val displayName: String, + val separator: String, + ) : NamedEnum { + Comma("Comma", ", "), + Dot("Dot", ". "), + Semicolon("Semicolon", "; "), + VerticalBar("Vertical Bar", "| "), + Space("Space", " "), + Custom("Custom", ":3c"); + } + + enum class TupleGrouping( + override val displayName: String, + val prefix: String, + val postfix: String, + ) : NamedEnum { + Parentheses("Parenthesis", "(", ")"), + SquareBrackets("Square Brackets", "[", "]"), + CurlyBrackets("Curly Brackets", "{", "}"), + VerticalBar("Vertical Bar", "|", "|"); + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/config/groups/FormatterSettings.kt b/src/main/kotlin/com/lambda/config/groups/FormatterSettings.kt new file mode 100644 index 000000000..3967f7f0a --- /dev/null +++ b/src/main/kotlin/com/lambda/config/groups/FormatterSettings.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.config.groups + +import com.lambda.config.Configurable +import com.lambda.util.NamedEnum +import com.lambda.util.math.Vec2d +import net.minecraft.util.math.Vec2f +import net.minecraft.util.math.Vec3d +import net.minecraft.util.math.Vec3i +import org.joml.Vector3f +import org.joml.Vector4f +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.ZonedDateTime + +class FormatterSettings( + owner: Configurable, + vararg baseGroup: NamedEnum, + vis: () -> Boolean = { true } +) : FormatterConfig, SettingGroup(owner) { + val localeEnum by owner.setting("Locale", FormatterConfig.Locales.US, "The regional formatting used for numbers", vis).group(*baseGroup) + override val locale get() = localeEnum.locale + + val sep by owner.setting("Separator", FormatterConfig.TupleSeparator.Comma, "Separator for string serialization of tuple data structures", vis).group(*baseGroup) + val customSep by owner.setting("Custom Separator", "") { vis() && sep == FormatterConfig.TupleSeparator.Custom }.group(*baseGroup) + override val separator get() = if (sep == FormatterConfig.TupleSeparator.Custom) customSep else sep.separator + + val group by owner.setting("Tuple Prefix", FormatterConfig.TupleGrouping.Parentheses) { vis() }.group(*baseGroup) + override val prefix get() = group.prefix + override val postfix get() = group.postfix + + val floatingPrecision by owner.setting("Floating Precision", 3, 0..6, 1, "Precision for floating point numbers") { vis() }.group(*baseGroup) + override val precision get() = floatingPrecision + + val timeFormat by owner.setting("Time Format", FormatterConfig.Time.IsoDateTime) { vis() }.group(*baseGroup) + override val format get() = timeFormat.format +} \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/module/hud/Coordinates.kt b/src/main/kotlin/com/lambda/module/hud/Coordinates.kt index a20020500..5727d1413 100644 --- a/src/main/kotlin/com/lambda/module/hud/Coordinates.kt +++ b/src/main/kotlin/com/lambda/module/hud/Coordinates.kt @@ -17,14 +17,17 @@ package com.lambda.module.hud +import com.lambda.config.groups.FormatterConfig +import com.lambda.config.groups.FormatterSettings import com.lambda.gui.dsl.ImGuiBuilder import com.lambda.module.HudModule import com.lambda.module.tag.ModuleTag import com.lambda.threading.runSafe -import com.lambda.util.Formatting.asString -import com.lambda.util.Formatting.string +import com.lambda.util.Formatting.format +import com.lambda.util.NamedEnum import com.lambda.util.extension.dimensionName import com.lambda.util.extension.isNether +import com.lambda.util.math.Vec2d import com.lambda.util.math.netherCoord import com.lambda.util.math.overworldCoord @@ -33,19 +36,34 @@ object Coordinates : HudModule( description = "Show your coordinates", tag = ModuleTag.HUD, ) { + private val page by setting("Page", Page.CurrentDimension) private val showDimension by setting("Show Dimension", true) - private val decimals by setting("Decimals", 2, 0..4, 1) + + private val formatter = FormatterSettings(this, Page.CurrentDimension).apply { ::timeFormat.edit { hide() } } + private val otherFormatter = FormatterSettings(this, Page.OtherDimension).apply { + ::timeFormat.edit { hide() } + ::group.edit { defaultValue(FormatterConfig.TupleGrouping.SquareBrackets) } + } override fun ImGuiBuilder.buildLayout() { runSafe { - val pos = player.pos.asString(decimals) - val coord = if (world.isNether) { - "$pos [${player.overworldCoord.x.string}, ${player.overworldCoord.z.string}]" - } else { - "$pos [${player.netherCoord.x.string}, ${player.netherCoord.z.string}]" - } - val dimension = if (showDimension) " ${world.dimensionName}" else "" - textCopyable("$coord$dimension") + val position = player.pos.format(formatter) + val otherDimensionPos = + if (world.isNether) player.overworldCoord.let { Vec2d(it.x, it.z) }.format(otherFormatter) + else player.netherCoord.let { Vec2d(it.x, it.z) }.format(otherFormatter) + + val text = "$position $otherDimensionPos" + + val withDimension = + if (showDimension) "$text ${world.dimensionName}" + else text + + textCopyable(withDimension) } } + + enum class Page(override val displayName: String) : NamedEnum { + CurrentDimension("Current Dimension"), + OtherDimension("Other Dimension"), + } } diff --git a/src/main/kotlin/com/lambda/module/hud/Tps.kt b/src/main/kotlin/com/lambda/module/hud/Tps.kt index fb4a3f075..b490e26b4 100644 --- a/src/main/kotlin/com/lambda/module/hud/Tps.kt +++ b/src/main/kotlin/com/lambda/module/hud/Tps.kt @@ -20,7 +20,7 @@ package com.lambda.module.hud import com.lambda.gui.dsl.ImGuiBuilder import com.lambda.module.HudModule import com.lambda.module.tag.ModuleTag -import com.lambda.util.Formatting.string +import com.lambda.util.Formatting.format import com.lambda.util.ServerTPS import com.lambda.util.ServerTPS.recentData import imgui.ImVec2 @@ -45,10 +45,10 @@ object Tps : HudModule( val current = data.last() val avg = data.average().toFloat() if (!showGraph) { - text("${format.displayName}: ${avg.string}${format.unit}") + text("${format.displayName}: ${avg.format()}${format.unit}") return } - val overlay = "cur ${current.string}${format.unit} | avg ${avg.string}${format.unit}" + val overlay = "cur ${current.format()}${format.unit} | avg ${avg.format()}${format.unit}" plotLines( label = "##TPSPlot", diff --git a/src/main/kotlin/com/lambda/module/modules/combat/AutoDisconnect.kt b/src/main/kotlin/com/lambda/module/modules/combat/AutoDisconnect.kt index bdb6426ec..eb46ab4c6 100644 --- a/src/main/kotlin/com/lambda/module/modules/combat/AutoDisconnect.kt +++ b/src/main/kotlin/com/lambda/module/modules/combat/AutoDisconnect.kt @@ -27,7 +27,7 @@ import com.lambda.module.tag.ModuleTag import com.lambda.sound.SoundManager.playSound import com.lambda.util.Communication import com.lambda.util.Communication.prefix -import com.lambda.util.Formatting.string +import com.lambda.util.Formatting.format import com.lambda.util.combat.CombatUtils.hasDeadlyCrystal import com.lambda.util.combat.DamageUtils.isFallDeadly import com.lambda.util.extension.fullHealth @@ -125,7 +125,7 @@ object AutoDisconnect : Module( private fun SafeContext.damageDisconnect(source: DamageSource, amount: Float) { buildText { literal("Got ") - highlighted(amount.string) + highlighted(amount.format()) literal(" damage of type ") highlighted(source.name) source.attacker?.let { @@ -140,7 +140,7 @@ object AutoDisconnect : Module( } source.position?.let { literal(" at position ") - highlighted(it.string) + highlighted(it.format()) } literal(".") }.let { @@ -160,11 +160,11 @@ object AutoDisconnect : Module( text(text) literal("\n\n") literal("Disconnected at ") - highlighted(player.pos.string) + highlighted(player.pos.format()) literal(" on ") highlighted(Communication.currentTime()) literal(" with ") - highlighted(player.fullHealth.string) + highlighted(player.fullHealth.format()) literal(" health.") if (player.isSubmergedInWater) { literal("\n") @@ -197,7 +197,7 @@ object AutoDisconnect : Module( if (player.fullHealth < minimumHealth) { buildText { literal("Health ") - highlighted(player.fullHealth.string) + highlighted(player.fullHealth.format()) literal(" below minimum of ") highlighted("$minimumHealth") literal("!") @@ -223,7 +223,7 @@ object AutoDisconnect : Module( }?.let { creeper -> buildText { literal("An ignited creeper was ") - highlighted(creeper.pos.distanceTo(player.pos).string) + highlighted(creeper.pos.distanceTo(player.pos).format()) literal(" blocks away!") } } @@ -238,7 +238,7 @@ object AutoDisconnect : Module( literal("The player ") text(otherPlayer.name) literal(" was ") - highlighted("${otherPlayer.distanceTo(player).string} blocks away") + highlighted("${otherPlayer.distanceTo(player).format()} blocks away") literal("!") } } diff --git a/src/main/kotlin/com/lambda/module/modules/player/Replay.kt b/src/main/kotlin/com/lambda/module/modules/player/Replay.kt index 97b3897ad..c65e0f069 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/Replay.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/Replay.kt @@ -47,7 +47,7 @@ import com.lambda.util.Communication.logError import com.lambda.util.Communication.warn import com.lambda.util.FileUtils.locationBoundDirectory import com.lambda.util.FolderRegister -import com.lambda.util.Formatting.asString +import com.lambda.util.Formatting.format import com.lambda.util.Formatting.getTime import com.lambda.util.KeyCode import com.lambda.util.StringUtils.sanitizeForFilename @@ -162,7 +162,7 @@ object Replay : Module( this@Replay.warn( "Position deviates from the recording by ${ "%.3f".format(diff) - } blocks. Desired position: ${pos.asString(3)}" + } blocks. Desired position: ${pos.format()}" ) if (cancelOnDeviation && diff > deviationThreshold) { state = State.Inactive @@ -210,7 +210,7 @@ object Replay : Module( literal(" of ") color(ClickGuiLayout.primaryColor) { literal(saving.duration.toString()) } literal(" at ") - color(ClickGuiLayout.primaryColor) { literal(saving.endPos.asString(1)) } + color(ClickGuiLayout.primaryColor) { literal(saving.endPos.format(precision = 1)) } playMessage(saving) saveMessage(saving) pruneMessage(saving) @@ -398,7 +398,7 @@ object Replay : Module( literal("Checkpoint #") color(ClickGuiLayout.primaryColor) { literal("${recordings.indexOf(checkRec)}") } literal(" created at ") - color(ClickGuiLayout.primaryColor) { literal(checkRec.endPos.asString(0)) } + color(ClickGuiLayout.primaryColor) { literal(checkRec.endPos.format(precision = 0)) } literal(".") playMessage(checkRec) saveMessage(checkRec) @@ -581,8 +581,8 @@ object Replay : Module( ) override fun toString() = "Recording from ${ - startPos.asString(1) - } to ${endPos.asString(1)} (in ${duration})" + startPos.format(precision = 0) + } to ${endPos.format(precision = 0)} (in ${duration})" override fun serialize( src: Recording?, diff --git a/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 11c66b24a..d80a16761 100644 --- a/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -54,7 +54,7 @@ import com.lambda.interaction.request.placing.PlaceRequest import com.lambda.task.Task import com.lambda.task.tasks.EatTask.Companion.eat import com.lambda.threading.runSafeAutomated -import com.lambda.util.Formatting.string +import com.lambda.util.Formatting.format import com.lambda.util.extension.Structure import com.lambda.util.extension.inventorySlots import com.lambda.util.item.ItemUtils.block @@ -70,7 +70,7 @@ class BuildTask private constructor( private val lifeMaintenance: Boolean, automated: Automated ) : Task(), Automated by automated { - override val name: String get() = "Building $blueprint with ${(breaks / (age / 20.0 + 0.001)).string} b/s ${(placements / (age / 20.0 + 0.001)).string} p/s" + override val name: String get() = "Building $blueprint with ${(breaks / (age / 20.0 + 0.001)).format(precision = 1)} b/s ${(placements / (age / 20.0 + 0.001)).format(precision = 1)} p/s" private val pendingInteractions = ConcurrentLinkedQueue() private val atMaxPendingInteractions diff --git a/src/main/kotlin/com/lambda/util/DebugInfoHud.kt b/src/main/kotlin/com/lambda/util/DebugInfoHud.kt index 0723f1413..198eb0a1d 100644 --- a/src/main/kotlin/com/lambda/util/DebugInfoHud.kt +++ b/src/main/kotlin/com/lambda/util/DebugInfoHud.kt @@ -22,7 +22,7 @@ import com.lambda.Lambda.mc import com.lambda.command.CommandRegistry import com.lambda.event.EventFlow import com.lambda.module.ModuleRegistry -import com.lambda.util.Formatting.asString +import com.lambda.util.Formatting.format import com.lambda.util.extension.tickDelta import net.minecraft.util.Formatting import net.minecraft.util.hit.BlockHitResult @@ -55,7 +55,7 @@ object DebugInfoHud { null -> add("Crosshair Target: None") } - add("Eye Pos: ${mc.cameraEntity?.getCameraPosVec(mc.tickDelta.toFloat())?.asString(3)}") + add("Eye Pos: ${mc.cameraEntity?.getCameraPosVec(mc.tickDelta)?.format()}") return } diff --git a/src/main/kotlin/com/lambda/util/Formatting.kt b/src/main/kotlin/com/lambda/util/Formatting.kt index 134d93fdd..2aec6f476 100644 --- a/src/main/kotlin/com/lambda/util/Formatting.kt +++ b/src/main/kotlin/com/lambda/util/Formatting.kt @@ -17,8 +17,12 @@ package com.lambda.util -import net.minecraft.util.math.BlockPos +import com.lambda.config.groups.FormatterConfig +import com.lambda.util.math.Vec2d +import net.minecraft.util.math.Vec2f import net.minecraft.util.math.Vec3d +import net.minecraft.util.math.Vec3i +import java.time.LocalDate import java.time.LocalDateTime import java.time.ZoneId import java.time.ZonedDateTime @@ -26,27 +30,104 @@ import java.time.format.DateTimeFormatter import java.util.* object Formatting { - val Vec3d.string: String - get() = asString() + fun Short.format(formatter: FormatterConfig) = format(formatter.locale) + fun Short.format(locale: Locale = Default.locale) = "%d".format(locale, this) - val Float.string: String - get() = "%.2f".format(Locale.US, this) + fun Int.format(formatter: FormatterConfig) = format(formatter.locale) + fun Int.format(locale: Locale = Default.locale) = "%d".format(locale, this) - val Double.string: String - get() = "%.2f".format(Locale.US, this) + fun Long.format(formatter: FormatterConfig) = format(formatter.locale) + fun Long.format(locale: Locale = Default.locale) = "%d".format(locale, this) - fun Vec3d.asString(decimals: Int = 2): String { - val format = "%.${decimals}f" - return "(${format.format(Locale.US, x)}, ${format.format(Locale.US, y)}, ${format.format(Locale.US, z)})" - } + fun Float.format(formatter: FormatterConfig) = format(formatter.locale, formatter.precision) + fun Float.format(locale: Locale = Default.locale, precision: Int = Default.precision) = "%,.${precision}f".format(locale, this) + + fun Double.format(formatter: FormatterConfig) = format(formatter.locale, formatter.precision) + fun Double.format(locale: Locale = Default.locale, precision: Int = Default.precision) = "%,.${precision}f".format(locale, this) + + fun Vec2f.format(formatter: FormatterConfig) = format(formatter.locale, formatter.separator, formatter.prefix, formatter.postfix, formatter.precision) + fun Vec2f.format(locale: Locale = Default.locale, separator: String = Default.separator, prefix: String = Default.prefix, postfix: String = Default.postfix, precision: Int = Default.precision) = + "$prefix${x.format(locale, precision)}$separator${y.format(locale, precision)}$postfix" + + fun Vec2d.format(formatter: FormatterConfig) = format(formatter.locale, formatter.separator, formatter.prefix, formatter.postfix, formatter.precision) + fun Vec2d.format(locale: Locale = Default.locale, separator: String = Default.separator, prefix: String = Default.prefix, postfix: String = Default.postfix, precision: Int = Default.precision) = + "$prefix${x.format(locale, precision)}$separator${y.format(locale, precision)}$postfix" + + fun Vec3i.format(formatter: FormatterConfig) = format(formatter.locale, formatter.separator, formatter.prefix, formatter.postfix) + fun Vec3i.format(locale: Locale = Default.locale, separator: String = Default.separator, prefix: String = Default.prefix, postfix: String = Default.postfix) = + "$prefix${x.format(locale)}$separator${y.format(locale)}$separator${z.format(locale)}$postfix" + + fun Vec3d.format(formatter: FormatterConfig) = format(formatter.locale, formatter.separator, formatter.prefix, formatter.postfix, formatter.precision) + fun Vec3d.format(locale: Locale = Default.locale, separator: String = Default.separator, prefix: String = Default.prefix, postfix: String = Default.postfix, precision: Int = Default.precision) = + "$prefix${x.format(locale, precision)}$separator${y.format(locale, precision)}$separator${z.format(locale, precision)}$postfix" + + fun ShortArray.format(formatter: FormatterConfig) = format(formatter.locale, formatter.separator, formatter.prefix, formatter.postfix) + fun ShortArray.format(locale: Locale = Default.locale, separator: String = Default.separator, prefix: String = Default.prefix, postfix: String = Default.postfix) = + joinToString(separator, prefix, postfix) { it.format(locale) } + + fun IntArray.format(formatter: FormatterConfig) = format(formatter.locale, formatter.separator, formatter.prefix, formatter.postfix) + fun IntArray.format(locale: Locale = Default.locale, separator: String = Default.separator, prefix: String = Default.prefix, postfix: String = Default.postfix) = + joinToString(separator, prefix, postfix) { it.format(locale) } + + fun LongArray.format(formatter: FormatterConfig) = format(formatter.locale, formatter.separator, formatter.prefix, formatter.postfix) + fun LongArray.format(locale: Locale = Default.locale, separator: String = Default.separator, prefix: String = Default.prefix, postfix: String = Default.postfix) = + joinToString(separator, prefix, postfix) { it.format(locale) } + + fun FloatArray.format(formatter: FormatterConfig) = format(formatter.locale, formatter.separator, formatter.prefix, formatter.postfix, formatter.precision) + fun FloatArray.format(locale: Locale = Default.locale, separator: String = Default.separator, prefix: String = Default.prefix, postfix: String = Default.postfix, precision: Int = Default.precision) = + joinToString(separator, prefix, postfix) { it.format(locale, precision) } - fun BlockPos.asString() = "($x, $y, $z)" + fun DoubleArray.format(formatter: FormatterConfig) = format(formatter.locale, formatter.separator, formatter.prefix, formatter.postfix, formatter.precision) + fun DoubleArray.format(locale: Locale = Default.locale, separator: String = Default.separator, prefix: String = Default.prefix, postfix: String = Default.postfix, precision: Int = Default.precision) = + joinToString(separator, prefix, postfix) { it.format(locale, precision) } - fun getTime(formatter: DateTimeFormatter = DateTimeFormatter.RFC_1123_DATE_TIME): String { + @JvmName("formatVec2fiList1") + fun Iterable.format(formatter: FormatterConfig) = format(formatter.locale, formatter.separator, formatter.prefix, formatter.postfix, formatter.precision) + @JvmName("formatVec2fList2") + fun Iterable.format(locale: Locale = Default.locale, separator: String = Default.separator, prefix: String = Default.prefix, postfix: String = Default.postfix, precision: Int = Default.precision) = + joinToString(separator, prefix, postfix) { it.format(locale, separator, prefix, postfix, precision) } + + @JvmName("formatVec2dList1") + fun Iterable.format(formatter: FormatterConfig) = format(formatter.locale, formatter.separator, formatter.prefix, formatter.postfix, formatter.precision) + @JvmName("formatVec2dList2") + fun Iterable.format(locale: Locale = Default.locale, separator: String = Default.separator, prefix: String = Default.prefix, postfix: String = Default.postfix, precision: Int = Default.precision) = + joinToString(separator, prefix, postfix) { it.format(locale, separator, prefix, postfix, precision) } + + @JvmName("formatVec3iList1") + fun Iterable.format(formatter: FormatterConfig) = format(formatter.locale, formatter.separator, formatter.prefix, formatter.postfix) + @JvmName("formatVec3iList2") + fun Iterable.format(locale: Locale = Default.locale, separator: String = Default.separator, prefix: String = Default.prefix, postfix: String = Default.postfix) = + joinToString(separator, prefix, postfix) { it.format(locale, separator, prefix, postfix) } + + @JvmName("formatVec3dList1") + fun Iterable.format(formatter: FormatterConfig) = format(formatter.locale, formatter.separator, formatter.prefix, formatter.postfix, formatter.precision) + @JvmName("formatVec3dList2") + fun Iterable.format(locale: Locale = Default.locale, separator: String = Default.separator, prefix: String = Default.prefix, postfix: String = Default.postfix, precision: Int = Default.precision) = + joinToString(separator, prefix, postfix) { it.format(locale, separator, prefix, postfix, precision) } + + fun LocalDate.format(formatter: FormatterConfig): String = format(formatter.format) + fun LocalDate.format(format: DateTimeFormatter = Default.format): String = format(format) + + fun LocalDateTime.format(formatter: FormatterConfig): String = format(formatter.format) + fun LocalDateTime.format(format: DateTimeFormatter = Default.format): String = format(format) + + fun ZonedDateTime.format(formatter: FormatterConfig): String = format(formatter.format) + fun ZonedDateTime.format(format: DateTimeFormatter = Default.format): String = format(format) + + fun getTime(formatter: DateTimeFormatter = Default.format): String { val localDateTime = LocalDateTime.now() val zoneId = ZoneId.systemDefault() val zonedDateTime = ZonedDateTime.of(localDateTime, zoneId) return zonedDateTime.format(formatter) } + + object Default : FormatterConfig { + override val locale: Locale = Locale.US + override val separator: String = "," + override val prefix: String = "(" + override val postfix: String = ")" + override val precision: Int = 3 + override val format: DateTimeFormatter = DateTimeFormatter.ISO_DATE_TIME + } }