Skip to content

Commit 17a855a

Browse files
committed
feat: add SBItemData implementation
1 parent 0765c8a commit 17a855a

File tree

14 files changed

+403
-10
lines changed

14 files changed

+403
-10
lines changed

build.gradle.kts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import com.google.common.hash.Hashing
1010
import com.google.devtools.ksp.gradle.KspTaskJvm
1111
import com.google.gson.Gson
1212
import com.google.gson.JsonObject
13+
import dev.whyoleg.sweetspi.gradle.sweetSpiProcessor
14+
import dev.whyoleg.sweetspi.gradle.sweetSpiRuntime
1315
import moe.nea.licenseextractificator.LicenseDiscoveryTask
1416
import moe.nea.mcautotranslations.gradle.CollectTranslations
1517
import net.fabricmc.loom.LoomGradleExtension
@@ -27,6 +29,7 @@ plugins {
2729
alias(libs.plugins.kotlin.plugin.serialization)
2830
alias(libs.plugins.kotlin.plugin.powerassert)
2931
alias(libs.plugins.kotlin.plugin.ksp)
32+
id("dev.whyoleg.sweetspi") version "0.1.2"
3033
// alias(libs.plugins.loom)
3134
// TODO: use arch loom once they update to 1.8
3235
id("fabric-loom") version "1.9.2"
@@ -256,6 +259,8 @@ dependencies {
256259
annotationProcessor(projects.javaplugin)
257260
implementation("com.google.auto.service:auto-service-annotations:1.1.1")
258261
ksp("dev.zacsweers.autoservice:auto-service-ksp:1.2.0")
262+
ksp(sweetSpiProcessor())
263+
include(implementation(sweetSpiRuntime())!!)
259264
include(libs.manninghamMills)
260265
include(libs.moulconfig)
261266

src/compat/rei/java/moe/nea/firmament/compat/rei/HoveredItemStackProvider.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,18 @@ class OverlayHoveredItemStackProvider : HoveredItemStackProvider {
3636
}
3737
}
3838
}
39+
interface CheckedMyServiceProvider {
40+
abstract fun provideService(): MyService
41+
abstract fun shouldProvideService(): Boolean
42+
}
43+
44+
interface MyService : CheckedMyServiceProvider {
45+
abstract fun someServiceFunc()
46+
override fun provideService(): MyService {
47+
return this
48+
}
49+
50+
override fun shouldProvideService(): Boolean {
51+
return true
52+
}
53+
}

src/main/kotlin/commands/rome.kt

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package moe.nea.firmament.commands
33
import com.mojang.brigadier.CommandDispatcher
44
import com.mojang.brigadier.arguments.StringArgumentType.string
55
import io.ktor.client.statement.bodyAsText
6+
import java.util.ServiceLoader
67
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource
8+
import net.minecraft.item.ItemStack
79
import net.minecraft.nbt.NbtOps
810
import net.minecraft.text.Text
911
import net.minecraft.text.TextCodecs
@@ -25,6 +27,10 @@ import moe.nea.firmament.repo.HypixelStaticData
2527
import moe.nea.firmament.repo.ItemCache
2628
import moe.nea.firmament.repo.RepoDownloadManager
2729
import moe.nea.firmament.repo.RepoManager
30+
import moe.nea.firmament.repo.item.SBItemId
31+
import moe.nea.firmament.repo.item.SBItemData
32+
import moe.nea.firmament.repo.item.SBItemProperty
33+
import moe.nea.firmament.repo.item.SBStackSize
2834
import moe.nea.firmament.util.FirmFormatters
2935
import moe.nea.firmament.util.FirmFormatters.debugPath
3036
import moe.nea.firmament.util.FirmFormatters.formatBool
@@ -33,9 +39,15 @@ import moe.nea.firmament.util.SBData
3339
import moe.nea.firmament.util.ScreenUtil
3440
import moe.nea.firmament.util.SkyblockId
3541
import moe.nea.firmament.util.accessors.messages
42+
import moe.nea.firmament.util.blue
3643
import moe.nea.firmament.util.collections.InstanceList
3744
import moe.nea.firmament.util.collections.WeakCache
45+
import moe.nea.firmament.util.gold
46+
import moe.nea.firmament.util.grey
47+
import moe.nea.firmament.util.lime
3848
import moe.nea.firmament.util.mc.SNbtFormatter
49+
import moe.nea.firmament.util.mc.SNbtFormatter.Companion.toPrettyString
50+
import moe.nea.firmament.util.red
3951
import moe.nea.firmament.util.tr
4052
import moe.nea.firmament.util.unformattedString
4153

