This commit is contained in:
nea
2022-07-29 20:51:53 +02:00
parent 386ee78026
commit f4bf70032a
18 changed files with 119 additions and 274 deletions

View File

@@ -0,0 +1,18 @@
package moe.nea.notenoughupdates
import io.github.moulberry.repo.NEURepository
import net.fabricmc.api.ModInitializer
import java.nio.file.Path
object NotEnoughUpdates : ModInitializer {
val DATA_DIR = Path.of(".notenoughupdates")
const val MOD_ID = "notenoughupdates"
val neuRepo = NEURepository.of(Path.of("NotEnoughUpdates-REPO")).also {
it.reload()
}
override fun onInitialize() {
}
}

View File

@@ -0,0 +1,38 @@
package moe.nea.notenoughupdates.rei
import io.github.moulberry.repo.data.NEUItem
import me.shedaniel.rei.api.client.plugins.REIClientPlugin
import me.shedaniel.rei.api.client.registry.entry.EntryRegistry
import me.shedaniel.rei.api.common.entry.EntryStack
import me.shedaniel.rei.api.common.entry.type.EntryTypeRegistry
import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes
import moe.nea.notenoughupdates.NotEnoughUpdates.neuRepo
import moe.nea.notenoughupdates.repo.ItemCache.asItemStack
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.item.ItemStack
class NEUReiPlugin : REIClientPlugin {
companion object {
fun EntryStack<NEUItem>.asItemEntry(): EntryStack<ItemStack> {
return EntryStack.of(VanillaEntryTypes.ITEM, value.asItemStack())
}
val SKYBLOCK_ITEM_TYPE_ID = ResourceLocation("notenoughupdates", "skyblockitems")
}
override fun registerEntryTypes(registry: EntryTypeRegistry) {
registry.register(SKYBLOCK_ITEM_TYPE_ID, SBItemEntryDefinition)
}
override fun registerEntries(registry: EntryRegistry) {
neuRepo.items.items.values.forEach {
if (!it.isVanilla)
registry.addEntry(EntryStack.of(SBItemEntryDefinition, it))
}
}
}

View File

@@ -0,0 +1,98 @@
package moe.nea.notenoughupdates.rei
import com.mojang.blaze3d.vertex.PoseStack
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
import me.shedaniel.rei.api.client.gui.widgets.Tooltip
import me.shedaniel.rei.api.client.gui.widgets.TooltipContext
import me.shedaniel.rei.api.common.entry.EntrySerializer
import me.shedaniel.rei.api.common.entry.EntryStack
import me.shedaniel.rei.api.common.entry.comparison.ComparisonContext
import me.shedaniel.rei.api.common.entry.type.EntryDefinition
import me.shedaniel.rei.api.common.entry.type.EntryType
import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes
import moe.nea.notenoughupdates.rei.NEUReiPlugin.Companion.asItemEntry
import moe.nea.notenoughupdates.repo.ItemCache.asItemStack
import moe.nea.notenoughupdates.repo.ItemCache.getResourceLocation
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.tags.TagKey
import net.minecraft.world.item.ItemStack
import java.util.stream.Stream
object SBItemEntryDefinition : EntryDefinition<NEUItem> {
override fun equals(o1: NEUItem?, o2: NEUItem?, context: ComparisonContext?): Boolean {
return o1 == o2
}
override fun cheatsAs(entry: EntryStack<NEUItem>?, value: NEUItem?): ItemStack? {
return value?.asItemStack()
}
override fun getValueType(): Class<NEUItem> = NEUItem::class.java
override fun getType(): EntryType<NEUItem> =
EntryType.deferred(NEUReiPlugin.SKYBLOCK_ITEM_TYPE_ID)
override fun getRenderer(): EntryRenderer<NEUItem> = object : EntryRenderer<NEUItem> {
override fun render(
entry: EntryStack<NEUItem>,
matrices: PoseStack,
bounds: Rectangle,
mouseX: Int,
mouseY: Int,
delta: Float
) {
VanillaEntryTypes.ITEM.definition.renderer
.render(
entry.asItemEntry(),
matrices, bounds, mouseX, mouseY, delta
)
}
override fun getTooltip(entry: EntryStack<NEUItem>, tooltipContext: TooltipContext): Tooltip? {
return VanillaEntryTypes.ITEM.definition.renderer
.getTooltip(entry.asItemEntry(), tooltipContext)
}
}
override fun getSerializer(): EntrySerializer<NEUItem>? {
return null
}
override fun getTagsFor(entry: EntryStack<NEUItem>?, value: NEUItem?): Stream<out TagKey<*>> {
return Stream.empty()
}
override fun asFormattedText(entry: EntryStack<NEUItem>, value: NEUItem): Component {
return VanillaEntryTypes.ITEM.definition.asFormattedText(entry.asItemEntry(), value.asItemStack())
}
override fun hash(entry: EntryStack<NEUItem>, value: NEUItem, context: ComparisonContext): Long {
return value.skyblockItemId.hashCode().toLong()
}
override fun wildcard(entry: EntryStack<NEUItem>, value: NEUItem): NEUItem {
return value
}
override fun normalize(entry: EntryStack<NEUItem>, value: NEUItem): NEUItem {
return value
}
override fun copy(entry: EntryStack<NEUItem>?, value: NEUItem): NEUItem {
return value
}
override fun isEmpty(entry: EntryStack<NEUItem>?, value: NEUItem?): Boolean {
return false
}
override fun getIdentifier(entry: EntryStack<NEUItem>?, value: NEUItem): ResourceLocation {
return value.getResourceLocation()
}
}

