aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/kotlin/features/inventory/TimerInLore.kt29
-rw-r--r--src/main/kotlin/util/SkyblockId.kt44
-rw-r--r--src/test/kotlin/util/skyblock/TimestampTest.kt28
-rw-r--r--src/test/resources/testdata/items/backpack-in-menu.snbt122
-rw-r--r--translations/en_us.json4
5 files changed, 212 insertions, 15 deletions
diff --git a/src/main/kotlin/features/inventory/TimerInLore.kt b/src/main/kotlin/features/inventory/TimerInLore.kt
index 8eac77f..cc1df9a 100644
--- a/src/main/kotlin/features/inventory/TimerInLore.kt
+++ b/src/main/kotlin/features/inventory/TimerInLore.kt
@@ -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 }
}
@@ -91,6 +93,14 @@ object TimerInLore {
"(?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
var lastTimer: ZonedDateTime? = null
@@ -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())
)
}
}
diff --git a/src/main/kotlin/util/SkyblockId.kt b/src/main/kotlin/util/SkyblockId.kt
index af767f8..8ede97e 100644
--- a/src/main/kotlin/util/SkyblockId.kt
+++ b/src/main/kotlin/util/SkyblockId.kt
@@ -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) }
diff --git a/src/test/kotlin/util/skyblock/TimestampTest.kt b/src/test/kotlin/util/skyblock/TimestampTest.kt
new file mode 100644
index 0000000..b960cb9
--- /dev/null
+++ b/src/test/kotlin/util/skyblock/TimestampTest.kt
@@ -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
+ )
+ }
+}
diff --git a/src/test/resources/testdata/items/backpack-in-menu.snbt b/src/test/resources/testdata/items/backpack-in-menu.snbt
new file mode 100644
index 0000000..2f22768
--- /dev/null
+++ b/src/test/resources/testdata/items/backpack-in-menu.snbt
@@ -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"
+}
diff --git a/translations/en_us.json b/translations/en_us.json
index 1ef9a9b..cc7fb39 100644
--- a/translations/en_us.json
+++ b/translations/en_us.json
@@ -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",