@@ -223,6 +235,41 @@ fun firmamentCommand() = literal("firmament") {
223235
ScreenUtil.setScreenLater(MiningBlockInfoUi.makeScreen())
224236
}
225237
}
238+
thenLiteral("scratch") {
239+
thenExecute {
240+
val original = SBItemData.fromStack(MC.stackInHand)
241+
original.debugStackCreation().forEach {
242+
println("============== Applied modifier: ${it.lastAppliedModifier} (${it.data}) ==============")
243+
if (it.stack.isEmpty)
244+
println("<empty>")
245+
else
246+
println(ItemStack.CODEC
247+
.encodeStart(MC.currentOrDefaultNbtOps, it.stack)
248+
.orThrow.toPrettyString())
249+
}
250+
println("============== FINISHED ==============")
251+
val roundtripped = original.roundtrip()
252+
fun <T> printProp(prop: SBItemProperty<T>) {
253+
val data = roundtripped.getData(prop)
254+
val oldData = original.getData(prop)
255+
val dataT = Text.literal("${data}")
256+
if (oldData == null)
257+
dataT.gold()
258+
else if (oldData == data)
259+
dataT.lime()
260+
else
261+
dataT.red()
262+
source.sendFeedback(Text.literal("${prop.javaClass.simpleName}")
263+
.blue()
264+
.append(Text.literal(": ").grey())
265+
.append(dataT))
266+
}
267+
SBItemProperty.allProperties.forEach { prop ->
268+
printProp(prop)
269+
}
270+
source.sendFeedback(tr("firmament.itemdebug.done", "Item reconstruction finished, check your console."))
271+
}
272+
}
226273
thenLiteral("dumpchat") {
227274
thenExecute {
228275
MC.inGameHud.chatHud.messages.forEach {
@@ -252,7 +299,8 @@ fun firmamentCommand() = literal("firmament") {
252299
source.sendFeedback(Text.stringifiedTranslatable("firmament.sbinfo.gametype", locrawInfo.gametype))
253300
source.sendFeedback(Text.stringifiedTranslatable("firmament.sbinfo.mode", locrawInfo.mode))
254301
source.sendFeedback(Text.stringifiedTranslatable("firmament.sbinfo.map", locrawInfo.map))
255-
source.sendFeedback(tr("firmament.sbinfo.custommining", "Custom Mining: ${formatBool(locrawInfo.skyblockLocation?.hasCustomMining ?: false)}"))
302+
source.sendFeedback(tr("firmament.sbinfo.custommining",
303+
"Custom Mining: ${formatBool(locrawInfo.skyblockLocation?.hasCustomMining ?: false)}"))
256304
}
257305
}
258306
}

src/main/kotlin/features/debug/PowerUserTools.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ object PowerUserTools : FirmamentFeature {
181181
} else if (it.matches(TConfig.copyItemStack)) {
182182
ClipboardUtils.setTextContent(
183183
ItemStack.CODEC
184-
.encodeStart(MC.currentOrDefaultRegistries.getOps(NbtOps.INSTANCE), item)
184+
.encodeStart(MC.currentOrDefaultNbtOps, item)
185185
.orThrow.toPrettyString())
186186
lastCopiedStack = Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.stack"))
187187
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package moe.nea.firmament.repo.item
2+
3+
import com.google.auto.service.AutoService
4+
import io.github.moulberry.repo.data.NEUItem
5+
import net.minecraft.item.ItemStack
6+
import moe.nea.firmament.util.mc.loreAccordingToNbt
7+
import moe.nea.firmament.util.removeColorCodes
8+
import moe.nea.firmament.util.unformattedString
9+
import moe.nea.firmament.util.useMatch
10+
11+
@AutoService(SBItemProperty::class)
12+
object SBBreakingPower : SBItemProperty<Int>() {
13+
private val BREAKING_POWER_REGEX = "Breaking Power (?<power>[0-9]+)".toPattern()
14+
15+
fun fromLore(string: String?): Int? {
16+
return BREAKING_POWER_REGEX.useMatch(string) {
17+
group("power").toInt()
18+
}
19+
}
20+
21+
override fun fromNeuItem(neuItem: NEUItem, store: SBItemData): Int? {
22+
return fromLore(neuItem.lore.firstOrNull()?.removeColorCodes())
23+
}
24+
25+
override fun fromStack(
26+
stack: ItemStack,
27+
store: SBItemData
28+
): Int? {
29+
return fromLore(stack.loreAccordingToNbt.firstOrNull()?.unformattedString)
30+
}
31+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package moe.nea.firmament.repo.item
2+
3+
import kotlin.collections.runningFold
4+
import net.minecraft.item.ItemStack
5+
import moe.nea.firmament.repo.RepoManager
6+
7+
class SBItemData {
8+
private var itemCache: ItemStack? = null
9+
10+
private val data: MutableMap<SBItemProperty<*>, Any> = mutableMapOf()
11+
12+
fun <T> getData(prop: SBItemProperty<T>): T? {
13+
return data[prop] as T?
14+
}
15+
16+
fun <Prop : SBItemProperty<T>, T> set(property: Prop, data: T) {
17+
if (data != null) {
18+
(this.data as MutableMap<Any, Any>)[property] = data
19+
} else {
20+
this.data.remove(property)
21+
}
22+
itemCache = null
23+
}
24+
25+
private fun <T> enhanceStack(stack: ItemStack, property: SBItemProperty.State<T>): ItemStack {
26+
val data = getData(property)
27+
return property.applyToStack(stack, this, data)
28+
}
29+
30+
private fun createStack(): ItemStack {
31+
return SBItemProperty.allStates.fold(ItemStack.EMPTY) { stack, prop ->
32+
enhanceStack(stack, prop)
33+
}
34+
}
35+
36+
fun debugStackCreation(): List<PartialStack<*>> {
37+
fun <T> combinedEnhanceStack(previous: PartialStack<*>, mod: SBItemProperty.State<T>): PartialStack<T> {
38+
val nextStack = enhanceStack(previous.stack.copy(), mod)
39+
return PartialStack(mod, getData(mod), nextStack)
40+
}
41+
return SBItemProperty.allStates
42+
.runningFold(
43+
PartialStack<Nothing>(null, null, ItemStack.EMPTY)) { stack: PartialStack<*>, prop ->
44+
combinedEnhanceStack(stack, prop)
45+
}
46+
}
47+
48+
/**
49+
* Creates an [ItemStack] based on the current properties. The returned item stack must not be modified by the
50+
* caller.
51+
*/
52+
fun toImmutableStack(): ItemStack {
53+
var cached = itemCache
54+
if (cached == null) {
55+
cached = createStack()
56+
itemCache = cached
57+
}
58+
return cached
59+
}
60+
61+
data class PartialStack<T>(
62+
val lastAppliedModifier: SBItemProperty<T>?,
63+
val data: T?,
64+
val stack: ItemStack,
65+
)
66+
67+
companion object {
68+
/**
69+
* Create an [SBItemData] from only the given characteristica. Any unspecified characteristica will be non-existent.
70+
* If you want to compute all other properties based on the given properties, use [roundtrip].
71+
*/
72+
fun fromCharacteristica(
73+
vararg char: SBItemProperty.BoundState<*>
74+
): SBItemData {
75+
val store = SBItemData()
76+
char.forEach {
77+
it.applyTo(store)
78+
}
79+
return store
80+
}
81+
82+
fun fromStack(itemStack: ItemStack): SBItemData {
83+
val store = SBItemData()
84+
store.loadFrom(itemStack)
85+
return store
86+
}
87+
}
88+
89+
/**
90+
* Creates a new [SBItemData] from the item stack this [SBItemData] produces. This will initialize all properties.
91+
*/
92+
fun roundtrip(): SBItemData {
93+
return fromStack(toImmutableStack())
94+
}
95+
96+
/**
97+
* Creates a new [SBItemData] with cheap inferences completed only by using data available in [io.github.moulberry.repo.data.NEUItem]. This is a cheaper version of [roundtrip], that does not create any [ItemStack]s, and preserves all properties already provided. Check if the property you need overrides [SBItemProperty.fromNeuItem].
98+
*/
99+
fun cheapInfer(): SBItemData {
100+
val neuItem = getData(SBItemId)?.let { RepoManager.getNEUItem(it) } ?: return this
101+
val store = SBItemData()
102+
SBItemProperty.allProperties.forEach {
103+
it.fromNeuItem(neuItem, this)
104+
}
105+
store.data.putAll(this.data)
106+
return store
107+
}
108+
109+
private fun loadFrom(stack: ItemStack) {
110+
SBItemProperty.allProperties.forEach {
111+
loadModifier(stack, it)
112+
}
113+
}
114+
115+
private fun <T> loadModifier(
116+
stack: ItemStack,
117+
modifier: SBItemProperty<T>
118+
) {
119+
val data = modifier.fromStack(stack, this) ?: return
120+
set(modifier, data)
121+
}
122+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package moe.nea.firmament.repo.item
2+
3+
import com.google.auto.service.AutoService
4+
import net.minecraft.item.ItemStack
5+
import moe.nea.firmament.repo.ItemCache.asItemStack
6+
import moe.nea.firmament.repo.RepoManager
7+
import moe.nea.firmament.util.SkyblockId
8+
import moe.nea.firmament.util.skyBlockId
9+
10+
@AutoService(SBItemProperty::class)
11+
object SBItemId : SBItemProperty.State<SkyblockId>() {
12+
13+
override fun fromStack(stack: ItemStack, store: SBItemData): SkyblockId? {
14+
return stack.skyBlockId
15+
}
16+
17+
override fun applyToStack(stack: ItemStack, store: SBItemData, value: SkyblockId?): ItemStack {
18+
val id = value ?: SkyblockId.NULL
19+
return RepoManager.getNEUItem(id).asItemStack(idHint = id)
20+
}
21+
22+
override val order: Int
23+
get() = -10000
24+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package moe.nea.firmament.repo.item
2+
3+
import io.github.moulberry.repo.data.NEUItem
4+
import net.minecraft.item.ItemStack
5+
import moe.nea.firmament.repo.ItemCache.asItemStack
6+
import moe.nea.firmament.util.compatloader.CompatLoader
7+
8+
/**
9+
* A property of a skyblock item. Not every skyblock item must have this property, but some should.
10+
*
11+
* Access to this class should be limited to [State.bindWith] and [SBItemData.getData].
12+
* @see State
13+
*/
14+
abstract class SBItemProperty<T> {
15+
data class BoundState<T>(val property: State<T>, val data: T) {
16+
fun applyTo(store: SBItemData) {
17+
store.set(property, data)
18+
}
19+
}
20+
21+
// TODO: Actually implement and make use of this method.
22+
/**
23+
* Extract this property's state from a [NEUItem]. If this method returns something, it may be equivalent to [fromStack] with the neu item resolved to an item stack according to [asItemStack], but *should not* instantiate an item stack. This method may return null to indicate that it needs a fully constructed item stack to extract a property. This method return one value and then later return another value from [fromStack], but behaviour is generally discouraged.
24+
*/
25+
open fun fromNeuItem(neuItem: NEUItem, store: SBItemData): T? {
26+
return null
27+
}
28+
29+
/**
30+
* Extract this property's state from an [ItemStack]. This should be fully reversible (i.e. all info used to in [fromStack] needs to be set by [State.applyToStack].
31+
*/
32+
abstract fun fromStack(stack: ItemStack, store: SBItemData): T?
33+
34+
/**
35+
* A property of a skyblock item that carriers state. Unlike a plain [SBItemProperty] these modifiers can be used
36+
* to change the state of an item, including its rendering as a vanilla [ItemStack].
37+
*/
38+
abstract class State<T> : SBItemProperty<T>() {
39+
abstract fun applyToStack(stack: ItemStack, store: SBItemData, value: T?): ItemStack
40+
fun bindWith(data: T) = BoundState(this, data)
41+
}
42+
43+
/**
44+
* The order of this property relative to other properties. Lower values get computed first, so higher values may
45+
* rely on their data being stored already (if that item stack has any of that data), and they can overwrite the
46+
* rendering of the lower states.
47+
*/
48+
open val order: Int get() = 0
49+
50+
companion object {
51+
val loader = CompatLoader<SBItemProperty<*>>(SBItemProperty::class)
52+
val allProperties by lazy {
53+
loader.allValidInstances.sortedBy { it.order }
54+
}
55+
val allStates by lazy {
56+
allProperties.filterIsInstance<State<*>>()
57+
}
58+
}
59+
}

0 commit comments

Comments
 (0)