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.aqua
import moe.nea.firmament.util.grey import moe.nea.firmament.util.grey
import moe.nea.firmament.util.mc.displayNameAccordingToNbt import moe.nea.firmament.util.mc.displayNameAccordingToNbt
import moe.nea.firmament.util.timestamp
import moe.nea.firmament.util.tr import moe.nea.firmament.util.tr
import moe.nea.firmament.util.unformattedString import moe.nea.firmament.util.unformattedString
object TimerInLore { object TimerInLore {
object TConfig : ManagedConfig("lore-timers", Category.INVENTORY) { object TConfig : ManagedConfig("lore-timers", Category.INVENTORY) {
val showTimers by toggle("show") { true } val showTimers by toggle("show") { true }
val showCreationTimestamp by toggle("show-creation") { true }
val timerFormat by choice("format") { TimerFormat.SOCIALIST } val timerFormat by choice("format") { TimerFormat.SOCIALIST }
} }
@@ -90,6 +92,14 @@ object TimerInLore {
val regex = 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() "(?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 @Subscribe
fun modifyLore(event: ItemTooltipEvent) { fun modifyLore(event: ItemTooltipEvent) {
if (!TConfig.showTimers) return if (!TConfig.showTimers) return
@@ -111,9 +121,13 @@ object TimerInLore {
var baseLine = ZonedDateTime.now(SBData.hypixelTimeZone) var baseLine = ZonedDateTime.now(SBData.hypixelTimeZone)
if (countdownType.isRelative) { if (countdownType.isRelative) {
if (lastTimer == null) { if (lastTimer == null) {
event.lines.add(i + 1, event.lines.add(
tr("firmament.loretimer.missingrelative", i + 1,
"Found a relative countdown with no baseline (Firmament)").grey()) tr(
"firmament.loretimer.missingrelative",
"Found a relative countdown with no baseline (Firmament)"
).grey()
)
continue continue
} }
baseLine = lastTimer baseLine = lastTimer
@@ -123,7 +137,8 @@ object TimerInLore {
lastTimer = timer lastTimer = timer
val localTimer = timer.withZoneSameInstant(ZoneId.systemDefault()) val localTimer = timer.withZoneSameInstant(ZoneId.systemDefault())
// TODO: install approximate time stabilization algorithm // TODO: install approximate time stabilization algorithm
event.lines.add(i + 1, event.lines.add(
i + 1,
Text.literal("${countdownType.label}: ") Text.literal("${countdownType.label}: ")
.grey() .grey()
.append(Text.literal(TConfig.timerFormat.formatter.format(localTimer)).aqua()) .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.NEUIngredient
import io.github.moulberry.repo.data.NEUItem import io.github.moulberry.repo.data.NEUItem
import io.github.moulberry.repo.data.Rarity 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.Optional
import java.util.UUID import java.util.UUID
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@@ -38,7 +43,8 @@ import moe.nea.firmament.util.json.DashlessUUIDSerializer
@Serializable @Serializable
value class SkyblockId(val neuItem: String) : Comparable<SkyblockId> { value class SkyblockId(val neuItem: String) : Comparable<SkyblockId> {
val identifier val identifier
get() = Identifier.of("skyblockitem", get() = Identifier.of(
"skyblockitem",
neuItem.lowercase().replace(";", "__") neuItem.lowercase().replace(";", "__")
.replace(":", "___") .replace(":", "___")
.replace(illlegalPathRegex) { .replace(illlegalPathRegex) {
@@ -137,6 +143,30 @@ fun ItemStack.modifyExtraAttributes(block: (NbtCompound) -> Unit) {
val ItemStack.skyblockUUIDString: String? val ItemStack.skyblockUUIDString: String?
get() = extraAttributes.getString("uuid").getOrNull()?.takeIf { it.isNotBlank() } 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? val ItemStack.skyblockUUID: UUID?
get() = skyblockUUIDString?.let { UUID.fromString(it) } 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.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": "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.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": "Time Format",
"firmament.config.lore-timers.format.choice.american": "§9Ame§cri§fcan", "firmament.config.lore-timers.format.choice.american": "§9Ame§cri§fcan",
"firmament.config.lore-timers.format.choice.local": "System Time Format", "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.choice.socialist": "European-ish",
"firmament.config.lore-timers.format.description": "Choose the time format in which resolved timers are displayed.", "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": "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.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": "Party Commands",
"firmament.config.party-commands.cooldown": "Cooldown", "firmament.config.party-commands.cooldown": "Cooldown",