aboutsummaryrefslogtreecommitdiff
path: root/common/src/main/kotlin/moe
diff options
context:
space:
mode:
Diffstat (limited to 'common/src/main/kotlin/moe')
-rw-r--r--common/src/main/kotlin/moe/nea/notenoughupdates/LegacyTagParser.kt232
-rw-r--r--common/src/main/kotlin/moe/nea/notenoughupdates/NotEnoughUpdates.kt23
-rw-r--r--common/src/main/kotlin/moe/nea/notenoughupdates/rei/NEUReiPlugin.kt138
3 files changed, 349 insertions, 44 deletions
diff --git a/common/src/main/kotlin/moe/nea/notenoughupdates/LegacyTagParser.kt b/common/src/main/kotlin/moe/nea/notenoughupdates/LegacyTagParser.kt
new file mode 100644
index 0000000..809ea4a
--- /dev/null
+++ b/common/src/main/kotlin/moe/nea/notenoughupdates/LegacyTagParser.kt
@@ -0,0 +1,232 @@
+package moe.nea.notenoughupdates
+
+import net.minecraft.nbt.*
+import java.util.*
+
+class LegacyTagParser private constructor(string: String) {
+ data class TagParsingException(val baseString: String, val offset: Int, val mes0: String) :
+ Exception("$mes0 at $offset in `$baseString`.")
+
+ class StringRacer(val backing: String) {
+ var idx = 0
+ val stack = Stack<Int>()
+
+ fun pushState() {
+ stack.push(idx)
+ }
+
+ fun popState() {
+ idx = stack.pop()
+ }
+
+ fun discardState() {
+ stack.pop()
+ }
+
+ fun peek(count: Int): String {
+ return backing.substring(minOf(idx, backing.length), minOf(idx + count, backing.length))
+ }
+
+ fun finished(): Boolean {
+ return peek(1).isEmpty()
+ }
+
+ fun peekReq(count: Int): String? {
+ val p = peek(count)
+ if (p.length != count)
+ return null
+ return p
+ }
+
+ fun consumeCountReq(count: Int): String? {
+ val p = peekReq(count)
+ if (p != null)
+ idx += count
+ return p
+ }
+
+ fun tryConsume(string: String): Boolean {
+ val p = peek(string.length)
+ if (p != string)
+ return false
+ idx += p.length
+ return true
+ }
+
+ fun consumeWhile(shouldConsumeThisString: (String) -> Boolean): String {
+ var lastString: String = ""
+ while (true) {
+ val nextString = lastString + peek(1)
+ if (!shouldConsumeThisString(nextString)) {
+ return lastString
+ }
+ idx++
+ lastString = nextString
+ }
+ }
+
+ fun expect(search: String, errorMessage: String) {
+ if (!tryConsume(search))
+ error(errorMessage)
+ }
+
+ fun error(errorMessage: String): Nothing {
+ throw TagParsingException(backing, idx, errorMessage)
+ }
+
+ }
+
+ val racer = StringRacer(string)
+ val baseTag = parseTag()
+
+ companion object {
+ val digitRange = '0'..'9'
+ fun parse(string: String): CompoundTag {
+ return LegacyTagParser(string).baseTag
+ }
+ }
+
+ fun skipWhitespace() {
+ racer.consumeWhile { Character.isWhitespace(it.last()) } // Only check last since other chars are always checked before.
+ }
+
+ fun parseTag(): CompoundTag {
+ skipWhitespace()
+ racer.expect("{", "Expected '{’ at start of tag")
+ skipWhitespace()
+ val tag = CompoundTag()
+ while (!racer.tryConsume("}")) {
+ skipWhitespace()
+ val lhs = parseIdentifier()
+ skipWhitespace()
+ racer.expect(":", "Expected ':' after identifier in tag")
+ skipWhitespace()
+ val rhs = parseAny()
+ tag.put(lhs, rhs)
+ racer.tryConsume(",")
+ skipWhitespace()
+ }
+ return tag
+ }
+
+ private fun parseAny(): Tag {
+ skipWhitespace()
+ val nextChar = racer.peekReq(1) ?: racer.error("Expected new object, found EOF")
+ return when {
+ nextChar == "{" -> parseTag()
+ nextChar == "[" -> parseList()
+ nextChar == "\"" -> parseStringTag()
+ nextChar.first() in (digitRange) -> parseNumericTag()
+ else -> racer.error("Unexpected token found. Expected start of new element")
+ }
+ }
+
+ fun parseList(): ListTag {
+ skipWhitespace()
+ racer.expect("[", "Expected '[' at start of tag")
+ skipWhitespace()
+ val list = ListTag()
+ while (!racer.tryConsume("]")) {
+ skipWhitespace()
+ racer.pushState()
+ val lhs = racer.consumeWhile { it.all { it in digitRange } }
+ skipWhitespace()
+ if (!racer.tryConsume(":") || lhs.isEmpty()) { // No prefixed 0:
+ racer.popState()
+ list.add(parseAny()) // Reparse our number (or not a number) as actual tag
+ } else {
+ racer.discardState()
+ skipWhitespace()
+ list.add(parseAny()) // Ignore prefix indexes. They should not be generated out of order by any vanilla implementation (which is what NEU should export). Instead append where it appears in order.
+ }
+ skipWhitespace()
+ racer.tryConsume(",")
+ }
+ return list
+ }
+
+ fun parseQuotedString(): String {
+ skipWhitespace()
+ racer.expect("\"", "Expected '\"' at string start")
+ val sb = StringBuilder()
+ while (true) {
+ when (val peek = racer.consumeCountReq(1)) {
+ "\"" -> break
+ "\\" -> {
+ val escaped = racer.consumeCountReq(1) ?: racer.error("Unfinished backslash escape")
+ if (escaped != "\"" && escaped != "\\") {
+ // Surprisingly i couldn't find unicode escapes to be generated by the original minecraft 1.8.9 implementation
+ racer.idx--
+ racer.error("Invalid backslash escape '$escaped'")
+ }
+ sb.append(escaped)
+ }
+ null -> racer.error("Unfinished string")
+ else -> {
+ sb.append(peek)
+ }
+ }
+ }
+ return sb.toString()
+ }
+
+ fun parseStringTag(): StringTag {
+ return StringTag.valueOf(parseQuotedString())
+ }
+
+ object Patterns {
+ val DOUBLE = "([-+]?[0-9]*\\.?[0-9]+)[d|D]".toRegex()
+ val FLOAT = "([-+]?[0-9]*\\.?[0-9]+)[f|F]".toRegex()
+ val BYTE = "([-+]?[0-9]+)[b|B]".toRegex()
+ val LONG = "([-+]?[0-9]+)[l|L]".toRegex()
+ val SHORT = "([-+]?[0-9]+)[s|S]".toRegex()
+ val INTEGER = "([-+]?[0-9]+)".toRegex()
+ val DOUBLE_UNTYPED = "([-+]?[0-9]*\\.?[0-9]+)".toRegex()
+ val ROUGH_PATTERN = "[-+]?[0-9]*\\.?[0-9]+[dDbBfFlLsS]?".toRegex()
+ }
+
+ fun parseNumericTag(): NumericTag {
+ skipWhitespace()
+ val textForm = racer.consumeWhile { Patterns.ROUGH_PATTERN.matchEntire(it) != null }
+ if (textForm.isEmpty()) {
+ racer.error("Expected numeric tag (starting with either -, +, . or a digit")
+ }
+ val doubleMatch = Patterns.DOUBLE.matchEntire(textForm) ?: Patterns.DOUBLE_UNTYPED.matchEntire(textForm)
+ if (doubleMatch != null) {
+ return DoubleTag.valueOf(doubleMatch.groups[1]!!.value.toDouble())
+ }
+ val floatMatch = Patterns.FLOAT.matchEntire(textForm)
+ if (floatMatch != null) {
+ return FloatTag.valueOf(floatMatch.groups[1]!!.value.toFloat())
+ }
+ val byteMatch = Patterns.BYTE.matchEntire(textForm)
+ if (byteMatch != null) {
+ return ByteTag.valueOf(byteMatch.groups[1]!!.value.toByte())
+ }
+ val longMatch = Patterns.LONG.matchEntire(textForm)
+ if (longMatch != null) {
+ return LongTag.valueOf(longMatch.groups[1]!!.value.toLong())
+ }
+ val shortMatch = Patterns.SHORT.matchEntire(textForm)
+ if (shortMatch != null) {
+ return ShortTag.valueOf(shortMatch.groups[1]!!.value.toShort())
+ }
+ val integerMatch = Patterns.INTEGER.matchEntire(textForm)
+ if (integerMatch != null) {
+ return IntTag.valueOf(integerMatch.groups[1]!!.value.toInt())
+ }
+ throw IllegalStateException("Could not properly parse numeric tag '$textForm', despite passing rough verification. This is a bug in the LegacyTagParser")
+ }
+
+ private fun parseIdentifier(): String {
+ skipWhitespace()
+ if (racer.peek(1) == "\"") {
+ return parseQuotedString()
+ }
+ return racer.consumeWhile {
+ val x = it.last()
+ x != ':' && !Character.isWhitespace(x)
+ }
+ }
+
+}
diff --git a/common/src/main/kotlin/moe/nea/notenoughupdates/NotEnoughUpdates.kt b/common/src/main/kotlin/moe/nea/notenoughupdates/NotEnoughUpdates.kt
new file mode 100644
index 0000000..615d25e
--- /dev/null
+++ b/common/src/main/kotlin/moe/nea/notenoughupdates/NotEnoughUpdates.kt
@@ -0,0 +1,23 @@
+package moe.nea.notenoughupdates
+
+import dev.architectury.registry.registries.Registries
+import io.github.moulberry.repo.NEURepository
+import java.nio.file.Path
+
+object NotEnoughUpdates {
+ val REGISTRIES by lazy { Registries.get(MOD_ID) }
+
+
+ const val MOD_ID = "notenoughupdates"
+
+ val neuRepo = NEURepository.of(Path.of("NotEnoughUpdates-REPO")).also {
+ it.reload()
+ }
+
+
+
+
+ fun init() {
+
+ }
+}
diff --git a/common/src/main/kotlin/moe/nea/notenoughupdates/rei/NEUReiPlugin.kt b/common/src/main/kotlin/moe/nea/notenoughupdates/rei/NEUReiPlugin.kt
index de8c689..0fb3621 100644
--- a/common/src/main/kotlin/moe/nea/notenoughupdates/rei/NEUReiPlugin.kt
+++ b/common/src/main/kotlin/moe/nea/notenoughupdates/rei/NEUReiPlugin.kt
@@ -1,7 +1,8 @@
package moe.nea.notenoughupdates.rei
import com.mojang.blaze3d.vertex.PoseStack
-import io.github.moulberry.repo.NEURepository
+import com.mojang.serialization.Dynamic
+import io.github.moulberry.repo.data.NEUItem
import me.shedaniel.math.Point
import me.shedaniel.math.Rectangle
import me.shedaniel.rei.api.client.entry.renderer.EntryRenderer
@@ -16,45 +17,105 @@ import me.shedaniel.rei.api.common.entry.type.EntryType
import me.shedaniel.rei.api.common.entry.type.EntryTypeRegistry
import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes
import me.shedaniel.rei.api.common.util.EntryStacks
-import net.minecraft.core.Registry
+import moe.nea.notenoughupdates.LegacyTagParser
+import moe.nea.notenoughupdates.NotEnoughUpdates.neuRepo
+import net.minecraft.ChatFormatting
+import net.minecraft.nbt.CompoundTag
+import net.minecraft.nbt.NbtOps
+import net.minecraft.nbt.StringTag
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.TextComponent
import net.minecraft.resources.ResourceLocation
import net.minecraft.tags.TagKey
-import net.minecraft.world.item.Item
+import net.minecraft.util.datafix.DataFixers.getDataFixer
+import net.minecraft.util.datafix.fixes.References
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.item.enchantment.Enchantments
-import java.nio.file.Path
+import java.util.concurrent.ConcurrentHashMap
import java.util.stream.Stream
class NEUReiPlugin : REIClientPlugin {
- data class SBItem(val sbname: String, val backing: Item)
companion object {
- fun EntryStack<NEUReiPlugin.SBItem>.asItemStack() =
- EntryStack.of(VanillaEntryTypes.ITEM, ItemStack(this.value.backing).also {
- it.enchant(Enchantments.BINDING_CURSE, 1)
- it.hoverName = TextComponent(value.sbname)
- })
+ fun EntryStack<NEUItem>.asItemEntry(): EntryStack<ItemStack> {
+ return EntryStack.of(VanillaEntryTypes.ITEM, value.asItemStack())
+ }
+
+ fun ItemStack.appendLore(args: List<Component>) {
+ val compoundTag = getOrCreateTagElement("display")
+ val loreList = compoundTag.getList("Lore", StringTag.TAG_STRING.toInt())
+ for (arg in args) {
+ loreList.add(StringTag.valueOf(Component.Serializer.toJson(arg)))
+ }
+ compoundTag.put("Lore", loreList)
+ }
+
+ val cache: MutableMap<String, ItemStack> = ConcurrentHashMap()
+
+ fun NEUItem.asItemStackNow(): ItemStack {
+ val df = getDataFixer()
+ val itemTag1_8_9 = CompoundTag()
+ itemTag1_8_9.put("tag", LegacyTagParser.parse(this.nbttag))
+ itemTag1_8_9.putString("id", this.minecraftItemId)
+ itemTag1_8_9.putByte("Count", 1)
+ itemTag1_8_9.putShort("Damage", this.damage.toShort())
+ val itemTag_modern = try {
+ df.update(
+ References.ITEM_STACK,
+ Dynamic(NbtOps.INSTANCE, itemTag1_8_9),
+ 99,
+ 2975
+ ).value as CompoundTag
+ } catch (e: Exception) {
+ e.printStackTrace()
+ return ItemStack(Items.PAINTING).apply {
+ appendLore(listOf(TextComponent("Exception rendering item: $skyblockItemId")))
+ }
+ }
+ val itemInstance = ItemStack.of(itemTag_modern)
+ return itemInstance.also {
+ if(false)it.appendLore(
+ listOf(
+ TextComponent("Old: $minecraftItemId").withStyle {
+ it.withItalic(false).withColor(ChatFormatting.RED)
+ },
+ TextComponent("Modern: $itemTag_modern").withStyle {
+ it.withItalic(false).withColor(ChatFormatting.RED)
+ },
+ )
+ )
+ it.hoverName = TextComponent(this.skyblockItemId)
+ }
+ }
+
+ fun NEUItem.asItemStack(): ItemStack {
+ var s = cache[this.skyblockItemId]
+ if (s == null) {
+ s = asItemStackNow()
+ cache[this.skyblockItemId] = s
+ }
+ return s
+ }
+
val hehe = ResourceLocation("notenoughupdates", "skyblockitems")
}
- object SBItemEntryDefinition : EntryDefinition<SBItem> {
- override fun equals(o1: SBItem?, o2: SBItem?, context: ComparisonContext?): Boolean {
+ object SBItemEntryDefinition : EntryDefinition<NEUItem> {
+ override fun equals(o1: NEUItem?, o2: NEUItem?, context: ComparisonContext?): Boolean {
return o1 == o2
}
- override fun getValueType(): Class<SBItem> = SBItem::class.java
- override fun getType(): EntryType<SBItem> =
+ override fun getValueType(): Class<NEUItem> = NEUItem::class.java
+ override fun getType(): EntryType<NEUItem> =
EntryType.deferred(hehe)
- override fun getRenderer(): EntryRenderer<SBItem> = object : EntryRenderer<SBItem> {
+ override fun getRenderer(): EntryRenderer<NEUItem> = object : EntryRenderer<NEUItem> {
override fun render(
- entry: EntryStack<SBItem>,
+ entry: EntryStack<NEUItem>,
matrices: PoseStack,
bounds: Rectangle,
mouseX: Int,
@@ -63,75 +124,64 @@ class NEUReiPlugin : REIClientPlugin {
) {
VanillaEntryTypes.ITEM.definition.renderer
.render(
- entry.asItemStack(),
+ entry.asItemEntry(),
matrices, bounds, mouseX, mouseY, delta
)
}
- override fun getTooltip(entry: EntryStack<SBItem>, mouse: Point): Tooltip? {
+ override fun getTooltip(entry: EntryStack<NEUItem>, mouse: Point): Tooltip? {
return VanillaEntryTypes.ITEM.definition.renderer
- .getTooltip(entry.asItemStack(), mouse)
+ .getTooltip(entry.asItemEntry(), mouse)
}
}
- override fun getSerializer(): EntrySerializer<SBItem>? {
+ override fun getSerializer(): EntrySerializer<NEUItem>? {
return null
}
- override fun getTagsFor(entry: EntryStack<SBItem>?, value: SBItem?): Stream<out TagKey<*>> {
+ override fun getTagsFor(entry: EntryStack<NEUItem>?, value: NEUItem?): Stream<out TagKey<*>> {
return Stream.empty()
}
- override fun asFormattedText(entry: EntryStack<SBItem>, value: SBItem): Component {
- return VanillaEntryTypes.ITEM.definition.asFormattedText(entry.asItemStack(), ItemStack(value.backing))
+ override fun asFormattedText(entry: EntryStack<NEUItem>, value: NEUItem): Component {
+ return VanillaEntryTypes.ITEM.definition.asFormattedText(entry.asItemEntry(), value.asItemStack())
}
- override fun hash(entry: EntryStack<SBItem>, value: SBItem, context: ComparisonContext): Long {
- return value.sbname.hashCode().toLong()
+ override fun hash(entry: EntryStack<NEUItem>, value: NEUItem, context: ComparisonContext): Long {
+ return value.skyblockItemId.hashCode().toLong()
}
- override fun wildcard(entry: EntryStack<SBItem>, value: SBItem): SBItem {
+ override fun wildcard(entry: EntryStack<NEUItem>, value: NEUItem): NEUItem {
return value
}
- override fun normalize(entry: EntryStack<SBItem>, value: SBItem): SBItem {
+ override fun normalize(entry: EntryStack<NEUItem>, value: NEUItem): NEUItem {
return value
}
- override fun copy(entry: EntryStack<SBItem>?, value: SBItem): SBItem {
- return value.copy()
+ override fun copy(entry: EntryStack<NEUItem>?, value: NEUItem): NEUItem {
+ return value
}
- override fun isEmpty(entry: EntryStack<SBItem>?, value: SBItem?): Boolean {
+ override fun isEmpty(entry: EntryStack<NEUItem>?, value: NEUItem?): Boolean {
return false
}
- override fun getIdentifier(entry: EntryStack<SBItem>?, value: SBItem): ResourceLocation? {
- return ResourceLocation("skyblockitem", value.sbname)
+ override fun getIdentifier(entry: EntryStack<NEUItem>?, value: NEUItem): ResourceLocation {
+ return ResourceLocation("skyblockitem", value.skyblockItemId.lowercase().replace(";", "__"))
}
}
- val neuRepo = NEURepository.of(Path.of("NotEnoughUpdates-REPO")).also {
- it.reload()
- }
-
override fun registerEntryTypes(registry: EntryTypeRegistry) {
registry.register(hehe, SBItemEntryDefinition)
}
override fun registerEntries(registry: EntryRegistry) {
neuRepo.items.items.values.forEach {
- println("Adding item: $it")
- registry.addEntry(
- EntryStack.of(
- SBItemEntryDefinition, SBItem(
- it.skyblockItemId.lowercase().replace(";", "__"), Registry.ITEM.get(ResourceLocation(it.minecraftItemId))
- )
- )
- )
+ registry.addEntry(EntryStack.of(SBItemEntryDefinition, it))
}
registry.addEntry(EntryStacks.of(ItemStack(Items.DIAMOND).also {
it.enchant(Enchantments.ALL_DAMAGE_PROTECTION, 10)