feat: add creation timestamp in lore
This commit is contained in:
@@ -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())
|
||||||
|
|||||||
@@ -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) }
|
||||||
|
|
||||||
|
|||||||
28
src/test/kotlin/util/skyblock/TimestampTest.kt
Normal file
28
src/test/kotlin/util/skyblock/TimestampTest.kt
Normal 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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
122
src/test/resources/testdata/items/backpack-in-menu.snbt
vendored
Normal file
122
src/test/resources/testdata/items/backpack-in-menu.snbt
vendored
Normal 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"
|
||||||
|
}
|
||||||
@@ -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",
|
||||||
|
|||||||
Reference in New Issue
Block a user