diff options
33 files changed, 1611 insertions, 16 deletions
diff --git a/build.gradle.kts b/build.gradle.kts index 38c1b9f..c8c2af7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -73,6 +73,15 @@ repositories { maven("https://maven.notenoughupdates.org/releases") } +kotlin { + sourceSets.all { + languageSettings { +// languageVersion = "2.0" + enableLanguageFeature("BreakContinueInInlineLambdas") + } + } +} + val shadowMe by configurations.creating { exclude(group = "org.jetbrains.kotlin") exclude(group = "org.jetbrains.kotlinx") @@ -109,8 +118,10 @@ dependencies { modImplementation(libs.modmenu) modImplementation(libs.libgui) modImplementation(libs.moulconfig) + modImplementation(libs.manninghamMills) modCompileOnly(libs.explosiveenhancement) include(libs.libgui) + include(libs.manninghamMills) include(libs.moulconfig) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 91f6c00..388d5c6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -27,7 +27,8 @@ jarvis = "1.1.1" nealisp = "1.0.0" explosiveenhancement = "1.2.2-1.20.x" moulconfig = "3.0.0-beta.5" - +manninghamMills = "2.4.1" +notenoughanimations = "ZLjUeuU8" [libraries] minecraft = { module = "com.mojang:minecraft", version.ref = "minecraft" } @@ -46,8 +47,9 @@ jarvis_api = { module = "moe.nea.jarvis:jarvis-api", version.ref = "jarvis" } jarvis_fabric = { module = "moe.nea.jarvis:jarvis-fabric", version.ref = "jarvis" } nealisp = { module = "moe.nea:nealisp", version.ref = "nealisp" } explosiveenhancement = { module = "maven.modrinth:explosive-enhancement", version.ref = "explosiveenhancement" } - +manninghamMills = { module = "me.shedaniel:mm", version.ref = "manninghamMills" } # Runtime: +notenoughanimations = { module = "maven.modrinth:not-enough-animations", version.ref = "notenoughanimations" } hotswap = { module = "virtual.github.hotswapagent:hotswap-agent", version.ref = "hotswap_agent" } architectury_fabric = { module = "dev.architectury:architectury-fabric", version.ref = "architectury" } rei_fabric = { module = "me.shedaniel:RoughlyEnoughItems-fabric", version.ref = "rei" } @@ -61,7 +63,12 @@ freecammod = { module = "maven.modrinth:freecam", version.ref = "freecammod" } [bundles] dbus = ["dbus_java_core", "dbus_java_unixsocket"] -runtime_required = ["architectury_fabric", "rei_fabric"] +runtime_required = [ + "architectury_fabric", + "rei_fabric", + "notenoughanimations", + +] runtime_optional = [ "devauth", # "freecammod", diff --git a/src/main/java/moe/nea/firmament/init/EarlyRiser.java b/src/main/java/moe/nea/firmament/init/EarlyRiser.java new file mode 100644 index 0000000..268e3f3 --- /dev/null +++ b/src/main/java/moe/nea/firmament/init/EarlyRiser.java @@ -0,0 +1,78 @@ +/* + * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe> + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package moe.nea.firmament.init; + +import me.shedaniel.mm.api.ClassTinkerers; +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.MappingResolver; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.*; + +import java.lang.reflect.Modifier; +import java.util.Objects; + +public class EarlyRiser implements Runnable { + MappingResolver remapper = FabricLoader.getInstance().getMappingResolver(); + String PlayerEntity = remapper.mapClassName("intermediary", "net.minecraft.class_1657"); + String World = remapper.mapClassName("intermediary", "net.minecraft.class_1937"); + String GameProfile = "com.mojang.authlib.GameProfile"; + String BlockPos = remapper.mapClassName("intermediary", "net.minecraft.class_2338"); + String AbstractClientPlayerEntity = remapper.mapClassName("intermediary", "net.minecraft.class_742"); + String GuiPlayer = "moe.nea.firmament.gui.entity.GuiPlayer"; + // World world, BlockPos pos, float yaw, GameProfile gameProfile + Type constructorDescriptor = Type.getMethodType(Type.VOID_TYPE, getTypeForClassName(World), getTypeForClassName(BlockPos), Type.FLOAT_TYPE, getTypeForClassName(GameProfile)); + + @Override + public void run() { + ClassTinkerers.addTransformation(AbstractClientPlayerEntity, it -> mapClassNode(it, getTypeForClassName(PlayerEntity))); + ClassTinkerers.addTransformation(GuiPlayer, it -> mapClassNode(it, getTypeForClassName(AbstractClientPlayerEntity))); + } + + private void mapClassNode(ClassNode classNode, Type superClass) { + for (MethodNode method : classNode.methods) { + if (Objects.equals(method.name, "<init>") && Type.getMethodType(method.desc).equals(constructorDescriptor)) { + modifyConstructor(method, superClass); + return; + } + } + var node = new MethodNode(Opcodes.ASM9, "<init>", constructorDescriptor.getDescriptor(), null, null); + classNode.methods.add(node); + modifyConstructor(node, superClass); + } + + private Type getTypeForClassName(String className) { + return Type.getObjectType(className.replace('.', '/')); + } + + private void modifyConstructor(MethodNode method, Type superClass) { + method.access = (method.access | Modifier.PUBLIC) & ~Modifier.PRIVATE & ~Modifier.PROTECTED; + if (method.instructions.size() != 0) return; // Some other mod has already made a constructor here + + // World world, BlockPos pos, float yaw, GameProfile gameProfile + // ALOAD this + method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); + + // ALOAD World + method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1)); + + // ALOAD BlockPos + method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 2)); + + // ALOAD yaw + method.instructions.add(new VarInsnNode(Opcodes.FLOAD, 3)); + + // ALOAD gameProfile + method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 4)); + + // Call super + method.instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, superClass.getInternalName(), "<init>", constructorDescriptor.getDescriptor(), false)); + + // Return + method.instructions.add(new InsnNode(Opcodes.RETURN)); + } +} diff --git a/src/main/java/moe/nea/firmament/mixins/AppendRepoAsResourcePack.java b/src/main/java/moe/nea/firmament/mixins/AppendRepoAsResourcePack.java new file mode 100644 index 0000000..729b9b8 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/AppendRepoAsResourcePack.java @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe> + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package moe.nea.firmament.mixins; + +import moe.nea.firmament.repo.RepoModResourcePack; +import net.fabricmc.fabric.api.resource.ModResourcePack; +import net.fabricmc.fabric.impl.resource.loader.ModResourcePackUtil; +import net.minecraft.resource.ResourceType; +import org.jetbrains.annotations.Nullable; +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.callback.CallbackInfo; + +import java.util.List; + +@Mixin(ModResourcePackUtil.class) +public class AppendRepoAsResourcePack { + @Inject(method = "appendModResourcePacks", at = @At("TAIL")) + private static void onAppendModResourcePack( + List<ModResourcePack> packs, + ResourceType type, + @Nullable String subPath, + CallbackInfo ci + ) { + RepoModResourcePack.Companion.append(packs); + } + +} diff --git a/src/main/java/moe/nea/firmament/mixins/accessor/AccessorAbstractClientPlayerEntity.java b/src/main/java/moe/nea/firmament/mixins/accessor/AccessorAbstractClientPlayerEntity.java new file mode 100644 index 0000000..efa4a56 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/accessor/AccessorAbstractClientPlayerEntity.java @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe> + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package moe.nea.firmament.mixins.accessor; + +import net.minecraft.client.network.AbstractClientPlayerEntity; +import net.minecraft.client.network.PlayerListEntry; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(AbstractClientPlayerEntity.class) +public interface AccessorAbstractClientPlayerEntity { + @Accessor("playerListEntry") + void setPlayerListEntry_firmament(PlayerListEntry playerListEntry); +} diff --git a/src/main/kotlin/moe/nea/firmament/commands/rome.kt b/src/main/kotlin/moe/nea/firmament/commands/rome.kt index 91bdf47..7df39b3 100644 --- a/src/main/kotlin/moe/nea/firmament/commands/rome.kt +++ b/src/main/kotlin/moe/nea/firmament/commands/rome.kt @@ -1,5 +1,6 @@ /* * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe> + * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe> * * SPDX-License-Identifier: GPL-3.0-or-later */ @@ -52,7 +53,12 @@ fun firmamentCommand() = literal("firmament") { val configObj = AllConfigsGui.allConfigs.find { it.name == config } if (configObj == null) { - source.sendFeedback(Text.stringifiedTranslatable("firmament.command.toggle.no-config-found", config)) + source.sendFeedback( + Text.stringifiedTranslatable( + "firmament.command.toggle.no-config-found", + config + ) + ) return@thenExecute } val propertyObj = configObj.allOptions[property] @@ -72,9 +78,11 @@ fun firmamentCommand() = literal("firmament") { propertyObj.value = !propertyObj.value configObj.save() source.sendFeedback( - Text.stringifiedTranslatable("firmament.command.toggle.toggled",configObj.labelText, - propertyObj.labelText, - Text.translatable("firmament.toggle.${propertyObj.value}")) + Text.stringifiedTranslatable( + "firmament.command.toggle.toggled", configObj.labelText, + propertyObj.labelText, + Text.translatable("firmament.toggle.${propertyObj.value}") + ) ) } } @@ -144,22 +152,37 @@ fun firmamentCommand() = literal("firmament") { Text.stringifiedTranslatable("firmament.price.bazaar.productid", bazaarData.productId.bazaarId) ) source.sendFeedback( - Text.stringifiedTranslatable("firmament.price.bazaar.buy.price", FirmFormatters.formatCurrency(bazaarData.quickStatus.buyPrice, 1)) + Text.stringifiedTranslatable( + "firmament.price.bazaar.buy.price", + FirmFormatters.formatCurrency(bazaarData.quickStatus.buyPrice, 1) + ) ) source.sendFeedback( - Text.stringifiedTranslatable("firmament.price.bazaar.buy.order", bazaarData.quickStatus.buyOrders) + Text.stringifiedTranslatable( + "firmament.price.bazaar.buy.order", + bazaarData.quickStatus.buyOrders + ) ) source.sendFeedback( - Text.stringifiedTranslatable("firmament.price.bazaar.sell.price", FirmFormatters.formatCurrency(bazaarData.quickStatus.sellPrice, 1)) + Text.stringifiedTranslatable( + "firmament.price.bazaar.sell.price", + FirmFormatters.formatCurrency(bazaarData.quickStatus.sellPrice, 1) + ) ) source.sendFeedback( - Text.stringifiedTranslatable("firmament.price.bazaar.sell.order", bazaarData.quickStatus.sellOrders) + Text.stringifiedTranslatable( + "firmament.price.bazaar.sell.order", + bazaarData.quickStatus.sellOrders + ) ) } val lowestBin = HypixelStaticData.lowestBin[itemName] if (lowestBin != null) { source.sendFeedback( - Text.stringifiedTranslatable("firmament.price.lowestbin", FirmFormatters.formatCurrency(lowestBin, 1)) + Text.stringifiedTranslatable( + "firmament.price.lowestbin", + FirmFormatters.formatCurrency(lowestBin, 1) + ) ) } } diff --git a/src/main/kotlin/moe/nea/firmament/gui/entity/EntityModifier.kt b/src/main/kotlin/moe/nea/firmament/gui/entity/EntityModifier.kt new file mode 100644 index 0000000..fae3a4b --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/gui/entity/EntityModifier.kt @@ -0,0 +1,14 @@ +/* + * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe> + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package moe.nea.firmament.gui.entity + +import com.google.gson.JsonObject +import net.minecraft.entity.LivingEntity + +fun interface EntityModifier { + fun apply(entity: LivingEntity, info: JsonObject): LivingEntity +} diff --git a/src/main/kotlin/moe/nea/firmament/gui/entity/EntityRenderer.kt b/src/main/kotlin/moe/nea/firmament/gui/entity/EntityRenderer.kt new file mode 100644 index 0000000..d645e5b --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/gui/entity/EntityRenderer.kt @@ -0,0 +1,202 @@ +/* + * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe> + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package moe.nea.firmament.gui.entity + +import com.google.gson.Gson +import com.google.gson.JsonArray +import com.google.gson.JsonObject +import org.apache.logging.log4j.LogManager +import org.joml.Quaternionf +import org.joml.Vector3f +import kotlin.math.atan +import net.minecraft.client.gui.DrawContext +import net.minecraft.client.gui.screen.ingame.InventoryScreen +import net.minecraft.entity.Entity +import net.minecraft.entity.EntityType +import net.minecraft.entity.LivingEntity +import net.minecraft.util.Identifier +import moe.nea.firmament.util.MC +import moe.nea.firmament.util.assertNotNullOr +import moe.nea.firmament.util.iterate +import moe.nea.firmament.util.openFirmamentResource +import moe.nea.firmament.util.render.enableScissorWithTranslation + +object EntityRenderer { + val fakeWorld = FakeWorld() + private fun <T : Entity> t(entityType: EntityType<T>): () -> T { + return { entityType.create(fakeWorld)!! } + } + + val entityIds: Map<String, () -> LivingEntity> = mapOf( + "Zombie" to t(EntityType.ZOMBIE), + "Chicken" to t(EntityType.CHICKEN), + "Slime" to t(EntityType.SLIME), + "Wolf" to t(EntityType.WOLF), + "Skeleton" to t(EntityType.SKELETON), + "Creeper" to t(EntityType.CREEPER), + "Ocelot" to t(EntityType.OCELOT), + "Blaze" to t(EntityType.BLAZE), + "Rabbit" to t(EntityType.RABBIT), + "Sheep" to t(EntityType.SHEEP), + "Horse" to t(EntityType.HORSE), + "Eisengolem" to t(EntityType.IRON_GOLEM), + "Silverfish" to t(EntityType.SILVERFISH), + "Witch" to t(EntityType.WITCH), + "Endermite" to t(EntityType.ENDERMITE), + "Snowman" to t(EntityType.SNOW_GOLEM), + "Villager" to t(EntityType.VILLAGER), + "Guardian" to t(EntityType.GUARDIAN), + "ArmorStand" to t(EntityType.ARMOR_STAND), + "Squid" to t(EntityType.SQUID), + "Bat" to t(EntityType.BAT), + "Spider" to t(EntityType.SPIDER), + "CaveSpider" to t(EntityType.CAVE_SPIDER), + "Pigman" to t(EntityType.ZOMBIFIED_PIGLIN), + "Ghast" to t(EntityType.GHAST), + "MagmaCube" to t(EntityType.MAGMA_CUBE), + "Wither" to t(EntityType.WITHER), + "Enderman" to t(EntityType.ENDERMAN), + "Mooshroom" to t(EntityType.MOOSHROOM), + "WitherSkeleton" to t(EntityType.WITHER_SKELETON), + "Cow" to t(EntityType.COW), + "Dragon" to t(EntityType.ENDER_DRAGON), + "Player" to { makeGuiPlayer(fakeWorld) }, + "Pig" to t(EntityType.PIG), + "Giant" to t(EntityType.GIANT), + ) + val entityModifiers: Map<String, EntityModifier> = mapOf( + "playerdata" to ModifyPlayerSkin, + "equipment" to ModifyEquipment, + "riding" to ModifyRiding, + "charged" to ModifyCharged, + "witherdata" to ModifyWither, + "invisible" to ModifyInvisible, + "age" to ModifyAge, + "horse" to ModifyHorse, + "name" to ModifyName, + ) + + val logger = LogManager.getLogger("Firmament.Entity") + fun applyModifiers(entityId: String, modifiers: List<JsonObject>): LivingEntity? { + val entityType = assertNotNullOr(entityIds[entityId]) { + logger.error("Could not create entity with id $entityId") + return null + } + var entity = entityType() + for (modifierJson in modifiers) { + val modifier = assertNotNullOr(modifierJson["type"]?.asString?.let(entityModifiers::get)) { + logger.error("Unknown modifier $modifierJson") + return null + } + entity = modifier.apply(entity, modifierJson) + } + return entity + } + + fun constructEntity(info: JsonObject): LivingEntity? { + val modifiers = (info["modifiers"] as JsonArray?)?.map { it.asJsonObject } ?: emptyList() + val entityType = assertNotNullOr(info["entity"]?.asString) { + logger.error("Missing entity type on entity object") + return null + } + return applyModifiers(entityType, modifiers) + } + + private val gson = Gson() + fun constructEntity(location: Identifier): LivingEntity? { + return constructEntity( + gson.fromJson( + location.openFirmamentResource().bufferedReader(), JsonObject::class.java + ) + ) + } + + fun renderEntity( + entity: LivingEntity, + renderContext: DrawContext, + posX: Int, + posY: Int, + mouseX: Float, + mouseY: Float + ) { + var bottomOffset = 0.0F + var currentEntity = entity + val maxSize = entity.iterate { it.firstPassenger as? LivingEntity } + .map { it.height } + .sum() + while (true) { + currentEntity.age = MC.player?.age ?: 0 + drawEntity( + renderContext, + posX, + posY, + posX + 50, + posY + 80, + (2F / maxSize * 30).toInt(), + -bottomOffset, + mouseX, + mouseY, + currentEntity + ) + val next = currentEntity.firstPassenger as? LivingEntity ?: break + bottomOffset += currentEntity.getPassengerRidingPos(next).y.toFloat() * 0.75F + currentEntity = next + } + } + + + fun drawEntity( + context: DrawContext, + x1: Int, + y1: Int, + x2: Int, + y2: Int, + size: Int, + bottomOffset: Float, + mouseX: Float, + mouseY: Float, + entity: LivingEntity + ) { + context.enableScissorWithTranslation(x1.toFloat(), y1.toFloat(), x2.toFloat(), y2.toFloat()) + val centerX = (x1 + x2) / 2f + val centerY = (y1 + y2) / 2f + val targetYaw = atan(((centerX - mouseX) / 40.0f).toDouble()).toFloat() + val targetPitch = atan(((centerY - mouseY) / 40.0f).toDouble()).toFloat() + val rotateToFaceTheFront = Quaternionf().rotateZ(Math.PI.toFloat()) + val rotateToFaceTheCamera = Quaternionf().rotateX(targetPitch * 20.0f * (Math.PI.toFloat() / 180)) + rotateToFaceTheFront.mul(rotateToFaceTheCamera) + val oldBodyYaw = entity.bodyYaw + val oldYaw = entity.yaw + val oldPitch = entity.pitch + val oldPrevHeadYaw = entity.prevHeadYaw + val oldHeadYaw = entity.headYaw + entity.bodyYaw = 180.0f + targetYaw * 20.0f + entity.yaw = 180.0f + targetYaw * 40.0f + entity.pitch = -targetPitch * 20.0f + entity.headYaw = entity.yaw + entity.prevHeadYaw = entity.yaw + val vector3f = Vector3f(0.0f, entity.height / 2.0f + bottomOffset, 0.0f) + InventoryScreen.drawEntity( + context, + centerX, + centerY, + size, + vector3f, + rotateToFaceTheFront, + rotateToFaceTheCamera, + entity + ) + entity.bodyYaw = oldBodyYaw + entity.yaw = oldYaw + entity.pitch = oldPitch + entity.prevHeadYaw = oldPrevHeadYaw + entity.headYaw = oldHeadYaw + context.disableScissor() + } + + +} diff --git a/src/main/kotlin/moe/nea/firmament/gui/entity/EntityWidget.kt b/src/main/kotlin/moe/nea/firmament/gui/entity/EntityWidget.kt new file mode 100644 index 0000000..42fa485 --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/gui/entity/EntityWidget.kt @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe> + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package moe.nea.firmament.gui.entity + +import me.shedaniel.math.Dimension +import me.shedaniel.math.Point +import me.shedaniel.math.Rectangle +import me.shedaniel.rei.api.client.gui.widgets.WidgetWithBounds +import net.minecraft.client.gui.DrawContext +import net.minecraft.client.gui.Element +import net.minecraft.entity.LivingEntity + +class EntityWidget(val entity: LivingEntity, val point: Point) : WidgetWithBounds() { + override fun children(): List<Element> { + return emptyList() + } + + var hasErrored = false + + override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) { + try { + if (!hasErrored) + EntityRenderer.renderEntity(entity, context, point.x, point.y, mouseX.toFloat(), mouseY.toFloat()) + } catch (ex: Exception) { + EntityRenderer.logger.error("Failed to render constructed entity: $entity", ex) + hasErrored = true + } + if (hasErrored) { + context.fill(point.x, point.y, point.x + 50, point.y + 80, 0xFFAA2222.toInt()) + } + } + + override fun getBounds(): Rectangle { + return Rectangle(point, Dimension(50, 80)) + } +} diff --git a/src/main/kotlin/moe/nea/firmament/gui/entity/FakeWorld.kt b/src/main/kotlin/moe/nea/firmament/gui/entity/FakeWorld.kt new file mode 100644 index 0000000..4cdfc45 --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/gui/entity/FakeWorld.kt @@ -0,0 +1,491 @@ +/* + * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe> + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package moe.nea.firmament.gui.entity + +import com.mojang.datafixers.util.Pair +import com.mojang.serialization.Lifecycle +import java.util.* +import java.util.function.BooleanSupplier +import java.util.function.Consumer +import java.util.stream.Stream +import kotlin.jvm.optionals.getOrNull +import kotlin.streams.asSequence +import net.minecraft.block.Block +import net.minecraft.block.BlockState +import net.minecraft.client.world.ClientWorld +import net.minecraft.entity.Entity +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.fluid.Fluid +import net.minecraft.item.map.MapState +import net.minecraft.recipe.RecipeManager +import net.minecraft.registry.BuiltinRegistries +import net.minecraft.registry.DynamicRegistryManager +import net.minecraft.registry.Registry +import net.minecraft.registry.RegistryKey +import net.minecraft.registry.RegistryKeys +import net.minecraft.registry.RegistryWrapper +import net.minecraft.registry.entry.RegistryEntry +import net.minecraft.registry.entry.RegistryEntryList +import net.minecraft.registry.entry.RegistryEntryOwner +import net.minecraft.registry.tag.TagKey +import net.minecraft.resource.featuretoggle.FeatureFlag +import net.minecraft.resource.featuretoggle.FeatureFlags +import net.minecraft.resource.featuretoggle.FeatureSet +import net.minecraft.scoreboard.Scoreboard +import net.minecraft.sound.SoundCategory +import net.minecraft.sound.SoundEvent +import net.minecraft.util.Identifier +import net.minecraft.util.TypeFilter +import net.minecraft.util.function.LazyIterationConsumer +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Box +import net.minecraft.util.math.ChunkPos +import net.minecraft.util.math.Direction +import net.minecraft.util.math.Vec3d +import net.minecraft.util.math.random.Random +import net.minecraft.util.profiler.DummyProfiler +import net.minecraft.world.BlockView +import net.minecraft.world.Difficulty +import net.minecraft.world.GameRules +import net.minecraft.world.MutableWorldProperties +import net.minecraft.world.World +import net.minecraft.world.biome.Biome +import net.minecraft.world.biome.BiomeKeys +import net.minecraft.world.chunk.Chunk +import net.minecraft.world.chunk.ChunkManager +import net.minecraft.world.chunk.ChunkStatus +import net.minecraft.world.chunk.EmptyChunk +import net.minecraft.world.chunk.light.LightingProvider +import net.minecraft.world.entity.EntityLookup +import net.minecraft.world.event.GameEvent +import net.minecraft.world.tick.OrderedTick +import net.minecraft.world.tick.QueryableTickScheduler +import net.minecraft.world.tick.TickManager + +fun <T> makeRegistry(registryWrapper: RegistryWrapper.Impl<T>, key: RegistryKey<out Registry<T>>): Registry<T> { + val inverseLookup = registryWrapper.streamEntries() + .asSequence().map { it.value() to it.registryKey() } + .toMap() + val idLookup = registryWrapper.streamEntries() + .asSequence() + .map { it.registryKey() } + .withIndex() + .associate { it.value to it.index } + val map = registryWrapper.streamEntries().asSequence().map { it.registryKey() to it.value() }.toMap(mutableMapOf()) + val inverseIdLookup = idLookup.asIterable().associate { (k, v) -> v to k } + return object : Registry<T> { + override fun get(key: RegistryKey<T>?): T? { + return registryWrapper.getOptional(key).getOrNull()?.value() + } + + override fun iterator(): MutableIterator<T> { + return object : MutableIterator<T> { + val iterator = registryWrapper.streamEntries().iterator() + override fun hasNext(): Boolean { + return iterator.hasNext() + } + + override fun next(): T { + return iterator.next().value() + } + + override fun remove() { + TODO("Not yet implemented") + } + } + } + + override fun getRawId(value: T?): Int { + return idLookup[inverseLookup[value ?: return -1] ?: return -1] ?: return -1 + } + + override fun get(id: Identifier?): T? { + return get(RegistryKey.of(key, id)) + } + + override fun get(index: Int): T? { + return get(inverseIdLookup[index] ?: return null) + } + + override fun size(): Int { + return idLookup.size + } + |
