legacy tag parsing and transformation

This commit is contained in:
nea
2022-07-26 11:42:48 +02:00
parent 372eec27e5
commit 37218614c6
15 changed files with 373 additions and 164 deletions

View File

@@ -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)
}
}
}

View File

@@ -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() {
}
}

View File

@@ -1,7 +1,8 @@
package moe.nea.notenoughupdates.rei package moe.nea.notenoughupdates.rei
import com.mojang.blaze3d.vertex.PoseStack 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.Point
import me.shedaniel.math.Rectangle import me.shedaniel.math.Rectangle
import me.shedaniel.rei.api.client.entry.renderer.EntryRenderer 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.EntryTypeRegistry
import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes
import me.shedaniel.rei.api.common.util.EntryStacks 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.Component
import net.minecraft.network.chat.TextComponent import net.minecraft.network.chat.TextComponent
import net.minecraft.resources.ResourceLocation import net.minecraft.resources.ResourceLocation
import net.minecraft.tags.TagKey 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.ItemStack
import net.minecraft.world.item.Items import net.minecraft.world.item.Items
import net.minecraft.world.item.enchantment.Enchantments import net.minecraft.world.item.enchantment.Enchantments
import java.nio.file.Path import java.util.concurrent.ConcurrentHashMap
import java.util.stream.Stream import java.util.stream.Stream
class NEUReiPlugin : REIClientPlugin { class NEUReiPlugin : REIClientPlugin {
data class SBItem(val sbname: String, val backing: Item)
companion object { companion object {
fun EntryStack<NEUReiPlugin.SBItem>.asItemStack() = fun EntryStack<NEUItem>.asItemEntry(): EntryStack<ItemStack> {
EntryStack.of(VanillaEntryTypes.ITEM, ItemStack(this.value.backing).also { return EntryStack.of(VanillaEntryTypes.ITEM, value.asItemStack())
it.enchant(Enchantments.BINDING_CURSE, 1) }
it.hoverName = TextComponent(value.sbname)
}) 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") val hehe = ResourceLocation("notenoughupdates", "skyblockitems")
} }
object SBItemEntryDefinition : EntryDefinition<SBItem> { object SBItemEntryDefinition : EntryDefinition<NEUItem> {
override fun equals(o1: SBItem?, o2: SBItem?, context: ComparisonContext?): Boolean { override fun equals(o1: NEUItem?, o2: NEUItem?, context: ComparisonContext?): Boolean {
return o1 == o2 return o1 == o2
} }
override fun getValueType(): Class<SBItem> = SBItem::class.java override fun getValueType(): Class<NEUItem> = NEUItem::class.java
override fun getType(): EntryType<SBItem> = override fun getType(): EntryType<NEUItem> =
EntryType.deferred(hehe) EntryType.deferred(hehe)
override fun getRenderer(): EntryRenderer<SBItem> = object : EntryRenderer<SBItem> { override fun getRenderer(): EntryRenderer<NEUItem> = object : EntryRenderer<NEUItem> {
override fun render( override fun render(
entry: EntryStack<SBItem>, entry: EntryStack<NEUItem>,
matrices: PoseStack, matrices: PoseStack,
bounds: Rectangle, bounds: Rectangle,
mouseX: Int, mouseX: Int,
@@ -63,75 +124,64 @@ class NEUReiPlugin : REIClientPlugin {
) { ) {
VanillaEntryTypes.ITEM.definition.renderer VanillaEntryTypes.ITEM.definition.renderer
.render( .render(
entry.asItemStack(), entry.asItemEntry(),
matrices, bounds, mouseX, mouseY, delta 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 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 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() return Stream.empty()
} }
override fun asFormattedText(entry: EntryStack<SBItem>, value: SBItem): Component { override fun asFormattedText(entry: EntryStack<NEUItem>, value: NEUItem): Component {
return VanillaEntryTypes.ITEM.definition.asFormattedText(entry.asItemStack(), ItemStack(value.backing)) return VanillaEntryTypes.ITEM.definition.asFormattedText(entry.asItemEntry(), value.asItemStack())
} }
override fun hash(entry: EntryStack<SBItem>, value: SBItem, context: ComparisonContext): Long { override fun hash(entry: EntryStack<NEUItem>, value: NEUItem, context: ComparisonContext): Long {
return value.sbname.hashCode().toLong() 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 return value
} }
override fun normalize(entry: EntryStack<SBItem>, value: SBItem): SBItem { override fun normalize(entry: EntryStack<NEUItem>, value: NEUItem): NEUItem {
return value return value
} }
override fun copy(entry: EntryStack<SBItem>?, value: SBItem): SBItem { override fun copy(entry: EntryStack<NEUItem>?, value: NEUItem): NEUItem {
return value.copy() return value
} }
override fun isEmpty(entry: EntryStack<SBItem>?, value: SBItem?): Boolean { override fun isEmpty(entry: EntryStack<NEUItem>?, value: NEUItem?): Boolean {
return false return false
} }
override fun getIdentifier(entry: EntryStack<SBItem>?, value: SBItem): ResourceLocation? { override fun getIdentifier(entry: EntryStack<NEUItem>?, value: NEUItem): ResourceLocation {
return ResourceLocation("skyblockitem", value.sbname) return ResourceLocation("skyblockitem", value.skyblockItemId.lowercase().replace(";", "__"))
} }
} }
val neuRepo = NEURepository.of(Path.of("NotEnoughUpdates-REPO")).also {
it.reload()
}
override fun registerEntryTypes(registry: EntryTypeRegistry) { override fun registerEntryTypes(registry: EntryTypeRegistry) {
registry.register(hehe, SBItemEntryDefinition) registry.register(hehe, SBItemEntryDefinition)
} }
override fun registerEntries(registry: EntryRegistry) { override fun registerEntries(registry: EntryRegistry) {
neuRepo.items.items.values.forEach { neuRepo.items.items.values.forEach {
println("Adding item: $it") registry.addEntry(EntryStack.of(SBItemEntryDefinition, it))
registry.addEntry(
EntryStack.of(
SBItemEntryDefinition, SBItem(
it.skyblockItemId.lowercase().replace(";", "__"), Registry.ITEM.get(ResourceLocation(it.minecraftItemId))
)
)
)
} }
registry.addEntry(EntryStacks.of(ItemStack(Items.DIAMOND).also { registry.addEntry(EntryStacks.of(ItemStack(Items.DIAMOND).also {
it.enchant(Enchantments.ALL_DAMAGE_PROTECTION, 10) it.enchant(Enchantments.ALL_DAMAGE_PROTECTION, 10)

View File

@@ -1,30 +0,0 @@
package net.examplemod
import dev.architectury.injectables.annotations.ExpectPlatform
import dev.architectury.platform.Platform
import java.nio.file.Path
object ExampleExpectPlatform {
/**
* We can use [Platform.getConfigFolder] but this is just an example of [ExpectPlatform].
*
*
* This must be a **public static** method. The platform-implemented solution must be placed under a
* platform sub-package, with its class suffixed with `Impl`.
*
*
* Example:
* Expect: net.examplemod.ExampleExpectPlatform#getConfigDirectory()
* Actual Fabric: net.examplemod.fabric.ExampleExpectPlatformImpl#getConfigDirectory()
* Actual Forge: net.examplemod.forge.ExampleExpectPlatformImpl#getConfigDirectory()
*
*
* [You should also get the IntelliJ plugin to help with @ExpectPlatform.](https://plugins.jetbrains.com/plugin/16210-architectury)
*/
@ExpectPlatform
@JvmStatic
fun getConfigDirectory(): Path {
// Just throw an error, the content should get replaced at runtime.
throw AssertionError()
}
}

View File

@@ -1,31 +0,0 @@
package net.examplemod
import com.google.common.base.Suppliers
import dev.architectury.registry.CreativeTabRegistry
import dev.architectury.registry.registries.DeferredRegister
import dev.architectury.registry.registries.Registries
import dev.architectury.registry.registries.RegistrySupplier
import net.minecraft.core.Registry
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.item.CreativeModeTab
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import java.util.function.Supplier
object ExampleMod {
const val MOD_ID = "examplemod"
// We can use this if we don't want to use DeferredRegister
@Suppress("unused")
val REGISTRIES: Supplier<Registries> = Suppliers.memoize { Registries.get(MOD_ID) }
// Registering a new creative tab
val EXAMPLE_TAB: CreativeModeTab = CreativeTabRegistry.create(ResourceLocation(MOD_ID, "example_tab")) { ItemStack(EXAMPLE_ITEM.get()) }
val ITEMS: DeferredRegister<Item> = DeferredRegister.create(MOD_ID, Registry.ITEM_REGISTRY)
val EXAMPLE_ITEM: RegistrySupplier<Item> = ITEMS.register("example_item") { Item(Item.Properties().tab(EXAMPLE_TAB)) }
fun init() {
ITEMS.register()
println(ExampleExpectPlatform.getConfigDirectory().toAbsolutePath().normalize().toString())
}
}

View File

@@ -1,23 +0,0 @@
package net.examplemod.mixin
import net.minecraft.client.gui.screens.TitleScreen
import org.objectweb.asm.Opcodes
import org.spongepowered.asm.mixin.Mixin
import org.spongepowered.asm.mixin.injection.At
import org.spongepowered.asm.mixin.injection.Inject
import org.spongepowered.asm.mixin.injection.Redirect
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo
@Mixin(TitleScreen::class)
class MixinTitleScreen {
@Inject(at = [At("HEAD")], method = ["init()V"])
private fun init(info: CallbackInfo) {
println("Hello from example architectury common mixin!")
}
@Redirect(method = ["render"], at = At("FIELD", target = "minceraftEasterEgg", opcode = Opcodes.GETFIELD))
private fun nextFloat(t: TitleScreen): Boolean {
return true
}
}

View File

@@ -1,3 +0,0 @@
{
"item.examplemod.example_item": "Example Item"
}

View File

@@ -3,11 +3,10 @@
"package": "net.examplemod.mixin", "package": "net.examplemod.mixin",
"compatibilityLevel": "JAVA_16", "compatibilityLevel": "JAVA_16",
"client": [ "client": [
"MixinTitleScreen"
], ],
"mixins": [ "mixins": [
], ],
"injectors": { "injectors": {
"defaultRequire": 1 "defaultRequire": 1
} }
} }

View File

@@ -9,6 +9,11 @@ architectury {
loom { loom {
accessWidenerPath.set(project(":common").loom.accessWidenerPath) accessWidenerPath.set(project(":common").loom.accessWidenerPath)
launches {
named("client") {
property("fabric.log.level", "info")
}
}
} }
/** /**

View File

@@ -0,0 +1,10 @@
package moe.nea.notenoughupdates
import moe.nea.notenoughupdates.NotEnoughUpdates.init
import net.fabricmc.api.ModInitializer
class FabricMain : ModInitializer {
override fun onInitialize() {
init()
}
}

View File

@@ -1,13 +0,0 @@
package net.examplemod.fabric
import net.fabricmc.loader.api.FabricLoader
import java.nio.file.Path
import net.examplemod.ExampleExpectPlatform
object ExampleExpectPlatformImpl {
/**
* This is our actual method to [ExampleExpectPlatform.getConfigDirectory].
*/
@JvmStatic
fun getConfigDirectory(): Path = FabricLoader.getInstance().configDir
}

View File

@@ -1,10 +0,0 @@
package net.examplemod.fabric
import net.examplemod.ExampleMod.init
import net.fabricmc.api.ModInitializer
class ExampleModFabric : ModInitializer {
override fun onInitialize() {
init()
}
}

View File

@@ -16,7 +16,7 @@
"environment": "client", "environment": "client",
"entrypoints": { "entrypoints": {
"main": [ "main": [
"net.examplemod.fabric.ExampleModFabric" "moe.nea.notenoughupdates.FabricMain"
], ],
"rei": [ "rei": [
"moe.nea.notenoughupdates.rei.NEUReiPlugin" "moe.nea.notenoughupdates.rei.NEUReiPlugin"

View File

@@ -1,16 +1,16 @@
package net.examplemod.forge package net.examplemod.forge
import dev.architectury.platform.forge.EventBuses import dev.architectury.platform.forge.EventBuses
import net.examplemod.ExampleMod import moe.nea.notenoughupdates.NotEnoughUpdates
import net.examplemod.ExampleMod.init import moe.nea.notenoughupdates.NotEnoughUpdates.init
import net.minecraftforge.fml.common.Mod import net.minecraftforge.fml.common.Mod
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext
@Mod(ExampleMod.MOD_ID) @Mod(NotEnoughUpdates.MOD_ID)
class ExampleModForge { class ExampleModForge {
init { init {
// Submit our event bus to let architectury register our content on the right time // Submit our event bus to let architectury register our content on the right time
EventBuses.registerModEventBus(ExampleMod.MOD_ID, FMLJavaModLoadingContext.get().modEventBus) EventBuses.registerModEventBus(NotEnoughUpdates.MOD_ID, FMLJavaModLoadingContext.get().modEventBus)
init() init()
} }
} }

View File

@@ -1,6 +1,6 @@
package net.examplemod.quilt package net.examplemod.quilt
import net.examplemod.ExampleMod.init import moe.nea.notenoughupdates.NotEnoughUpdates.init
import org.quiltmc.loader.api.ModContainer import org.quiltmc.loader.api.ModContainer
import org.quiltmc.qsl.base.api.entrypoint.ModInitializer import org.quiltmc.qsl.base.api.entrypoint.ModInitializer
@@ -9,4 +9,4 @@ class ExampleModQuilt : ModInitializer {
override fun onInitialize(mod: ModContainer) { override fun onInitialize(mod: ModContainer) {
init() init()
} }
} }