feat: add creation timestamp in lore

This commit is contained in:
Linnea Gräf
2025-06-26 19:15:38 +02:00
parent 1c5d0df368
commit a8f8382682
5 changed files with 212 additions and 15 deletions

View File

@@ -16,12 +16,14 @@ import moe.nea.firmament.util.SBData
import moe.nea.firmament.util.aqua
import moe.nea.firmament.util.grey
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
import moe.nea.firmament.util.timestamp
import moe.nea.firmament.util.tr
import moe.nea.firmament.util.unformattedString
object TimerInLore {
object TConfig : ManagedConfig("lore-timers", Category.INVENTORY) {
val showTimers by toggle("show") { true }
val showCreationTimestamp by toggle("show-creation") { true }
val timerFormat by choice("format") { TimerFormat.SOCIALIST }
}
@@ -90,6 +92,14 @@ object TimerInLore {
val regex =
"(?i)(?:(?<years>[0-9]+) ?(y|years?) )?(?:(?<days>[0-9]+) ?(d|days?))? ?(?:(?<hours>[0-9]+) ?(h|hours?))? ?(?:(?<minutes>[0-9]+) ?(m|minutes?))? ?(?:(?<seconds>[0-9]+) ?(s|seconds?))?\\b".toRegex()
@Subscribe
fun creationInLore(event: ItemTooltipEvent) {
if (!TConfig.showCreationTimestamp) return
val timestamp = event.stack.timestamp ?: return
val formattedTimestamp = TConfig.timerFormat.formatter.format(ZonedDateTime.ofInstant(timestamp, ZoneId.systemDefault()))
event.lines.add(tr("firmament.lore.creationtimestamp", "Created at: $formattedTimestamp").grey())
}
@Subscribe
fun modifyLore(event: ItemTooltipEvent) {
if (!TConfig.showTimers) return
@@ -111,9 +121,13 @@ object TimerInLore {
var baseLine = ZonedDateTime.now(SBData.hypixelTimeZone)
if (countdownType.isRelative) {
if (lastTimer == null) {
event.lines.add(i + 1,
tr("firmament.loretimer.missingrelative",
"Found a relative countdown with no baseline (Firmament)").grey())
event.lines.add(
i + 1,
tr(
"firmament.loretimer.missingrelative",
"Found a relative countdown with no baseline (Firmament)"
).grey()
)
continue
}
baseLine = lastTimer
@@ -123,10 +137,11 @@ object TimerInLore {
lastTimer = timer
val localTimer = timer.withZoneSameInstant(ZoneId.systemDefault())
// TODO: install approximate time stabilization algorithm
event.lines.add(i + 1,
Text.literal("${countdownType.label}: ")
.grey()
.append(Text.literal(TConfig.timerFormat.formatter.format(localTimer)).aqua())
event.lines.add(
i + 1,
Text.literal("${countdownType.label}: ")
.grey()
.append(Text.literal(TConfig.timerFormat.formatter.format(localTimer)).aqua())
)
}
}

View File

@@ -6,6 +6,11 @@ import com.mojang.serialization.Codec
import io.github.moulberry.repo.data.NEUIngredient
import io.github.moulberry.repo.data.NEUItem
import io.github.moulberry.repo.data.Rarity
import java.time.Instant
import java.time.LocalDateTime
import java.time.format.DateTimeFormatterBuilder
import java.time.format.SignStyle
import java.time.temporal.ChronoField
import java.util.Optional
import java.util.UUID
import kotlinx.serialization.Serializable
@@ -38,13 +43,14 @@ import moe.nea.firmament.util.json.DashlessUUIDSerializer
@Serializable
value class SkyblockId(val neuItem: String) : Comparable<SkyblockId> {
val identifier
get() = Identifier.of("skyblockitem",
neuItem.lowercase().replace(";", "__")
.replace(":", "___")
.replace(illlegalPathRegex) {
it.value.toCharArray()
.joinToString("") { "__" + it.code.toString(16).padStart(4, '0') }
})
get() = Identifier.of(
"skyblockitem",
neuItem.lowercase().replace(";", "__")
.replace(":", "___")
.replace(illlegalPathRegex) {
it.value.toCharArray()
.joinToString("") { "__" + it.code.toString(16).padStart(4, '0') }
})
override fun toString(): String {
return neuItem
@@ -137,6 +143,30 @@ fun ItemStack.modifyExtraAttributes(block: (NbtCompound) -> Unit) {
val ItemStack.skyblockUUIDString: String?
get() = extraAttributes.getString("uuid").getOrNull()?.takeIf { it.isNotBlank() }
private val timestampFormat = //"10/11/21 3:39 PM"
DateTimeFormatterBuilder().apply {
appendValue(ChronoField.MONTH_OF_YEAR, 2)
appendLiteral("/")
appendValue(ChronoField.DAY_OF_MONTH, 2)
appendLiteral("/")
appendValueReduced(ChronoField.YEAR, 2, 2, 1950)
appendLiteral(" ")
appendValue(ChronoField.HOUR_OF_AMPM, 1, 2, SignStyle.NEVER)
appendLiteral(":")
appendValue(ChronoField.MINUTE_OF_HOUR, 2)
appendLiteral(" ")
appendText(ChronoField.AMPM_OF_DAY)
}.toFormatter()
val ItemStack.timestamp
get() =
extraAttributes.getLong("timestamp").getOrNull()?.let { Instant.ofEpochMilli(it) }
?: extraAttributes.getString("timestamp").getOrNull()?.let {
ErrorUtil.catch("Could not parse timestamp $it") {
LocalDateTime.from(timestampFormat.parse(it)).atZone(SBData.hypixelTimeZone)
.toInstant()
}.orNull()
}
val ItemStack.skyblockUUID: UUID?
get() = skyblockUUIDString?.let { UUID.fromString(it) }

View File

@@ -0,0 +1,28 @@
package moe.nea.firmament.test.util.skyblock
import java.time.Instant
import java.time.ZonedDateTime
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import moe.nea.firmament.test.testutil.ItemResources
import moe.nea.firmament.util.SBData
import moe.nea.firmament.util.timestamp
class TimestampTest {
@Test
fun testLongTimestamp() {
Assertions.assertEquals(
Instant.ofEpochSecond(1658091600),
ItemResources.loadItem("hyperion").timestamp
)
}
@Test
fun testStringTimestamp() {
Assertions.assertEquals(
ZonedDateTime.of(2021, 10, 11, 15, 39, 0, 0, SBData.hypixelTimeZone).toInstant(),
ItemResources.loadItem("backpack-in-menu").timestamp
)
}
}

View File

@@ -0,0 +1,122 @@
{
components: {
"minecraft:custom_data": {
backpack_color: "BROWN",
originTag: "CRAFTING_GRID_COLLECT",
timestamp: "10/11/21 3:39 PM",
uuid: "3d7c83e8-c619-4603-8cfb-c95ceed90864"
},
"minecraft:custom_name": {
extra: [
{
color: "gold",
text: "Backpack Slot 3"
}
],
italic: 0b,
text: ""
},
"minecraft:lore": [
{
extra: [
{
color: "gold",
text: "Jumbo Backpack"
}
],
italic: 0b,
text: ""
},
{
extra: [
{
color: "gray",
text: ""
},
{
color: "gray",
text: "This backpack has "
},
{
color: "green",
text: "45"
},
{
color: "gray",
text: " slots."
}
],
italic: 0b,
text: ""
},
{
extra: [
" "
],
italic: 0b,
text: ""
},
{
extra: [
{
color: "gray",
text: ""
},
{
color: "yellow",
text: "Left-click to open!"
}
],
italic: 0b,
text: ""
},
{
extra: [
{
color: "gray",
text: ""
},
{
color: "yellow",
text: "Right-click to remove!"
}
],
italic: 0b,
text: ""
}
],
"minecraft:profile": {
id: [I;
1252359403,
1319582828,
-1927151386,
833492163
],
properties: [
{
name: "textures",
signature: "U/49v6SXIw8bAmqM6T7t1BIR736N3Adpx7MlWncnT8zcFEm97zwRx9/tyaUy/XxBHaPGSL6BbgW2TdBtfb9gf0emCAZyWmnzSTtqDGiWpxnQM8v3+gHS8zD7Xrho0a/hU33xTbQ2knj2iRz8C+FReoJFxCjS++aXq6IqliIb3GhqB5b1egaiG2Q3t+yerl2Xue4nhdYM3wtGsYApC/ClR3TEuBcJv1WUVZM8rEoU29pbVnyMCKineG6mIN7W86SmzcT2SF+zMVyD0/mI7R2hRT2lbXnkMpM6FFscdnlvzjjPB9brtAWY7JGJ63b9C+khnvZUlhlQ/3E/08dFnON31VeabJXOmfrbfAgsF0Hgfs7Io+HzoXSXr/FCxNCCFMWlSwORmG2WCT4VRFzG2SThatPVPGJkuR/tLLOLzXo4RKOMzY5EIwa2XSxRUI4+5z2SZY11ofGic3bZD3wvICs2EZ54Pi508ZOda0qI9w5Q/TazC+jX/I5Nq2TLqLj+uU/+UX8eKXvHdk8QpBynyv9SyHo21jVXpiUgL1AsdzBp9cTZHNJuYtBxgDogr3SyAKPmw3BOzVeUi6qW8k4lgtefLKYteVSh52PjFgvQZUR1GNmFaJ+hlgKz8yONp+wXhw3nyL4dMOd2Z/dVVSywBp0tyHuN5l3PfaInK4s8qSydaW0=",
value: "ewogICJ0aW1lc3RhbXAiIDogMTcxOTUzODgxNTgyNCwKICAicHJvZmlsZUlkIiA6ICJkOWYxNTlhYWYxZjY0NGZlOTEwOTg0NzI2ZDBjMWJjMCIsCiAgInByb2ZpbGVOYW1lIiA6ICJtYW5vbmFtaXNzaW9uRyIsCiAgInNpZ25hdHVyZVJlcXVpcmVkIiA6IHRydWUsCiAgInRleHR1cmVzIiA6IHsKICAgICJTS0lOIiA6IHsKICAgICAgInVybCIgOiAiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS81YWQwYjQwNTIxMjYyYjdhM2Y5OWU2M2JkZGQ0YTNlNTQxOTY1Njc3ZTE0MTRlYWZhMTQyZThiYmE5ZGZlNDgxIiwKICAgICAgIm1ldGFkYXRhIiA6IHsKICAgICAgICAibW9kZWwiIDogInNsaW0iCiAgICAgIH0KICAgIH0KICB9Cn0="
}
]
},
"minecraft:tooltip_display": {
hidden_components: [
"minecraft:jukebox_playable",
"minecraft:painting/variant",
"minecraft:map_id",
"minecraft:fireworks",
"minecraft:attribute_modifiers",
"minecraft:unbreakable",
"minecraft:written_book_content",
"minecraft:banner_patterns",
"minecraft:trim",
"minecraft:potion_contents",
"minecraft:block_entity_data",
"minecraft:dyed_color"
]
}
},
count: 3,
id: "minecraft:player_head"
}

View File

@@ -173,7 +173,7 @@
"firmament.config.jade-integration.blocks.description": "Show custom block descriptions and hardness levels in Jade.",
"firmament.config.jade-integration.progress": "Enable Custom Mining Progress",
"firmament.config.jade-integration.progress.description": "Show the custom mining progress in Jade, when in a world with mining fatigue.",
"firmament.config.lore-timers": "Lore Timers",
"firmament.config.lore-timers": "Item Timestamps",
"firmament.config.lore-timers.format": "Time Format",
"firmament.config.lore-timers.format.choice.american": "§9Ame§cri§fcan",
"firmament.config.lore-timers.format.choice.local": "System Time Format",
@@ -181,6 +181,8 @@
"firmament.config.lore-timers.format.choice.socialist": "European-ish",
"firmament.config.lore-timers.format.description": "Choose the time format in which resolved timers are displayed.",
"firmament.config.lore-timers.show": "Show Lore Timers",
"firmament.config.lore-timers.show-creation": "Show Creation",
"firmament.config.lore-timers.show-creation.description": "Shows the creation or craft timestamp of the item. Sometimes this timestamp is retained when upgrading an item, so it isn't necessarily the craft time of this specific item, but rather one of its components.",
"firmament.config.lore-timers.show.description": "Shows when a timer in a lore (such as interest, auction duration) would end.",
"firmament.config.party-commands": "Party Commands",
"firmament.config.party-commands.cooldown": "Cooldown",