View File

@@ -0,0 +1,75 @@
package moe.nea.notenoughupdates.repo
import com.mojang.serialization.Dynamic
import io.github.moulberry.repo.IReloadable
import io.github.moulberry.repo.NEURepository
import io.github.moulberry.repo.data.NEUItem
import moe.nea.notenoughupdates.util.LegacyTagParser
import moe.nea.notenoughupdates.util.appendLore
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.NbtOps
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.datafix.DataFixers
import net.minecraft.util.datafix.fixes.References
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import java.util.concurrent.ConcurrentHashMap
object ItemCache : IReloadable {
val cache: MutableMap<String, ItemStack> = ConcurrentHashMap()
val df = DataFixers.getDataFixer()
var isFlawless = true
private fun NEUItem.get10809CompoundTag(): CompoundTag = CompoundTag().apply {
put("tag", LegacyTagParser.parse(nbttag))
putString("id", minecraftItemId)
putByte("Count", 1)
putShort("Damage", damage.toShort())
}
private fun CompoundTag.transformFrom10809ToModern(): CompoundTag? =
try {
df.update(
References.ITEM_STACK,
Dynamic(NbtOps.INSTANCE, this),
-1,
2975
).value as CompoundTag
} catch (e: Exception) {
e.printStackTrace()
isFlawless = false
null
}
private fun NEUItem.asItemStackNow(): ItemStack {
val oldItemTag = get10809CompoundTag()
val modernItemTag = oldItemTag.transformFrom10809ToModern()
?: return ItemStack(Items.PAINTING).apply {
setHoverName(Component.literal(this@asItemStackNow.displayName))
appendLore(listOf(Component.literal("Exception rendering item: $skyblockItemId")))
}
val itemInstance = ItemStack.of(modernItemTag)
if (itemInstance.tag?.contains("Enchantments") == true) {
itemInstance.enchantmentTags.add(CompoundTag())
}
return itemInstance
}
fun NEUItem.asItemStack(): ItemStack {
var s = cache[this.skyblockItemId]
if (s == null) {
s = asItemStackNow()
cache[this.skyblockItemId] = s
}
return s
}
fun NEUItem.getResourceLocation() =
ResourceLocation("skyblockitem", skyblockItemId.lowercase().replace(";", "__"))
override fun reload(repository: NEURepository) {
cache.clear()
}
}

View File

@@ -0,0 +1,17 @@
package moe.nea.notenoughupdates.repo
import moe.nea.notenoughupdates.NotEnoughUpdates
object RepoDownloadManager {
val repoSavedLocation = NotEnoughUpdates.DATA_DIR.resolve("repo-extracted")
val repoMetadataLocation = NotEnoughUpdates.DATA_DIR.resolve("loaded-repo.json")
data class RepoMetadata(
var latestCommit: String,
var user: String,
var repository: String,
var branch: String,
)
}

View File

@@ -0,0 +1,23 @@
package moe.nea.notenoughupdates.util
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.nbt.StringTag
import net.minecraft.network.chat.Component
import net.minecraft.world.item.ItemStack
fun ItemStack.appendLore(args: List<Component>) {
val compoundTag = getOrCreateTagElement("display")
val loreList = compoundTag.getOrCreateList("Lore", StringTag.TAG_STRING)
for (arg in args) {
loreList.add(StringTag.valueOf(Component.Serializer.toJson(arg)))
}
}
fun CompoundTag.getOrCreateList(label: String, tag: Byte): ListTag = getList(label, tag.toInt()).also {
put(label, it)
}
fun CompoundTag.getOrCreateCompoundTag(label: String): CompoundTag = getCompound(label).also {
put(label, it)
}

View File

@@ -0,0 +1,232 @@
package moe.nea.notenoughupdates.util
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)
}
}
}

View File

@@ -0,0 +1,36 @@
{
"schemaVersion": 1,
"id": "notenoughupdates",
"version": "${version}",
"name": "Not Enough Updates",
"description": "Not Enough Updates - A mod for Hypixel Skyblock",
"authors": [
"nea89"
],
"contact": {
"homepage": "https://github.com/romangraef/TODO",
"sources": "https://github.com/romangraef/TODO"
},
"license": "LGPL-3.0",
"icon": "assets/notenoughupdates/icon.png",
"environment": "client",
"entrypoints": {
"main": [
{
"adapter": "kotlin",
"value": "moe.nea.notenoughupdates.NotEnoughUpdates"
}
],
"rei": [
"moe.nea.notenoughupdates.rei.NEUReiPlugin"
]
},
"mixins": [
"notenoughupdates.mixins.json"
],
"depends": {
"fabric": "*",
"fabric-language-kotlin": ">=1.8.2+kotlin.1.7.10",
"minecraft": ">=1.18.2"
}
}

View File

@@ -0,0 +1 @@
accessWidener v2 named

View File

@@ -0,0 +1,12 @@
{
"required": true,
"package": "moe.nea.notenoughupdates.mixins",
"compatibilityLevel": "JAVA_16",
"client": [
],
"mixins": [
],
"injectors": {
"defaultRequire": 1
}
}