diff options
Diffstat (limited to 'src/main/kotlin')
54 files changed, 1918 insertions, 1768 deletions
diff --git a/src/main/kotlin/events/BakeExtraModelsEvent.kt b/src/main/kotlin/events/BakeExtraModelsEvent.kt index f75bedc..adaa495 100644 --- a/src/main/kotlin/events/BakeExtraModelsEvent.kt +++ b/src/main/kotlin/events/BakeExtraModelsEvent.kt @@ -1,21 +1,24 @@ - package moe.nea.firmament.events -import java.util.function.Consumer +import java.util.function.BiConsumer +import net.minecraft.client.render.model.ReferencedModelsCollector import net.minecraft.client.util.ModelIdentifier +import net.minecraft.util.Identifier +// TODO: Rename this event, since it is not really directly baking models anymore class BakeExtraModelsEvent( - private val addItemModel: Consumer<ModelIdentifier>, - private val addAnyModel: Consumer<ModelIdentifier>, + private val addAnyModel: BiConsumer<ModelIdentifier, Identifier>, ) : FirmamentEvent() { - fun addNonItemModel(modelIdentifier: ModelIdentifier) { - this.addAnyModel.accept(modelIdentifier) - } + fun addNonItemModel(modelIdentifier: ModelIdentifier, identifier: Identifier) { + this.addAnyModel.accept(modelIdentifier, identifier) + } - fun addItemModel(modelIdentifier: ModelIdentifier) { - this.addItemModel.accept(modelIdentifier) - } + fun addItemModel(modelIdentifier: ModelIdentifier) { + addNonItemModel( + modelIdentifier, + modelIdentifier.id.withPrefixedPath(ReferencedModelsCollector.ITEM_DIRECTORY)) + } - companion object : FirmamentEventBus<BakeExtraModelsEvent>() + companion object : FirmamentEventBus<BakeExtraModelsEvent>() } diff --git a/src/main/kotlin/events/CustomItemModelEvent.kt b/src/main/kotlin/events/CustomItemModelEvent.kt index e50eca4..4328d77 100644 --- a/src/main/kotlin/events/CustomItemModelEvent.kt +++ b/src/main/kotlin/events/CustomItemModelEvent.kt @@ -2,35 +2,37 @@ package moe.nea.firmament.events import java.util.Optional import kotlin.jvm.optionals.getOrNull +import net.minecraft.client.render.item.ItemModels import net.minecraft.client.render.model.BakedModel -import net.minecraft.client.render.model.BakedModelManager import net.minecraft.client.util.ModelIdentifier import net.minecraft.item.ItemStack +import moe.nea.firmament.util.ErrorUtil import moe.nea.firmament.util.collections.WeakCache data class CustomItemModelEvent( - val itemStack: ItemStack, - var overrideModel: ModelIdentifier? = null, + val itemStack: ItemStack, + var overrideModel: ModelIdentifier? = null, ) : FirmamentEvent() { - companion object : FirmamentEventBus<CustomItemModelEvent>() { - val cache = - WeakCache.memoize<ItemStack, BakedModelManager, Optional<BakedModel>>("CustomItemModels") { stack, models -> - val modelId = getModelIdentifier(stack) ?: return@memoize Optional.empty() - val bakedModel = models.getModel(modelId) - if (bakedModel === models.missingModel) return@memoize Optional.empty() - Optional.of(bakedModel) - } + companion object : FirmamentEventBus<CustomItemModelEvent>() { + val cache = + WeakCache.memoize<ItemStack, ItemModels, Optional<BakedModel>>("CustomItemModels") { stack, models -> + val modelId = getModelIdentifier(stack) ?: return@memoize Optional.empty() + ErrorUtil.softCheck("Model Id needs to have an inventory variant", modelId.variant() == "inventory") + val bakedModel = models.getModel(modelId.id) + if (bakedModel == null || bakedModel === models.missingModelSupplier.get()) return@memoize Optional.empty() + Optional.of(bakedModel) + } - @JvmStatic - fun getModelIdentifier(itemStack: ItemStack?): ModelIdentifier? { - if (itemStack == null) return null - return publish(CustomItemModelEvent(itemStack)).overrideModel - } + @JvmStatic + fun getModelIdentifier(itemStack: ItemStack?): ModelIdentifier? { + if (itemStack == null) return null + return publish(CustomItemModelEvent(itemStack)).overrideModel + } - @JvmStatic - fun getModel(itemStack: ItemStack?, thing: BakedModelManager): BakedModel? { - if (itemStack == null) return null - return cache.invoke(itemStack, thing).getOrNull() - } - } + @JvmStatic + fun getModel(itemStack: ItemStack?, thing: ItemModels): BakedModel? { + if (itemStack == null) return null + return cache.invoke(itemStack, thing).getOrNull() + } + } } diff --git a/src/main/kotlin/events/DebugInstantiateEvent.kt b/src/main/kotlin/events/DebugInstantiateEvent.kt new file mode 100644 index 0000000..3470a8c --- /dev/null +++ b/src/main/kotlin/events/DebugInstantiateEvent.kt @@ -0,0 +1,9 @@ +package moe.nea.firmament.events + +/** + * Called in a devenv after minecraft has been initialized. This event should be used to force instantiation of lazy + * variables (and similar late init) to cause any possible issues to materialize. + */ +class DebugInstantiateEvent : FirmamentEvent() { + companion object : FirmamentEventBus<DebugInstantiateEvent>() +} diff --git a/src/main/kotlin/events/FinalizeResourceManagerEvent.kt b/src/main/kotlin/events/FinalizeResourceManagerEvent.kt index 0d411f1..12167f8 100644 --- a/src/main/kotlin/events/FinalizeResourceManagerEvent.kt +++ b/src/main/kotlin/events/FinalizeResourceManagerEvent.kt @@ -5,31 +5,28 @@ import java.util.concurrent.Executor import net.minecraft.resource.ReloadableResourceManagerImpl import net.minecraft.resource.ResourceManager import net.minecraft.resource.ResourceReloader -import net.minecraft.util.profiler.Profiler data class FinalizeResourceManagerEvent( - val resourceManager: ReloadableResourceManagerImpl, + val resourceManager: ReloadableResourceManagerImpl, ) : FirmamentEvent() { - companion object : FirmamentEventBus<FinalizeResourceManagerEvent>() + companion object : FirmamentEventBus<FinalizeResourceManagerEvent>() - inline fun registerOnApply(name: String, crossinline function: () -> Unit) { - resourceManager.registerReloader(object : ResourceReloader { - override fun reload( - synchronizer: ResourceReloader.Synchronizer, - manager: ResourceManager?, - prepareProfiler: Profiler?, - applyProfiler: Profiler?, - prepareExecutor: Executor?, - applyExecutor: Executor - ): CompletableFuture<Void> { - return CompletableFuture.completedFuture(Unit) - .thenCompose(synchronizer::whenPrepared) - .thenAcceptAsync({ function() }, applyExecutor) - } + inline fun registerOnApply(name: String, crossinline function: () -> Unit) { + resourceManager.registerReloader(object : ResourceReloader { + override fun reload( + synchronizer: ResourceReloader.Synchronizer, + manager: ResourceManager, + prepareExecutor: Executor, + applyExecutor: Executor + ): CompletableFuture<Void> { + return CompletableFuture.completedFuture(Unit) + .thenCompose(synchronizer::whenPrepared) + .thenAcceptAsync({ function() }, applyExecutor) + } - override fun getName(): String { - return name - } - }) - } + override fun getName(): String { + return name + } + }) + } } diff --git a/src/main/kotlin/events/IsSlotProtectedEvent.kt b/src/main/kotlin/events/IsSlotProtectedEvent.kt index cd431f7..cd2b676 100644 --- a/src/main/kotlin/events/IsSlotProtectedEvent.kt +++ b/src/main/kotlin/events/IsSlotProtectedEvent.kt @@ -37,7 +37,7 @@ data class IsSlotProtectedEvent( val event = IsSlotProtectedEvent(slot, action, false, itemStackOverride) publish(event) if (event.isProtected && !event.silent) { - MC.player?.sendMessage(Text.translatable("firmament.protectitem").append(event.itemStack.name)) + MC.sendChat(Text.translatable("firmament.protectitem").append(event.itemStack.name)) CommonSoundEffects.playFailure() } return event.isProtected diff --git a/src/main/kotlin/events/RegisterCustomShadersEvent.kt b/src/main/kotlin/events/RegisterCustomShadersEvent.kt deleted file mode 100644 index 2f6d1f8..0000000 --- a/src/main/kotlin/events/RegisterCustomShadersEvent.kt +++ /dev/null @@ -1,24 +0,0 @@ -package moe.nea.firmament.events - -import com.mojang.datafixers.util.Pair -import java.util.function.Consumer -import net.minecraft.client.gl.ShaderProgram -import net.minecraft.client.render.VertexFormat -import net.minecraft.resource.ResourceFactory -import moe.nea.firmament.Firmament - -data class RegisterCustomShadersEvent( - val list: MutableList<Pair<ShaderProgram, Consumer<ShaderProgram>>>, - val resourceFactory: ResourceFactory, -) : FirmamentEvent() { - companion object : FirmamentEventBus<RegisterCustomShadersEvent>() - - fun register(name: String, vertexFormat: VertexFormat, saver: Consumer<ShaderProgram>) { - require(name.startsWith("firmament_")) - try { - list.add(Pair.of(ShaderProgram(resourceFactory, name, vertexFormat), saver)) - } catch (ex: Exception) { - Firmament.logger.fatal("Could not load firmament shader $name", ex) - } - } -} diff --git a/src/main/kotlin/events/SlotRenderEvents.kt b/src/main/kotlin/events/SlotRenderEvents.kt index 5431573..5234176 100644 --- a/src/main/kotlin/events/SlotRenderEvents.kt +++ b/src/main/kotlin/events/SlotRenderEvents.kt @@ -3,20 +3,19 @@ package moe.nea.firmament.events import net.minecraft.client.gui.DrawContext +import net.minecraft.client.render.RenderLayer import net.minecraft.client.texture.Sprite import net.minecraft.screen.slot.Slot import net.minecraft.util.Identifier import moe.nea.firmament.util.MC +import moe.nea.firmament.util.render.drawGuiTexture interface SlotRenderEvents { val context: DrawContext val slot: Slot - val mouseX: Int - val mouseY: Int - val delta: Float - fun highlight(sprite: Sprite) { - context.drawSprite( + fun highlight(sprite: Identifier) { + context.drawGuiTexture( slot.x, slot.y, 0, 16, 16, sprite ) @@ -24,9 +23,6 @@ interface SlotRenderEvents { data class Before( override val context: DrawContext, override val slot: Slot, - override val mouseX: Int, - override val mouseY: Int, - override val delta: Float ) : FirmamentEvent(), SlotRenderEvents { companion object : FirmamentEventBus<Before>() @@ -34,9 +30,6 @@ interface SlotRenderEvents { data class After( override val context: DrawContext, override val slot: Slot, - override val mouseX: Int, - override val mouseY: Int, - override val delta: Float ) : FirmamentEvent(), SlotRenderEvents { companion object : FirmamentEventBus<After>() diff --git a/src/main/kotlin/events/WorldReadyEvent.kt b/src/main/kotlin/events/WorldReadyEvent.kt index 2c76c44..c79b100 100644 --- a/src/main/kotlin/events/WorldReadyEvent.kt +++ b/src/main/kotlin/events/WorldReadyEvent.kt @@ -1,7 +1,10 @@ - - package moe.nea.firmament.events class WorldReadyEvent : FirmamentEvent() { - companion object : FirmamentEventBus<WorldReadyEvent>() + companion object : FirmamentEventBus<WorldReadyEvent>() +// class FullyLoaded : FirmamentEvent() { +// companion object : FirmamentEventBus<FullyLoaded>() { +// TODO: check WorldLoadingState +// } +// } } diff --git a/src/main/kotlin/events/WorldRenderLastEvent.kt b/src/main/kotlin/events/WorldRenderLastEvent.kt index 21a670d..3c2103d 100644 --- a/src/main/kotlin/events/WorldRenderLastEvent.kt +++ b/src/main/kotlin/events/WorldRenderLastEvent.kt @@ -17,10 +17,7 @@ import net.minecraft.util.math.Vec3d data class WorldRenderLastEvent( val matrices: MatrixStack, val tickCounter: RenderTickCounter, - val renderBlockOutline: Boolean, val camera: Camera, - val gameRenderer: GameRenderer, - val lightmapTextureManager: LightmapTextureManager, val vertexConsumers: VertexConsumerProvider.Immediate, ) : FirmamentEvent() { companion object : FirmamentEventBus<WorldRenderLastEvent>() diff --git a/src/main/kotlin/features/chat/ChatLinks.kt b/src/main/kotlin/features/chat/ChatLinks.kt index 0681f57..5bce3f4 100644 --- a/src/main/kotlin/features/chat/ChatLinks.kt +++ b/src/main/kotlin/features/chat/ChatLinks.kt @@ -13,8 +13,10 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.async import kotlin.math.min import net.minecraft.client.gui.screen.ChatScreen +import net.minecraft.client.render.RenderLayer import net.minecraft.client.texture.NativeImage import net.minecraft.client.texture.NativeImageBackedTexture +import net.minecraft.scoreboard.ScoreboardCriterion.RenderType import net.minecraft.text.ClickEvent import net.minecraft.text.HoverEvent import net.minecraft.text.Style @@ -28,6 +30,7 @@ import moe.nea.firmament.events.ScreenRenderPostEvent import moe.nea.firmament.features.FirmamentFeature import moe.nea.firmament.gui.config.ManagedConfig import moe.nea.firmament.util.MC +import moe.nea.firmament.util.render.drawTexture import moe.nea.firmament.util.transformEachRecursively import moe.nea.firmament.util.unformattedString diff --git a/src/main/kotlin/features/debug/DeveloperFeatures.kt b/src/main/kotlin/features/debug/DeveloperFeatures.kt index 2001a3f..d4b118b 100644 --- a/src/main/kotlin/features/debug/DeveloperFeatures.kt +++ b/src/main/kotlin/features/debug/DeveloperFeatures.kt @@ -37,10 +37,10 @@ object DeveloperFeatures : FirmamentFeature { builder.directory(gradleDir.toFile()) builder.inheritIO() val process = builder.start() - MC.player?.sendMessage(Text.translatable("firmament.dev.resourcerebuild.start")) + MC.sendChat(Text.translatable("firmament.dev.resourcerebuild.start")) val startTime = TimeMark.now() process.toHandle().onExit().thenApply { - MC.player?.sendMessage(Text.stringifiedTranslatable( + MC.sendChat(Text.stringifiedTranslatable( "firmament.dev.resourcerebuild.done", startTime.passedTime())) Unit diff --git a/src/main/kotlin/features/debug/PowerUserTools.kt b/src/main/kotlin/features/debug/PowerUserTools.kt index 83e3aff..13320dc 100644 --- a/src/main/kotlin/features/debug/PowerUserTools.kt +++ b/src/main/kotlin/features/debug/PowerUserTools.kt @@ -9,6 +9,7 @@ import net.minecraft.entity.Entity import net.minecraft.entity.LivingEntity import net.minecraft.item.ItemStack import net.minecraft.item.Items +import net.minecraft.nbt.NbtOps import net.minecraft.text.Text import net.minecraft.text.TextCodecs import net.minecraft.util.hit.BlockHitResult @@ -54,15 +55,13 @@ object PowerUserTools : FirmamentFeature { var lastCopiedStack: Pair<ItemStack, Text>? = null set(value) { field = value - if (value != null) - lastCopiedStackViewTime = true + if (value != null) lastCopiedStackViewTime = true } var lastCopiedStackViewTime = false @Subscribe fun resetLastCopiedStack(event: TickEvent) { - if (!lastCopiedStackViewTime) - lastCopiedStack = null + if (!lastCopiedStackViewTime) lastCopiedStack = null lastCopiedStackViewTime = false } @@ -154,13 +153,13 @@ object PowerUserTools : FirmamentFeature { } ClipboardUtils.setTextContent(skullTexture.toString()) lastCopiedStack = - Pair( - item, - Text.stringifiedTranslatable("firmament.tooltip.copied.skull-id", skullTexture.toString()) - ) + Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.skull-id", skullTexture.toString())) println("Copied skull id: $skullTexture") } else if (it.matches(TConfig.copyItemStack)) { - ClipboardUtils.setTextContent(item.encode(MC.currentOrDefaultRegistries).toPrettyString()) + ClipboardUtils.setTextContent( + ItemStack.CODEC + .encodeStart(MC.currentOrDefaultRegistries.getOps(NbtOps.INSTANCE), item) + .orThrow.toPrettyString()) lastCopiedStack = Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.stack")) } } diff --git a/src/main/kotlin/features/diana/AncestralSpadeSolver.kt b/src/main/kotlin/features/diana/AncestralSpadeSolver.kt index ef3ead1..8918e66 100644 --- a/src/main/kotlin/features/diana/AncestralSpadeSolver.kt +++ b/src/main/kotlin/features/diana/AncestralSpadeSolver.kt @@ -104,12 +104,13 @@ object AncestralSpadeSolver : SubscriptionOwner { if (!isEnabled()) return RenderInWorldContext.renderInWorld(event) { nextGuess?.let { - color(1f, 1f, 0f, 0.5f) - tinyBlock(it, 1f) + tinyBlock(it, 1f, 0x80FFFFFF.toInt()) + // TODO: replace this color(1f, 1f, 0f, 1f) tracer(it, lineWidth = 3f) } if (particlePositions.size > 2 && lastDing.passedTime() < 10.seconds && nextGuess != null) { + // TODO: replace this // TODO: add toggle color(0f, 1f, 0f, 0.7f) line(particlePositions) } diff --git a/src/main/kotlin/features/diana/NearbyBurrowsSolver.kt b/src/main/kotlin/features/diana/NearbyBurrowsSolver.kt index ab1518a..2fb4002 100644 --- a/src/main/kotlin/features/diana/NearbyBurrowsSolver.kt +++ b/src/main/kotlin/features/diana/NearbyBurrowsSolver.kt @@ -1,6 +1,6 @@ - package moe.nea.firmament.features.diana +import me.shedaniel.math.Color import kotlin.time.Duration.Companion.seconds import net.minecraft.particle.ParticleTypes import net.minecraft.util.math.BlockPos @@ -20,125 +20,125 @@ import moe.nea.firmament.util.render.RenderInWorldContext.Companion.renderInWorl object NearbyBurrowsSolver : SubscriptionOwner { - private val recentlyDugBurrows: MutableMap<BlockPos, TimeMark> = mutableMapWithMaxSize(20) - private val recentEnchantParticles: MutableMap<BlockPos, TimeMark> = mutableMapWithMaxSize(500) - private var lastBlockClick: BlockPos? = null - - enum class BurrowType { - START, MOB, TREASURE - } - - val burrows = mutableMapOf<BlockPos, BurrowType>() - - @Subscribe - fun onChatEvent(event: ProcessChatEvent) { - val lastClickedBurrow = lastBlockClick ?: return - if (event.unformattedString.startsWith("You dug out a Griffin Burrow!") || - event.unformattedString.startsWith(" ☠ You were killed by") || - event.unformattedString.startsWith("You finished the Griffin burrow chain!") - ) { - markAsDug(lastClickedBurrow) - burrows.remove(lastClickedBurrow) - } - } - - - fun wasRecentlyDug(blockPos: BlockPos): Boolean { - val lastDigTime = recentlyDugBurrows[blockPos] ?: TimeMark.farPast() - return lastDigTime.passedTime() < 10.seconds - } - - fun markAsDug(blockPos: BlockPos) { - recentlyDugBurrows[blockPos] = TimeMark.now() - } - - fun wasRecentlyEnchanted(blockPos: BlockPos): Boolean { - val lastEnchantTime = recentEnchantParticles[blockPos] ?: TimeMark.farPast() - return lastEnchantTime.passedTime() < 4.seconds - } - - fun markAsEnchanted(blockPos: BlockPos) { - recentEnchantParticles[blockPos] = TimeMark.now() - } - - @Subscribe - fun onParticles(event: ParticleSpawnEvent) { - if (!DianaWaypoints.TConfig.nearbyWaypoints) return - - val position: BlockPos = event.position.toBlockPos().down() - - if (wasRecentlyDug(position)) return - - val isEven50Spread = (event.offset.x == 0.5f && event.offset.z == 0.5f) - - if (event.particleEffect.type == ParticleTypes.ENCHANT) { - if (event.count == 5 && event.speed == 0.05F && event.offset.y == 0.4F && isEven50Spread) { - markAsEnchanted(position) - } - return - } - - if (!wasRecentlyEnchanted(position)) return - - if (event.particleEffect.type == ParticleTypes.ENCHANTED_HIT - && event.count == 4 - && event.speed == 0.01F - && event.offset.y == 0.1f - && isEven50Spread - ) { - burrows[position] = BurrowType.START - } - if (event.particleEffect.type == ParticleTypes.CRIT - && event.count == 3 - && event.speed == 0.01F - && event.offset.y == 0.1F - && isEven50Spread - ) { - burrows[position] = BurrowType.MOB - } - if (event.particleEffect.type == ParticleTypes.DRIPPING_LAVA - && event.count == 2 - && event.speed == 0.01F - && event.offset.y == 0.1F - && event.offset.x == 0.35F && event.offset.z == 0.35f - ) { - burrows[position] = BurrowType.TREASURE - } - } - - @Subscribe - fun onRender(event: WorldRenderLastEvent) { - if (!DianaWaypoints.TConfig.nearbyWaypoints) return - renderInWorld(event) { - for ((location, burrow) in burrows) { - when (burrow) { - BurrowType.START -> color(.2f, .8f, .2f, 0.4f) - BurrowType.MOB -> color(0.3f, 0.4f, 0.9f, 0.4f) - BurrowType.TREASURE -> color(1f, 0.7f, 0.2f, 0.4f) - } - block(location) - } - } - } - - @Subscribe - fun onSwapWorld(worldReadyEvent: WorldReadyEvent) { - burrows.clear() - recentEnchantParticles.clear() - recentlyDugBurrows.clear() - lastBlockClick = null - } - - fun onBlockClick(blockPos: BlockPos) { - if (!DianaWaypoints.TConfig.nearbyWaypoints) return - burrows.remove(blockPos) - lastBlockClick = blockPos - } - - override val delegateFeature: FirmamentFeature - get() = DianaWaypoints + private val recentlyDugBurrows: MutableMap<BlockPos, TimeMark> = mutableMapWithMaxSize(20) + private val recentEnchantParticles: MutableMap<BlockPos, TimeMark> = mutableMapWithMaxSize(500) + private var lastBlockClick: BlockPos? = null + + enum class BurrowType { + START, MOB, TREASURE + } + + val burrows = mutableMapOf<BlockPos, BurrowType>() + + @Subscribe + fun onChatEvent(event: ProcessChatEvent) { + val lastClickedBurrow = lastBlockClick ?: return + if (event.unformattedString.startsWith("You dug out a Griffin Burrow!") || + event.unformattedString.startsWith(" ☠ You were killed by") || + event.unformattedString.startsWith("You finished the Griffin burrow chain!") + ) { + markAsDug(lastClickedBurrow) + burrows.remove(lastClickedBurrow) + } + } + + + fun wasRecentlyDug(blockPos: BlockPos): Boolean { + val lastDigTime = recentlyDugBurrows[blockPos] ?: TimeMark.farPast() + return lastDigTime.passedTime() < 10.seconds + } + + fun markAsDug(blockPos: BlockPos) { + recentlyDugBurrows[blockPos] = TimeMark.now() + } + + fun wasRecentlyEnchanted(blockPos: BlockPos): Boolean { + val lastEnchantTime = recentEnchantParticles[blockPos] ?: TimeMark.farPast() + return lastEnchantTime.passedTime() < 4.seconds + } + + fun markAsEnchanted(blockPos: BlockPos) { + recentEnchantParticles[blockPos] = TimeMark.now() + } + + @Subscribe + fun onParticles(event: ParticleSpawnEvent) { + if (!DianaWaypoints.TConfig.nearbyWaypoints) return + + val position: BlockPos = event.position.toBlockPos().down() + + if (wasRecentlyDug(position)) return + + val isEven50Spread = (event.offset.x == 0.5f && event.offset.z == 0.5f) + + if (event.particleEffect.type == ParticleTypes.ENCHANT) { + if (event.count == 5 && event.speed == 0.05F && event.offset.y == 0.4F && isEven50Spread) { + markAsEnchanted(position) + } + return + } + + if (!wasRecentlyEnchanted(position)) return + + if (event.particleEffect.type == ParticleTypes.ENCHANTED_HIT + && event.count == 4 + && event.speed == 0.01F + && event.offset.y == 0.1f + && isEven50Spread + ) { + burrows[position] = BurrowType.START + } + if (event.particleEffect.type == ParticleTypes.CRIT + && event.count == 3 + && event.speed == 0.01F + && event.offset.y == 0.1F + && isEven50Spread + ) { + burrows[position] = BurrowType.MOB + } + if (event.particleEffect.type == ParticleTypes.DRIPPING_LAVA + && event.count == 2 + && event.speed == 0.01F + && event.offset.y == 0.1F + && event.offset.x == 0.35F && event.offset.z == 0.35f + ) { + burrows[position] = BurrowType.TREASURE + } + } + + @Subscribe + fun onRender(event: WorldRenderLastEvent) { + if (!DianaWaypoints.TConfig.nearbyWaypoints) return + renderInWorld(event) { + for ((location, burrow) in burrows) { + val color = when (burrow) { + BurrowType.START -> Color.ofRGBA(.2f, .8f, .2f, 0.4f) + BurrowType.MOB -> Color.ofRGBA(0.3f, 0.4f, 0.9f, 0.4f) + BurrowType.TREASURE -> Color.ofRGBA(1f, 0.7f, 0.2f, 0.4f) + } + block(location, color.color) + } + } + } + + @Subscribe + fun onSwapWorld(worldReadyEvent: WorldReadyEvent) { + burrows.clear() + recentEnchantParticles.clear() + recentlyDugBurrows.clear() + lastBlockClick = null + } + + fun onBlockClick(blockPos: BlockPos) { + if (!DianaWaypoints.TConfig.nearbyWaypoints) return + burrows.remove(blockPos) + lastBlockClick = blockPos + } + + override val delegateFeature: FirmamentFeature + get() = DianaWaypoints } fun Position.toBlockPos(): BlockPos { - return BlockPos(MathHelper.floor(x), MathHelper.floor(y), MathHelper.floor(z)) + return BlockPos(MathHelper.floor(x), MathHelper.floor(y), MathHelper.floor(z)) } diff --git a/src/main/kotlin/features/inventory/CraftingOverlay.kt b/src/main/kotlin/features/inventory/CraftingOverlay.kt index a958e25..d2c79fd 100644 --- a/src/main/kotlin/features/inventory/CraftingOverlay.kt +++ b/src/main/kotlin/features/inventory/CraftingOverlay.kt @@ -69,7 +69,7 @@ object CraftingOverlay : FirmamentFeature { if (!slot.hasStack()) { val itemStack = SBItemStack(expectedItem)?.asImmutableItemStack() ?: return event.context.drawItem(itemStack, event.slot.x, event.slot.y) - event.context.drawItemInSlot( + event.context.drawStackOverlay( MC.font, itemStack, event.slot.x, diff --git a/src/main/kotlin/features/inventory/ItemRarityCosmetics.kt b/src/main/kotlin/features/inventory/ItemRarityCosmetics.kt index 77f5071..26712da 100644 --- a/src/main/kotlin/features/inventory/ItemRarityCosmetics.kt +++ b/src/main/kotlin/features/inventory/ItemRarityCosmetics.kt @@ -4,6 +4,7 @@ package moe.nea.firmament.features.inventory import java.awt.Color import net.minecraft.client.gui.DrawContext +import net.minecraft.client.render.RenderLayer import net.minecraft.item.ItemStack import net.minecraft.util.Formatting import net.minecraft.util.Identifier @@ -16,6 +17,7 @@ import moe.nea.firmament.util.MC import moe.nea.firmament.util.mc.loreAccordingToNbt import moe.nea.firmament.util.collections.lastNotNullOfOrNull import moe.nea.firmament.util.collections.memoizeIdentity +import moe.nea.firmament.util.render.drawGuiTexture import moe.nea.firmament.util.unformattedString object ItemRarityCosmetics : FirmamentFeature { @@ -43,10 +45,10 @@ object ItemRarityCosmetics : FirmamentFeature { "SUPREME" to Formatting.DARK_RED, ).mapValues { val c = Color(it.value.colorValue!!) - Triple(c.red / 255F, c.green / 255F, c.blue / 255F) + c.rgb } - private fun getSkyblockRarity0(itemStack: ItemStack): Triple<Float, Float, Float>? { + private fun getSkyblockRarity0(itemStack: ItemStack): Int? { return itemStack.loreAccordingToNbt.lastNotNullOfOrNull { val entry = it.unformattedString rarityToColor.entries.find { (k, v) -> k in entry }?.value @@ -57,13 +59,13 @@ object ItemRarityCosmetics : FirmamentFeature { fun drawItemStackRarity(drawContext: DrawContext, x: Int, y: Int, item: ItemStack) { - val (r, g, b) = getSkyblockRarity(item) ?: return - drawContext.drawSprite( + val rgb = getSkyblockRarity(item) ?: return + drawContext.drawGuiTexture( + RenderLayer::getGuiTextured, + Identifier.of("firmament:item_rarity_background"), x, y, - 0, 16, 16, - MC.guiAtlasManager.getSprite(Identifier.of("firmament:item_rarity_background")), - r, g, b, 1F + rgb ) } diff --git a/src/main/kotlin/features/inventory/PetFeatures.kt b/src/main/kotlin/features/inventory/PetFeatures.kt index 2c11e76..5ca10f7 100644 --- a/src/main/kotlin/features/inventory/PetFeatures.kt +++ b/src/main/kotlin/features/inventory/PetFeatures.kt @@ -7,6 +7,7 @@ import moe.nea.firmament.features.FirmamentFeature import moe.nea.firmament.gui.config.ManagedConfig import moe.nea.firmament.util.MC import moe.nea.firmament.util.petData +import moe.nea.firmament.util.render.drawGuiTexture import moe.nea.firmament.util.useMatch object PetFeatures : FirmamentFeature { @@ -28,9 +29,9 @@ object PetFeatures : FirmamentFeature { val stack = event.slot.stack if (stack.petData?.active == true) petMenuTitle.useMatch(MC.screenName ?: return) { - event.context.drawSprite( + event.context.drawGuiTexture( event.slot.x, event.slot.y, 0, 16, 16, - MC.guiAtlasManager.getSprite(Identifier.of("firmament:selected_pet_background")) + Identifier.of("firmament:selected_pet_background") ) } } diff --git a/src/main/kotlin/features/inventory/SlotLocking.kt b/src/main/kotlin/features/inventory/SlotLocking.kt index de54005..fc09476 100644 --- a/src/main/kotlin/features/inventory/SlotLocking.kt +++ b/src/main/kotlin/features/inventory/SlotLocking.kt @@ -35,6 +35,7 @@ import moe.nea.firmament.util.mc.ScreenUtil.getSlotByIndex import moe.nea.firmament.util.mc.SlotUtils.swapWithHotBar import moe.nea.firmament.util.mc.displayNameAccordingToNbt import moe.nea.firmament.util.mc.loreAccordingToNbt +import moe.nea.firmament.util.render.GuiRenderLayers import moe.nea.firmament.util.render.drawLine import moe.nea.firmament.util.skyblockUUID import moe.nea.firmament.util.unformattedString @@ -211,6 +212,11 @@ object SlotLocking : FirmamentFeature { } if (it.matches(TConfig.slotBind)) { storedLockingSlot = null + val boundSlots = DConfig.data?.boundSlots ?: return + if (slot != null) + boundSlots.entries.removeIf { + it.value == slot.index || it.key == slot.index + } } } @@ -331,24 +337,22 @@ object SlotLocking : FirmamentFeature { val isSlotLocked = it.slot.inventory is PlayerInventory && it.slot.index in (lockedSlots ?: setOf()) val isUUIDLocked = (it.slot.stack?.skyblockUUID) in (lockedUUIDs ?: setOf()) if (isSlotLocked || isUUIDLocked) { - RenderSystem.disableDepthTest() - it.context.drawSprite( - it.slot.x, it.slot.y, 0, + it.context.drawGuiTexture( + GuiRenderLayers.GUI_TEXTURED_NO_DEPTH, + when { + isSlotLocked -> + (Identifier.of("firmament:slot_locked")) + + isUUIDLocked -> + (Identifier.of("firmament:uuid_locked")) + + else -> + error("unreachable") + }, + it.slot.x, it.slot.y, 16, 16, - MC.guiAtlasManager.getSprite( - when { - isSlotLocked -> - (Identifier.of("firmament:slot_locked")) - - isUUIDLocked -> - (Identifier.of("firmament:uuid_locked")) - - else -> - error("unreachable") - } - ) + -1 ) - RenderSystem.enableDepthTest() } } } diff --git a/src/main/kotlin/features/inventory/buttons/InventoryButton.kt b/src/main/kotlin/features/inventory/buttons/InventoryButton.kt index be173bd..a46bd76 100644 --- a/src/main/kotlin/features/inventory/buttons/InventoryButton.kt +++ b/src/main/kotlin/features/inventory/buttons/InventoryButton.kt @@ -18,6 +18,7 @@ import moe.nea.firmament.repo.RepoManager import moe.nea.firmament.util.MC import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.collections.memoize +import moe.nea.firmament.util.render.drawGuiTexture @Serializable data class InventoryButton( @@ -54,13 +55,13 @@ data class InventoryButton( } fun render(context: DrawContext) { - context.drawSprite( + context.drawGuiTexture( 0, 0, 0, dimensions.width, dimensions.height, - MC.guiAtlasManager.getSprite(Identifier.of("firmament:inventory_button_background")) + Identifier.of("firmament:inventory_button_background") ) context.drawItem(getItem(), 1, 1) } diff --git a/src/main/kotlin/features/inventory/buttons/InventoryButtonEditor.kt b/src/main/kotlin/features/inventory/buttons/InventoryButtonEditor.kt index c57563e..7bf9c73 100644 --- a/src/main/kotlin/features/inventory/buttons/InventoryButtonEditor.kt +++ b/src/main/kotlin/features/inventory/buttons/InventoryButtonEditor.kt @@ -84,7 +84,6 @@ class InventoryButtonEditor( context.matrices.push() context.matrices.translate(0f, 0f, -10f) context.fill(lastGuiRect.minX, lastGuiRect.minY, lastGuiRect.maxX, lastGuiRect.maxY, -1) - context.setShaderColor(1f, 1f, 1f, 1f) context.matrices.pop() for (button in buttons) { val buttonPosition = button.getBounds(lastGuiRect) diff --git a/src/main/kotlin/features/inventory/storageoverlay/StorageBackingHandle.kt b/src/main/kotlin/features/inventory/storageoverlay/StorageBackingHandle.kt index 5c1ac34..8fad4df 100644 --- a/src/main/kotlin/features/inventory/storageoverlay/StorageBackingHandle.kt +++ b/src/main/kotlin/features/inventory/storageoverlay/StorageBackingHandle.kt @@ -40,6 +40,7 @@ sealed interface StorageBackingHandle { * representable as a [StorageBackingHandle], meaning another screen is open, for example the enderchest icon * selection screen. */ + @OptIn(ExperimentalContracts::class) fun fromScreen(screen: Screen?): StorageBackingHandle? { contract { returnsNotNull() implies (screen != null) diff --git a/src/main/kotlin/features/inventory/storageoverlay/StorageOverlayScreen.kt b/src/main/kotlin/features/inventory/storageoverlay/StorageOverlayScreen.kt index f81315d..a5b2fd6 100644 --- a/src/main/kotlin/features/inventory/storageoverlay/StorageOverlayScreen.kt +++ b/src/main/kotlin/features/inventory/storageoverlay/StorageOverlayScreen.kt @@ -21,6 +21,7 @@ import moe.nea.firmament.util.MoulConfigUtils.clickMCComponentInPlace import moe.nea.firmament.util.MoulConfigUtils.drawMCComponentInPlace import moe.nea.firmament.util.assertTrueOr import moe.nea.firmament.util.customgui.customGui +import moe.nea.firmament.util.render.drawGuiTexture class StorageOverlayScreen : Screen(Text.literal("")) { @@ -162,13 +163,11 @@ class StorageOverlayScreen : Screen(Text.literal("")) { context.drawGuiTexture(upperBackgroundSprite, measurements.x, measurements.y, - 0, measurements.overviewWidth, measurements.overviewHeight) context.drawGuiTexture(playerInventorySprite, measurements.playerX, measurements.playerY, - 0, PLAYER_WIDTH, PLAYER_HEIGHT) } @@ -188,7 +187,7 @@ class StorageOverlayScreen : Screen(Text.literal("")) { items.withIndex().forEach { (index, item) -> val (x, y) = getPlayerInventorySlotPosition(index) context.drawItem(item, x, y, 0) - context.drawItemInSlot(textRenderer, item, x, y) + context.drawStackOverlay(textRenderer, item, x, y) } } @@ -357,7 +356,7 @@ class StorageOverlayScreen : Screen(Text.literal("")) { val slotY = (index / 9) * SLOT_SIZE + y + 4 + textRenderer.fontHeight + 1 if (slots == null) { context.drawItem(stack, slotX, slotY) - context.drawItemInSlot(textRenderer, stack, slotX, slotY) + context.drawStackOverlay(textRenderer, stack, slotX, slotY) } else { val slot = slots[index] slot.x = slotX - slotOffset.x diff --git a/src/main/kotlin/features/inventory/storageoverlay/StorageOverviewScreen.kt b/src/main/kotlin/features/inventory/storageoverlay/StorageOverviewScreen.kt index 2cbd54e..9112fab 100644 --- a/src/main/kotlin/features/inventory/storageoverlay/StorageOverviewScreen.kt +++ b/src/main/kotlin/features/inventory/storageoverlay/StorageOverviewScreen.kt @@ -111,7 +111,7 @@ class StorageOverviewScreen() : Screen(Text.empty()) { context.fill(x, y, x + 18, y + 18, 0x40808080.toInt()) } context.drawItem(stack, x + 1, y + 1) - context.drawItemInSlot(MC.font, stack, x + 1, y + 1) + context.drawStackOverlay(MC.font, stack, x + 1, y + 1) } } diff --git a/src/main/kotlin/features/mining/CommissionFeatures.kt b/src/main/kotlin/features/mining/CommissionFeatures.kt index d0acdfd..faba253 100644 --- a/src/main/kotlin/features/mining/CommissionFeatures.kt +++ b/src/main/kotlin/features/mining/CommissionFeatures.kt @@ -1,6 +1,6 @@ package moe.nea.firmament.features.mining -import net.minecraft.util.Identifier +import moe.nea.firmament.Firmament import moe.nea.firmament.annotations.Subscribe import moe.nea.firmament.events.SlotRenderEvents import moe.nea.firmament.gui.config.ManagedConfig @@ -19,10 +19,8 @@ object CommissionFeatures { if (!Config.highlightCompletedCommissions) return if (MC.screenName != "Commissions") return val stack = event.slot.stack - if(stack.loreAccordingToNbt.any { it.unformattedString == "COMPLETED" }) { - event.highlight( - MC.guiAtlasManager.getSprite(Identifier.of("firmament:completed_commission_background")) - ) + if (stack.loreAccordingToNbt.any { it.unformattedString == "COMPLETED" }) { + event.highlight(Firmament.identifier("completed_commission_background")) } } } diff --git a/src/main/kotlin/features/mining/HotmPresets.kt b/src/main/kotlin/features/mining/HotmPresets.kt index 3f83f3d..533aa1e 100644 --- a/src/main/kotlin/features/mining/HotmPresets.kt +++ b/src/main/kotlin/features/mining/HotmPresets.kt @@ -9,9 +9,7 @@ import net.minecraft.client.gui.screen.ingame.HandledScreen import net.minecraft.entity.player.PlayerInventory import net.minecraft.item.Items import net.minecraft.screen.GenericContainerScreenHandler -import net.minecraft.screen.ScreenHandler import net.minecraft.screen.slot.Slot -import net.minecraft.screen.slot.SlotActionType import net.minecraft.text.Text import moe.nea.firmament.Firmament import moe.nea.firmament.annotations.Subscribe @@ -31,6 +29,7 @@ import moe.nea.firmament.util.customgui.customGui import moe.nea.firmament.util.mc.CommonTextures import moe.nea.firmament.util.mc.SlotUtils.clickRightMouseButton import moe.nea.firmament.util.mc.displayNameAccordingToNbt +import moe.nea.firmament.util.render.drawGuiTexture import moe.nea.firmament.util.unformattedString import moe.nea.firmament.util.useMatch @@ -81,7 +80,7 @@ object HotmPresets { override fun render(drawContext: DrawContext, delta: Float, mouseX: Int, mouseY: Int) { drawContext.drawGuiTexture( CommonTextures.genericWidget(), - bounds.x, bounds.y, 0, + bounds.x, bounds.y, bounds.width, bounds.height, ) @@ -191,7 +190,7 @@ object HotmPresets { if (hotmInventoryName == MC.screenName && event.slot.stack.displayNameAccordingToNbt.unformattedString in highlightedPerks ) { - event.highlight(MC.guiAtlasManager.getSprite(Firmament.identifier("hotm_perk_preset"))) + event.highlight((Firmament.identifier("hotm_perk_preset"))) } } diff --git a/src/main/kotlin/features/texturepack/BakedModelExtra.kt b/src/main/kotlin/features/texturepack/BakedModelExtra.kt index 32f419a..6305748 100644 --- a/src/main/kotlin/features/texturepack/BakedModelExtra.kt +++ b/src/main/kotlin/features/texturepack/BakedModelExtra.kt @@ -1,11 +1,30 @@ - package moe.nea.firmament.features.texturepack +import net.fabricmc.fabric.api.renderer.v1.model.WrapperBakedModel as WrapperBakedModelFabric import net.minecraft.client.render.model.BakedModel +import net.minecraft.client.render.model.WrapperBakedModel +import moe.nea.firmament.util.ErrorUtil interface BakedModelExtra { + companion object { + @JvmStatic + fun cast(originalModel: BakedModel): BakedModelExtra? { + var p = originalModel + for (i in 0..256) { + p = when (p) { + is BakedModelExtra -> return p + is WrapperBakedModel -> p.wrapped + is WrapperBakedModelFabric -> WrapperBakedModelFabric.unwrap(p) + else -> break + } + } + ErrorUtil.softError("Could not find a baked model for $originalModel") + return null + } + } + var tintOverrides_firmament: TintOverrides? fun getHeadModel_firmament(): BakedModel? - fun setHeadModel_firmament(headModel: BakedModel?) + fun setHeadModel_firmament(headModel: BakedModel?) } diff --git a/src/main/kotlin/features/texturepack/CustomBlockTextures.kt b/src/main/kotlin/features/texturepack/CustomBlockTextures.kt index a149928..2f7f084 100644 --- a/src/main/kotlin/features/texturepack/CustomBlockTextures.kt +++ b/src/main/kotlin/features/texturepack/CustomBlockTextures.kt @@ -244,7 +244,7 @@ object CustomBlockTextures { .flatMap { it.lookup.values } .flatten() .mapTo(mutableSetOf()) { it.replacement.blockModelIdentifier } - .forEach { event.addNonItemModel(it) } + .forEach { event.addNonItemModel(it, it.id) } } private fun prepare(manager: ResourceManager): BakedReplacements { @@ -263,7 +263,7 @@ object CustomBlockTextures { val island = SkyBlockIsland.forMode(mode) val islandMpa = map.getOrPut(island, ::mutableMapOf) for ((blockId, replacement) in json.replacements) { - val block = MC.defaultRegistries.getWrapperOrThrow(RegistryKeys.BLOCK) + val block = MC.defaultRegistries.getOrThrow(RegistryKeys.BLOCK) .getOptional(RegistryKey.of(RegistryKeys.BLOCK, blockId)) .getOrNull() if (block == null) { diff --git a/src/main/kotlin/features/texturepack/CustomGlobalArmorOverrides.kt b/src/main/kotlin/features/texturepack/CustomGlobalArmorOverrides.kt index 7b6e62b..54e9e11 100644 --- a/src/main/kotlin/features/texturepack/CustomGlobalArmorOverrides.kt +++ b/src/main/kotlin/features/texturepack/CustomGlobalArmorOverrides.kt @@ -3,13 +3,13 @@ package moe.nea.firmament.features.texturepack import java.util.Optional +import java.util.concurrent.atomic.AtomicInteger import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.Transient import kotlinx.serialization.UseSerializers -import kotlin.jvm.optionals.getOrNull -import net.minecraft.item.ArmorMaterial import net.minecraft.item.ItemStack +import net.minecraft.item.equipment.EquipmentModel import net.minecraft.resource.ResourceManager import net.minecraft.resource.SinglePreparationResourceReloader import net.minecraft.util.Identifier @@ -17,90 +17,139 @@ import net.minecraft.util.profiler.Profiler import moe.nea.firmament.Firmament import moe.nea.firmament.annotations.Subscribe import moe.nea.firmament.events.FinalizeResourceManagerEvent -import moe.nea.firmament.events.subscription.SubscriptionOwner -import moe.nea.firmament.features.FirmamentFeature import moe.nea.firmament.features.texturepack.CustomGlobalTextures.logger import moe.nea.firmament.util.IdentifierSerializer import moe.nea.firmament.util.collections.WeakCache import moe.nea.firmament.util.skyBlockId -object CustomGlobalArmorOverrides : SubscriptionOwner { - @Serializable - data class ArmorOverride( - @SerialName("item_ids") - val itemIds: List<String>, - val layers: List<ArmorOverrideLayer>, - val overrides: List<ArmorOverrideOverride> = listOf(), - ) { - @Transient - val bakedLayers = bakeLayers(layers) - } - - fun bakeLayers(layers: List<ArmorOverrideLayer>): List<ArmorMaterial.Layer> { - return layers.map { ArmorMaterial.Layer(it.identifier, it.suffix, it.tint) } - } - - @Serializable - data class ArmorOverrideLayer( - val tint: Boolean = false, - val identifier: Identifier, - val suffix: String = "", - ) - - @Serializable - data class ArmorOverrideOverride( - val predicate: FirmamentModelPredicate, - val layers: List<ArmorOverrideLayer>, - ) { - @Transient - val bakedLayers = bakeLayers(layers) - } - - override val delegateFeature: FirmamentFeature - get() = CustomSkyBlockTextures - - val overrideCache = WeakCache.memoize<ItemStack, Optional<List<ArmorMaterial.Layer>>>("ArmorOverrides") { stack -> - val id = stack.skyBlockId ?: return@memoize Optional.empty() - val override = overrides[id.neuItem] ?: return@memoize Optional.empty() - for (suboverride in override.overrides) { - if (suboverride.predicate.test(stack)) { - return@memoize Optional.of(suboverride.bakedLayers) - } - } - return@memoize Optional.of(override.bakedLayers) - } - - @JvmStatic - fun overrideArmor(stack: ItemStack): List<ArmorMaterial.Layer>? { - if (!CustomSkyBlockTextures.TConfig.enableArmorOverrides) return null - return overrideCache.invoke(stack).getOrNull() - } - - var overrides: Map<String, ArmorOverride> = mapOf() - - @Subscribe - fun onStart(event: FinalizeResourceManagerEvent) { - event.resourceManager.registerReloader(object : - SinglePreparationResourceReloader<Map<String, ArmorOverride>>() { - override fun prepare(manager: ResourceManager, profiler: Profiler): Map<String, ArmorOverride> { - val overrideFiles = manager.findResources("overrides/armor_models") { - it.namespace == "firmskyblock" && it.path.endsWith(".json") - } - val overrides = overrideFiles.mapNotNull { - Firmament.tryDecodeJsonFromStream<ArmorOverride>(it.value.inputStream).getOrElse { ex -> - logger.error("Failed to load armor texture override at ${it.key}", ex) - null - } - } - val associatedMap = overrides.flatMap { obj -> obj.itemIds.map { it to obj } } - .toMap() - return associatedMap - } - - override fun apply(prepared: Map<String, ArmorOverride>, manager: ResourceManager, profiler: Profiler) { - overrides = prepared - } - }) - } +object CustomGlobalArmorOverrides { + @Serializable + data class ArmorOverride( + @SerialName("item_ids") + val itemIds: List<String>, + val layers: List<ArmorOverrideLayer>? = null, + val model: Identifier? = null, + val overrides: List<ArmorOverrideOverride> = listOf(), + ) { + @Transient + lateinit var modelIdentifier: Identifier + fun bake() { + modelIdentifier = bakeModel(model, layers) + overrides.forEach { it.bake() } + } + + init { + require(layers != null || model != null) { "Either model or layers must be specified for armor override" } + require(layers == null || model == null) { "Can't specify both model and layers for armor override" } + } + } + + @Serializable + data class ArmorOverrideLayer( + val tint: Boolean = false, + val identifier: Identifier, + val suffix: String = "", + ) + + @Serializable + data class ArmorOverrideOverride( + val predicate: FirmamentModelPredicate, + val layers: List<ArmorOverrideLayer>? = null, + val model: Identifier? = null, + ) { + init { + require(layers != null || model != null) { "Either model or layers must be specified for armor override override" } + require(layers == null || model == null) { "Can't specify both model and layers for armor override override" } + } + + @Transient + lateinit var modelIdentifier: Identifier + fun bake() { + modelIdentifier = bakeModel(model, layers) + } + } + + + val overrideCache = WeakCache.memoize<ItemStack, Optional<Identifier>>("ArmorOverrides") { stack -> + val id = stack.skyBlockId ?: return@memoize Optional.empty() + val override = overrides[id.neuItem] ?: return@memoize Optional.empty() + for (suboverride in override.overrides) { + if (suboverride.predicate.test(stack)) { + return@memoize Optional.of(suboverride.modelIdentifier) + } + } + return@memoize Optional.of(override.modelIdentifier) + } + + var overrides: Map<String, ArmorOverride> = mapOf() + private var bakedOverrides: MutableMap<Identifier, EquipmentModel> = mutableMapOf() + private val sentinelFirmRunning = AtomicInteger() + + private fun bakeModel(model: Identifier?, layers: List<ArmorOverrideLayer>?): Identifier { + require(model == null || layers == null) + if (model != null) { + return model + } else if (layers != null) { + val idNumber = sentinelFirmRunning.incrementAndGet() + val identifier = Identifier.of("firmament:sentinel/$idNumber") + val equipmentLayers = layers.map { + EquipmentModel.Layer( + it.identifier, if (it.tint) { + Optional.of(EquipmentModel.Dyeable(Optional.empty())) + } else { + Optional.empty() + }, + false + ) + } + bakedOverrides[identifier] = EquipmentModel( + mapOf( + EquipmentModel.LayerType.HUMANOID to equipmentLayers, + EquipmentModel.LayerType.HUMANOID_LEGGINGS to equipmentLayers, + ) + ) + return identifier + } else { + error("Either model or layers must be non null") + } + } + + + @Subscribe + fun onStart(event: FinalizeResourceManagerEvent) { + event.resourceManager.registerReloader(object : + SinglePreparationResourceReloader<Map<String, ArmorOverride>>() { + override fun prepare(manager: ResourceManager, profiler: Profiler): Map<String, ArmorOverride> { + val overrideFiles = manager.findResources("overrides/armor_models") { + it.namespace == "firmskyblock" && it.path.endsWith(".json") + } + val overrides = overrideFiles.mapNotNull { + Firmament.tryDecodeJsonFromStream<ArmorOverride>(it.value.inputStream).getOrElse { ex -> + logger.error("Failed to load armor texture override at ${it.key}", ex) + null + } + } + val associatedMap = overrides.flatMap { obj -> obj.itemIds.map { it to obj } } + .toMap() + return associatedMap + } + + override fun apply(prepared: Map<String, ArmorOverride>, manager: ResourceManager, profiler: Profiler) { + bakedOverrides.clear() + prepared.forEach { it.value.bake() } + overrides = prepared + } + }) + } + + @JvmStatic + fun overrideArmor(itemStack: ItemStack): Optional<Identifier> { + return overrideCache.invoke(itemStack) + } + + @JvmStatic + fun overrideArmorLayer(id: Identifier): EquipmentModel? { + return bakedOverrides[id] + } } diff --git a/src/main/kotlin/features/texturepack/CustomGlobalTextures.kt b/src/main/kotlin/features/texturepack/CustomGlobalTextures.kt index 2771699..a1203df 100644 --- a/src/main/kotlin/features/texturepack/CustomGlobalTextures.kt +++ b/src/main/kotlin/features/texturepack/CustomGlobalTextures.kt @@ -146,7 +146,7 @@ object CustomGlobalTextures : SinglePreparationResourceReloader<CustomGlobalText it.overrides .asSequence() .filter { it.predicate.test(stack) } - .map { models.modelManager.getModel(ModelIdentifier(it.model, "inventory")) } + .map { models.getModel(it.model) } .firstOrNull() } .intoOptional() diff --git a/src/main/kotlin/features/texturepack/JsonUnbakedModelFirmExtra.kt b/src/main/kotlin/features/texturepack/JsonUnbakedModelFirmExtra.kt index 0d0f8f2..9f641b8 100644 --- a/src/main/kotlin/features/texturepack/JsonUnbakedModelFirmExtra.kt +++ b/src/main/kotlin/features/texturepack/JsonUnbakedModelFirmExtra.kt @@ -1,9 +1,11 @@ package moe.nea.firmament.features.texturepack +import net.minecraft.client.render.model.Baker import net.minecraft.util.Identifier interface JsonUnbakedModelFirmExtra { + fun storeExtraBaker_firmament(baker: Baker) fun setHeadModel_firmament(identifier: Identifier?) fun getHeadModel_firmament(): Identifier? diff --git a/src/main/kotlin/features/texturepack/TintOverrides.kt b/src/main/kotlin/features/texturepack/TintOverrides.kt index 8006db8..85fcae4 100644 --- a/src/main/kotlin/features/texturepack/TintOverrides.kt +++ b/src/main/kotlin/features/texturepack/TintOverrides.kt @@ -3,7 +3,6 @@ package moe.nea.firmament.features.texturepack import com.google.gson.JsonObject import com.google.gson.JsonPrimitive import moe.nea.firmament.util.ErrorUtil -import moe.nea.firmament.util.assertNotNullOr data class TintOverrides( val layerMap: Map<Int, TintOverride> = mapOf() @@ -14,21 +13,22 @@ data class TintOverrides( val EMPTY = TintOverrides() private val threadLocal = object : ThreadLocal<TintOverrides>() {} fun enter(overrides: TintOverrides?) { - ErrorUtil.softCheck("Double entered tintOverrides") { - threadLocal.get() == null - } + ErrorUtil.softCheck("Double entered tintOverrides", + threadLocal.get() == null) threadLocal.set(overrides ?: EMPTY) } fun exit(overrides: TintOverrides?) { - ErrorUtil.softCheck("Exited with non matching enter tintOverrides") { - threadLocal.get() == (overrides ?: EMPTY) - } + ErrorUtil.softCheck("Exited with non matching enter tintOverrides", + threadLocal.get() == (overrides ?: EMPTY)) threadLocal.remove() } - fun getCurrentOverrides() = - assertNotNullOr(threadLocal.get(), "Got current tintOverrides without entering") { EMPTY } + fun getCurrentOverrides(): TintOverrides { + return ErrorUtil.notNullOr(threadLocal.get(), "Got current tintOverrides without entering") { + EMPTY + } + } fun parse(jsonObject: JsonObject): TintOverrides { val map = mutableMapOf<Int, TintOverride>() diff --git a/src/main/kotlin/features/world/FairySouls.kt b/src/main/kotlin/features/world/FairySouls.kt index eec9b04..1263074 100644 --- a/src/main/kotlin/features/world/FairySouls.kt +++ b/src/main/kotlin/features/world/FairySouls.kt @@ -6,6 +6,7 @@ import io.github.moulberry.repo.data.Coordinate import kotlinx.serialization.Serializable import kotlinx.serialization.serializer import net.minecraft.text.Text +import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Vec3d import moe.nea.firmament.annotations.Subscribe import moe.nea.firmament.events.ProcessChatEvent @@ -98,9 +99,8 @@ object FairySouls : FirmamentFeature { fun onWorldRender(it: WorldRenderLastEvent) { if (!TConfig.displaySouls) return renderInWorld(it) { - color(1F, 1F, 0F, 0.8F) currentMissingSouls.forEach { - block(it.blockPos) + block(it.blockPos, 0x80FFFF00.toInt()) } color(1f, 0f, 1f, 1f) currentLocationSouls.forEach { diff --git a/src/main/kotlin/features/world/Waypoints.kt b/src/main/kotlin/features/world/Waypoints.kt index d535b4e..16db059 100644 --- a/src/main/kotlin/features/world/Waypoints.kt +++ b/src/main/kotlin/features/world/Waypoints.kt @@ -1,5 +1,3 @@ - - package moe.nea.firmament.features.world import com.mojang.brigadier.arguments.IntegerArgumentType @@ -12,6 +10,7 @@ import kotlin.collections.set import kotlin.time.Duration.Companion.hours import kotlin.time.Duration.Companion.seconds import net.minecraft.command.argument.BlockPosArgumentType +import net.minecraft.server.command.CommandOutput import net.minecraft.server.command.ServerCommandSource import net.minecraft.text.Text import net.minecraft.util.math.BlockPos @@ -35,263 +34,273 @@ import moe.nea.firmament.util.TimeMark import moe.nea.firmament.util.render.RenderInWorldContext object Waypoints : FirmamentFeature { - override val identifier: String - get() = "waypoints" + override val identifier: String + get() = "waypoints" - object TConfig : ManagedConfig(identifier, Category.MINING) { // TODO: add to misc - val tempWaypointDuration by duration("temp-waypoint-duration", 0.seconds, 1.hours) { 30.seconds } - val showIndex by toggle("show-index") { true } - val skipToNearest by toggle("skip-to-nearest") { false } - // TODO: look ahead size - } + object TConfig : ManagedConfig(identifier, Category.MINING) { // TODO: add to misc + val tempWaypointDuration by duration("temp-waypoint-duration", 0.seconds, 1.hours) { 30.seconds } + val showIndex by toggle("show-index") { true } + val skipToNearest by toggle("skip-to-nearest") { false } + // TODO: look ahead size + } - data class TemporaryWaypoint( - val pos: BlockPos, - val postedAt: TimeMark, - ) + data class TemporaryWaypoint( + val pos: BlockPos, + val postedAt: TimeMark, + ) - override val config get() = TConfig + override val config get() = TConfig - val temporaryPlayerWaypointList = mutableMapOf<String, TemporaryWaypoint>() - val temporaryPlayerWaypointMatcher = "(?i)x: (-?[0-9]+),? y: (-?[0-9]+),? z: (-?[0-9]+)".toPattern() + val temporaryPlayerWaypointList = mutableMapOf<String, TemporaryWaypoint>() + val temporaryPlayerWaypointMatcher = "(?i)x: (-?[0-9]+),? y: (-?[0-9]+),? z: (-?[0-9]+)".toPattern() - val waypoints = mutableListOf<BlockPos>() - var ordered = false - var orderedIndex = 0 + val waypoints = mutableListOf<BlockPos>() + var ordered = false + var orderedIndex = 0 - @Serializable - data class ColeWeightWaypoint( - val x: Int, - val y: Int, - val z: Int, - val r: Int = 0, - val g: Int = 0, - val b: Int = 0, - ) + @Serializable + data class ColeWeightWaypoint( + val x: Int, + val y: Int, + val z: Int, + val r: Int = 0, + val g: Int = 0, + val b: Int = 0, + ) - @Subscribe - fun onRenderOrderedWaypoints(event: WorldRenderLastEvent) { - if (waypoints.isEmpty()) return - RenderInWorldContext.renderInWorld(event) { - if (!ordered) { - waypoints.withIndex().forEach { - color(0f, 0.3f, 0.7f, 0.5f) - block(it.value) - color(1f, 1f, 1f, 1f) - if (TConfig.showIndex) - withFacingThePlayer(it.value.toCenterPos()) { - text(Text.literal(it.index.toString())) - } - } - } else { - orderedIndex %= waypoints.size - val firstColor = Color.ofRGBA(0, 200, 40, 180) - color(firstColor) - tracer(waypoints[orderedIndex].toCenterPos(), lineWidth = 3f) - waypoints.withIndex().toList() - .wrappingWindow(orderedIndex, 3) - .zip( - listOf( - firstColor, - Color.ofRGBA(180, 200, 40, 150), - Color.ofRGBA(180, 80, 20, 140), - ) - ) - .reversed() - .forEach { (waypoint, col) -> - val (index, pos) = waypoint - color(col) - block(pos) - color(1f, 1f, 1f, 1f) - if (TConfig.showIndex) - withFacingThePlayer(pos.toCenterPos()) { - text(Text.literal(index.toString())) - } - } - } - } - } + @Subscribe + fun onRenderOrderedWaypoints(event: WorldRenderLastEvent) { + if (waypoints.isEmpty()) return + RenderInWorldContext.renderInWorld(event) { + if (!ordered) { + waypoints.withIndex().forEach { + block(it.value, 0x800050A0.toInt()) + if (TConfig.showIndex) + withFacingThePlayer(it.value.toCenterPos()) { + text(Text.literal(it.index.toString())) + } + } + } else { + orderedIndex %= waypoints.size + val firstColor = Color.ofRGBA(0, 200, 40, 180) + color(firstColor) + tracer(waypoints[orderedIndex].toCenterPos(), lineWidth = 3f) + waypoints.withIndex().toList() + .wrappingWindow(orderedIndex, 3) + .zip( + listOf( + firstColor, + Color.ofRGBA(180, 200, 40, 150), + Color.ofRGBA(180, 80, 20, 140), + ) + ) + .reversed() + .forEach { (waypoint, col) -> + val (index, pos) = waypoint + block(pos, col.color) + if (TConfig.showIndex) + withFacingThePlayer(pos.toCenterPos()) { + text(Text.literal(index.toString())) + } + } + } + } + } - @Subscribe - fun onTick(event: TickEvent) { - if (waypoints.isEmpty() || !ordered) return - orderedIndex %= waypoints.size - val p = MC.player?.pos ?: return - if (TConfig.skipToNearest) { - orderedIndex = - (waypoints.withIndex().minBy { it.value.getSquaredDistance(p) }.index + 1) % waypoints.size - } else { - if (waypoints[orderedIndex].isWithinDistance(p, 3.0)) { - orderedIndex = (orderedIndex + 1) % waypoints.size - } - } - } + @Subscribe + fun onTick(event: TickEvent) { + if (waypoints.isEmpty() || !ordered) return + orderedIndex %= waypoints.size + val p = MC.player?.pos ?: return + if (TConfig.skipToNearest) { + orderedIndex = + (waypoints.withIndex().minBy { it.value.getSquaredDistance(p) }.index + 1) % waypoints.size + } else { + if (waypoints[orderedIndex].isWithinDistance(p, 3.0)) { + orderedIndex = (orderedIndex + 1) % waypoints.size + } + } + } - @Subscribe - fun onProcessChat(it: ProcessChatEvent) { - val matcher = temporaryPlayerWaypointMatcher.matcher(it.unformattedString) - if (it.nameHeuristic != null && TConfig.tempWaypointDuration > 0.seconds && matcher.find()) { - temporaryPlayerWaypointList[it.nameHeuristic] = TemporaryWaypoint( - BlockPos( - matcher.group(1).toInt(), - matcher.group(2).toInt(), - matcher.group(3).toInt(), - ), - TimeMark.now() - ) - } - } + @Subscribe + fun onProcessChat(it: ProcessChatEvent) { + val matcher = temporaryPlayerWaypointMatcher.matcher(it.unformattedString) + if (it.nameHeuristic != null && TConfig.tempWaypointDuration > 0.seconds && matcher.find()) { + temporaryPlayerWaypointList[it.nameHeuristic] = TemporaryWaypoint( + BlockPos( + matcher.group(1).toInt(), + matcher.group(2).toInt(), + matcher.group(3).toInt(), + ), + TimeMark.now() + ) + } + } - @Subscribe - fun onCommand(event: CommandEvent.SubCommand) { - event.subcommand("waypoint") { - thenArgument("pos", BlockPosArgumentType.blockPos()) { pos -> - thenExecute { - val position = pos.get(this).toAbsoluteBlockPos(source.asFakeServer()) - waypoints.add(position) - source.sendFeedback( - Text.stringifiedTranslatable( - "firmament.command.waypoint.added", - position.x, - position.y, - position.z - ) - ) - } - } - } - event.subcommand("waypoints") { - thenLiteral("clear") { - thenExecute { - waypoints.clear() - source.sendFeedback(Text.translatable("firmament.command.waypoint.clear")) - } - } - thenLiteral("toggleordered") { - thenExecute { - ordered = !ordered - if (ordered) { - val p = MC.player?.pos ?: Vec3d.ZERO - orderedIndex = - waypoints.withIndex().minByOrNull { it.value.getSquaredDistance(p) }?.index ?: 0 - } - source.sendFeedback(Text.translatable("firmament.command.waypoint.ordered.toggle.$ordered")) - } - } - thenLiteral("skip") { - thenExecute { - if (ordered && waypoints.isNotEmpty()) { - orderedIndex = (orderedIndex + 1) % waypoints.size - source.sendFeedback(Text.translatable("firmament.command.waypoint.skip")) - } else { - source.sendError(Text.translatable("firmament.command.waypoint.skip.error")) - } - } - } - thenLiteral("remove") { - thenArgument("index", IntegerArgumentType.integer(0)) { indexArg -> - thenExecute { - val index = get(indexArg) - if (index in waypoints.indices) { - waypoints.removeAt(index) - source.sendFeedback(Text.stringifiedTranslatable( - "firmament.command.waypoint.remove", - index)) - } else { - source.sendError(Text.stringifiedTranslatable("firmament.command.waypoint.remove.error")) - } - } - } - } - thenLiteral("import") { - thenExecute { - val contents = ClipboardUtils.getTextContents() - val data = try { - Firmament.json.decodeFromString<List<ColeWeightWaypoint>>(contents) - } catch (ex: Exception) { - Firmament.logger.error("Could not load waypoints from clipboard", ex) - source.sendError(Text.translatable("firmament.command.waypoint.import.error")) - return@thenExecute - } - waypoints.clear() - data.mapTo(waypoints) { BlockPos(it.x, it.y, it.z) } - source.sendFeedback( - Text.stringifiedTranslatable( - "firmament.command.waypoint.import", - data.size - ) - ) - } - } - } - } + @Subscribe + fun onCommand(event: CommandEvent.SubCommand) { + event.subcommand("waypoint") { + thenArgument("pos", BlockPosArgumentType.blockPos()) { pos -> + thenExecute { + val position = pos.get(this).toAbsoluteBlockPos(source.asFakeServer()) + waypoints.add(position) + source.sendFeedback( + Text.stringifiedTranslatable( + "firmament.command.waypoint.added", + position.x, + position.y, + position.z + ) + ) + } + } + } + event.subcommand("waypoints") { + thenLiteral("clear") { + thenExecute { + waypoints.clear() + source.sendFeedback(Text.translatable("firmament.command.waypoint.clear")) + } + } + thenLiteral("toggleordered") { + thenExecute { + ordered = !ordered + if (ordered) { + val p = MC.player?.pos ?: Vec3d.ZERO + orderedIndex = + waypoints.withIndex().minByOrNull { it.value.getSquaredDistance(p) }?.index ?: 0 + } + source.sendFeedback(Text.translatable("firmament.command.waypoint.ordered.toggle.$ordered")) + } + } + thenLiteral("skip") { + thenExecute { + if (ordered && waypoints.isNotEmpty()) { + orderedIndex = (orderedIndex + 1) % waypoints.size + source.sendFeedback(Text.translatable("firmament.command.waypoint.skip")) + } else { + source.sendError(Text.translatable("firmament.command.waypoint.skip.error")) + } + } + } + thenLiteral("remove") { + thenArgument("index", IntegerArgumentType.integer(0)) { indexArg -> + thenExecute { + val index = get(indexArg) + if (index in waypoints.indices) { + waypoints.removeAt(index) + source.sendFeedback(Text.stringifiedTranslatable( + "firmament.command.waypoint.remove", + index)) + } else { + source.sendError(Text.stringifiedTranslatable("firmament.command.waypoint.remove.error")) + } + } + } + } + thenLiteral("import") { + thenExecute { + val contents = ClipboardUtils.getTextContents() + val data = try { + Firmament.json.decodeFromString<List<ColeWeightWaypoint>>(contents) + } catch (ex: Exception) { + Firmament.logger.error("Could not load waypoints from clipboard", ex) + source.sendError(Text.translatable("firmament.command.waypoint.import.error")) + return@thenExecute + } + waypoints.clear() + data.mapTo(waypoints) { BlockPos(it.x, it.y, it.z) } + source.sendFeedback( + Text.stringifiedTranslatable( + "firmament.command.waypoint.import", + data.size + ) + ) + } + } + } + } - @Subscribe - fun onRenderTemporaryWaypoints(event: WorldRenderLastEvent) { - temporaryPlayerWaypointList.entries.removeIf { it.value.postedAt.passedTime() > TConfig.tempWaypointDuration } - if (temporaryPlayerWaypointList.isEmpty()) return - RenderInWorldContext.renderInWorld(event) { - color(1f, 1f, 0f, 1f) - temporaryPlayerWaypointList.forEach { (player, waypoint) -> - block(waypoint.pos) - } - color(1f, 1f, 1f, 1f) - temporaryPlayerWaypointList.forEach { (player, waypoint) -> - val skin = - MC.networkHandler?.listedPlayerListEntries?.find { it.profile.name == player } - ?.skinTextures - ?.texture - withFacingThePlayer(waypoint.pos.toCenterPos()) { - waypoint(waypoint.pos, Text.stringifiedTranslatable("firmament.waypoint.temporary", player)) - if (skin != null) { - matrixStack.translate(0F, -20F, 0F) - // Head front - texture( - skin, 16, 16, - 1 / 8f, 1 / 8f, - 2 / 8f, 2 / 8f, - ) - // Head overlay - texture( - skin, 16, 16, - 5 / 8f, 1 / 8f, - 6 / 8f, 2 / 8f, - ) - } - } - } - } - } + @Subscribe + fun onRenderTemporaryWaypoints(event: WorldRenderLastEvent) { + temporaryPlayerWaypointList.entries.removeIf { it.value.postedAt.passedTime() > TConfig.tempWaypointDuration } + if (temporaryPlayerWaypointList.isEmpty()) return + RenderInWorldContext.renderInWorld(event) { + temporaryPlayerWaypointList.forEach { (player, waypoint) -> + block(waypoint.pos, 0xFFFFFF00.toInt()) + } + temporaryPlayerWaypointList.forEach { (player, waypoint) -> + val skin = + MC.networkHandler?.listedPlayerListEntries?.find { it.profile.name == player } + ?.skinTextures + ?.texture + withFacingThePlayer(waypoint.pos.toCenterPos()) { + waypoint(waypoint.pos, Text.stringifiedTranslatable("firmament.waypoint.temporary", player)) + if (skin != null) { + matrixStack.translate(0F, -20F, 0F) + // Head front + texture( + skin, 16, 16, + 1 / 8f, 1 / 8f, + 2 / 8f, 2 / 8f, + ) + // Head overlay + texture( + skin, 16, 16, + 5 / 8f, 1 / 8f, + 6 / 8f, 2 / 8f, + ) + } + } + } + } + } - @Subscribe - fun onWorldReady(event: WorldReadyEvent) { - temporaryPlayerWaypointList.clear() - } + @Subscribe + fun onWorldReady(event: WorldReadyEvent) { + temporaryPlayerWaypointList.clear() + } } fun <E> List<E>.wrappingWindow(startIndex: Int, windowSize: Int): List<E> { - val result = ArrayList<E>(windowSize) - if (startIndex + windowSize < size) { - result.addAll(subList(startIndex, startIndex + windowSize)) - } else { - result.addAll(subList(startIndex, size)) - result.addAll(subList(0, minOf(windowSize - (size - startIndex), startIndex))) - } - return result + val result = ArrayList<E>(windowSize) + if (startIndex + windowSize < size) { + result.addAll(subList(startIndex, startIndex + windowSize)) + } else { + result.addAll(subList(startIndex, size)) + result.addAll(subList(0, minOf(windowSize - (size - startIndex), startIndex))) + } + return result } fun FabricClientCommandSource.asFakeServer(): ServerCommandSource { - val source = this - return ServerCommandSource( - source.player, - source.position, - source.rotation, - null, - 0, - "FakeServerCommandSource", - Text.literal("FakeServerCommandSource"), - null, - source.player - ) + val source = this + return ServerCommandSource( + object : CommandOutput { + override fun sendMessage(message: Text?) { + source.player.sendMessage(message, false) + } + + override fun shouldReceiveFeedback(): Boolean { + return true + } + + override fun shouldTrackOutput(): Boolean { + return true + } + + override fun shouldBroadcastConsoleToOps(): Boolean { + return true + } + }, + source.position, + source.rotation, + null, + 0, + "FakeServerCommandSource", + Text.literal("FakeServerCommandSource"), + null, + source.player + ) } diff --git a/src/main/kotlin/gui/BarComponent.kt b/src/main/kotlin/gui/BarComponent.kt index 8ef0753..b82c666 100644 --- a/src/main/kotlin/gui/BarComponent.kt +++ b/src/main/kotlin/gui/BarComponent.kt @@ -1,4 +1,3 @@ - package moe.nea.firmament.gui import com.mojang.blaze3d.systems.RenderSystem @@ -10,116 +9,115 @@ import io.github.notenoughupdates.moulconfig.observer.GetSetter import io.github.notenoughupdates.moulconfig.platform.ModernRenderContext import me.shedaniel.math.Color import net.minecraft.client.gui.DrawContext +import net.minecraft.client.render.RenderLayer import net.minecraft.util.Identifier import moe.nea.firmament.Firmament class BarComponent( - val progress: GetSetter<Double>, val total: GetSetter<Double>, - val fillColor: Color, - val emptyColor: Color, + val progress: GetSetter<Double>, val total: GetSetter<Double>, + val fillColor: Color, + val emptyColor: Color, ) : GuiComponent() { - override fun getWidth(): Int { - return 80 - } + override fun getWidth(): Int { + return 80 + } - override fun getHeight(): Int { - return 8 - } + override fun getHeight(): Int { + return 8 + } - data class Texture( - val identifier: Identifier, - val u1: Float, val v1: Float, - val u2: Float, val v2: Float, - ) { - fun draw(context: DrawContext, x: Int, y: Int, width: Int, height: Int, color: Color) { - context.drawTexturedQuad( - identifier, - x, y, x + width, x + height, 0, - u1, u2, v1, v2, - color.red / 255F, - color.green / 255F, - color.blue / 255F, - color.alpha / 255F, - ) - } - } + data class Texture( + val identifier: Identifier, + val u1: Float, val v1: Float, + val u2: Float, val v2: Float, + ) { + fun draw(context: DrawContext, x: Int, y: Int, width: Int, height: Int, color: Color) { + context.drawTexturedQuad( + RenderLayer::getGuiTextured, + identifier, + x, y, x + width, x + height, + u1, u2, v1, v2, + color.color + ) + } + } - companion object { - val resource = Firmament.identifier("textures/gui/bar.png") - val left = Texture(resource, 0 / 64F, 0 / 64F, 4 / 64F, 8 / 64F) - val middle = Texture(resource, 4 / 64F, 0 / 64F, 8 / 64F, 8 / 64F) - val right = Texture(resource, 8 / 64F, 0 / 64F, 12 / 64F, 8 / 64F) - val segmentOverlay = Texture(resource, 12 / 64F, 0 / 64F, 15 / 64F, 8 / 64F) - } + companion object { + val resource = Firmament.identifier("textures/gui/bar.png") + val left = Texture(resource, 0 / 64F, 0 / 64F, 4 / 64F, 8 / 64F) + val middle = Texture(resource, 4 / 64F, 0 / 64F, 8 / 64F, 8 / 64F) + val right = Texture(resource, 8 / 64F, 0 / 64F, 12 / 64F, 8 / 64F) + val segmentOverlay = Texture(resource, 12 / 64F, 0 / 64F, 15 / 64F, 8 / 64F) + } - private fun drawSection( - context: DrawContext, - texture: Texture, - x: Int, - y: Int, - width: Int, - sectionStart: Double, - sectionEnd: Double - ) { - if (sectionEnd < progress.get() && width == 4) { - texture.draw(context, x, y, 4, 8, fillColor) - return - } - if (sectionStart > progress.get() && width == 4) { - texture.draw(context, x, y, 4, 8, emptyColor) - return - } - val increasePerPixel = (sectionEnd - sectionStart) / width - var valueAtPixel = sectionStart - for (i in (0 until width)) { - val newTex = - Texture(texture.identifier, texture.u1 + i / 64F, texture.v1, texture.u1 + (i + 1) / 64F, texture.v2) - newTex.draw( - context, x + i, y, 1, 8, - if (valueAtPixel < progress.get()) fillColor else emptyColor - ) - valueAtPixel += increasePerPixel - } - } + private fun drawSection( + context: DrawContext, + texture: Texture, + x: Int, + y: Int, + width: Int, + sectionStart: Double, + sectionEnd: Double + ) { + if (sectionEnd < progress.get() && width == 4) { + texture.draw(context, x, y, 4, 8, fillColor) + return + } + if (sectionStart > progress.get() && width == 4) { + texture.draw(context, x, y, 4, 8, emptyColor) + return + } + val increasePerPixel = (sectionEnd - sectionStart) / width + var valueAtPixel = sectionStart + for (i in (0 until width)) { + val newTex = + Texture(texture.identifier, texture.u1 + i / 64F, texture.v1, texture.u1 + (i + 1) / 64F, texture.v2) + newTex.draw( + context, x + i, y, 1, 8, + if (valueAtPixel < progress.get()) fillColor else emptyColor + ) + valueAtPixel += increasePerPixel + } + } - override fun render(context: GuiImmediateContext) { - val renderContext = (context.renderContext as ModernRenderContext).drawContext - var i = 0 - val x = 0 - val y = 0 - while (i < context.width - 4) { - drawSection( - renderContext, - if (i == 0) left else middle, - x + i, y, - (context.width - (i + 4)).coerceAtMost(4), - i * total.get() / context.width, (i + 4) * total.get() / context.width - ) - i += 4 - } - drawSection( - renderContext, - right, - x + context.width - 4, - y, - 4, - (context.width - 4) * total.get() / context.width, - total.get() - ) - RenderSystem.setShaderColor(1F, 1F, 1F, 1F) + override fun render(context: GuiImmediateContext) { + val renderContext = (context.renderContext as ModernRenderContext).drawContext + var i = 0 + val x = 0 + val y = 0 + while (i < context.width - 4) { + drawSection( + renderContext, + if (i == 0) left else middle, + x + i, y, + (context.width - (i + 4)).coerceAtMost(4), + i * total.get() / context.width, (i + 4) * total.get() / context.width + ) + i += 4 + } + drawSection( + renderContext, + right, + x + context.width - 4, + y, + 4, + (context.width - 4) * total.get() / context.width, + total.get() + ) + RenderSystem.setShaderColor(1F, 1F, 1F, 1F) - } + } } fun Identifier.toMoulConfig(): MyResourceLocation { - return MyResourceLocation(this.namespace, this.path) + return MyResourceLocation(this.namespace, this.path) } fun RenderContext.color(color: Color) { - color(color.red, color.green, color.blue, color.alpha) + color(color.red, color.green, color.blue, color.alpha) } fun RenderContext.color(red: Int, green: Int, blue: Int, alpha: Int) { - color(red / 255f, green / 255f, blue / 255f, alpha / 255f) + color(red / 255f, green / 255f, blue / 255f, alpha / 255f) } diff --git a/src/main/kotlin/gui/entity/EntityRenderer.kt b/src/main/kotlin/gui/entity/EntityRenderer.kt index 8c7428d..ddb862f 100644 --- a/src/main/kotlin/gui/entity/EntityRenderer.kt +++ b/src/main/kotlin/gui/entity/EntityRenderer.kt @@ -1,4 +1,3 @@ - package moe.nea.firmament.gui.entity import com.google.gson.Gson @@ -13,7 +12,9 @@ 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.entity.SpawnReason import net.minecraft.util.Identifier +import net.minecraft.world.World import moe.nea.firmament.util.MC import moe.nea.firmament.util.assertNotNullOr import moe.nea.firmament.util.iterate @@ -21,177 +22,177 @@ 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 fakeWorld: World get() = MC.lastWorld!! + private fun <T : Entity> t(entityType: EntityType<T>): () -> T { + return { entityType.create(fakeWorld, SpawnReason.LOAD)!! } + } - 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 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 - } + 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) - } + 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 - ) - ) - } + 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, - minOf(2F / maxSize, 1F) * 30, - -bottomOffset, - mouseX, - mouseY, - currentEntity - ) - val next = currentEntity.firstPassenger as? LivingEntity ?: break - bottomOffset += currentEntity.getPassengerRidingPos(next).y.toFloat() * 0.75F - currentEntity = next - } - } + 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, + minOf(2F / maxSize, 1F) * 30, + -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: Float, - 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() - } + fun drawEntity( + context: DrawContext, + x1: Int, + y1: Int, + x2: Int, + y2: Int, + size: Float, + 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/gui/entity/FakeWorld.kt b/src/main/kotlin/gui/entity/FakeWorld.kt index f354d5a..7ec385c 100644 --- a/src/main/kotlin/gui/entity/FakeWorld.kt +++ b/src/main/kotlin/gui/entity/FakeWorld.kt @@ -1,38 +1,37 @@ - package moe.nea.firmament.gui.entity -import com.mojang.datafixers.util.Pair -import com.mojang.serialization.Lifecycle -import java.util.* +import java.util.UUID 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.gui.screen.world.SelectWorldScreen import net.minecraft.component.type.MapIdComponent import net.minecraft.entity.Entity +import net.minecraft.entity.damage.DamageSource import net.minecraft.entity.player.PlayerEntity import net.minecraft.fluid.Fluid +import net.minecraft.item.FuelRegistry import net.minecraft.item.map.MapState +import net.minecraft.particle.ParticleEffect import net.minecraft.recipe.BrewingRecipeRegistry -import net.minecraft.recipe.Ingredient import net.minecraft.recipe.RecipeManager -import net.minecraft.registry.BuiltinRegistries +import net.minecraft.recipe.RecipePropertySet +import net.minecraft.recipe.StonecuttingRecipe +import net.minecraft.recipe.display.CuttingRecipeDisplay import net.minecraft.registry.DynamicRegistryManager -import net.minecraft.registry.Registry +import net.minecraft.registry.Registries import net.minecraft.registry.RegistryKey import net.minecraft.registry.RegistryKeys -import net.minecraft.registry.RegistryWrapper +import net.minecraft.registry.ServerDynamicRegistryType import net.minecraft.registry.entry.RegistryEntry -import net.minecraft.registry.entry.RegistryEntryInfo -import net.minecraft.registry.entry.RegistryEntryList -import net.minecraft.registry.entry.RegistryEntryOwner -import net.minecraft.registry.tag.TagKey +import net.minecraft.resource.DataConfiguration +import net.minecraft.resource.ResourcePackManager import net.minecraft.resource.featuretoggle.FeatureFlags import net.minecraft.resource.featuretoggle.FeatureSet import net.minecraft.scoreboard.Scoreboard +import net.minecraft.server.SaveLoading +import net.minecraft.server.command.CommandManager import net.minecraft.sound.SoundCategory import net.minecraft.sound.SoundEvent import net.minecraft.util.Identifier @@ -43,11 +42,8 @@ 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 @@ -59,430 +55,284 @@ 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.explosion.ExplosionBehavior 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 - } - - override fun getKey(): RegistryKey<out Registry<T>> { - return key - } - - override fun getEntryInfo(key: RegistryKey<T>?): Optional<RegistryEntryInfo> { - TODO("Not yet implemented") - } - - override fun getLifecycle(): Lifecycle { - return Lifecycle.stable() - } - - override fun getDefaultEntry(): Optional<RegistryEntry.Reference<T>> { - return Optional.empty() - } - - override fun getIds(): MutableSet<Identifier> { - return idLookup.keys.mapTo(mutableSetOf()) { it.value } - } - - override fun getEntrySet(): MutableSet<MutableMap.MutableEntry<RegistryKey<T>, T>> { - return map.entries - } - - override fun getKeys(): MutableSet<RegistryKey<T>> { - return map.keys - } - - override fun getRandom(random: Random?): Optional<RegistryEntry.Reference<T>> { - return registryWrapper.streamEntries().findFirst() - } - - override fun containsId(id: Identifier?): Boolean { - return idLookup.containsKey(RegistryKey.of(key, id ?: return false)) - } - - override fun freeze(): Registry<T> { - return this - } - - override fun getEntry(rawId: Int): Optional<RegistryEntry.Reference<T>> { - val x = inverseIdLookup[rawId] ?: return Optional.empty() - return Optional.of(RegistryEntry.Reference.standAlone(registryWrapper, x)) - } - - override fun streamEntries(): Stream<RegistryEntry.Reference<T>> { - return registryWrapper.streamEntries() - } - - override fun streamTagsAndEntries(): Stream<Pair<TagKey<T>, RegistryEntryList.Named<T>>> { - return streamTags().map { Pair(it, getOrCreateEntryList(it)) } - } - - override fun streamTags(): Stream<TagKey<T>> { - return registryWrapper.streamTagKeys() - } - - override fun clearTags() { - } - - override fun getEntryOwner(): RegistryEntryOwner<T> { - return registryWrapper - } - - override fun getReadOnlyWrapper(): RegistryWrapper.Impl<T> { - return registryWrapper - } - - override fun populateTags(tagEntries: MutableMap<TagKey<T>, MutableList<RegistryEntry<T>>>?) { - } - - override fun getOrCreateEntryList(tag: TagKey<T>?): RegistryEntryList.Named<T> { - return getEntryList(tag).orElseGet { RegistryEntryList.of(registryWrapper, tag) } - } - - override fun getEntryList(tag: TagKey<T>?): Optional<RegistryEntryList.Named<T>> { - return registryWrapper.getOptional(tag ?: return Optional.empty()) - } - - override fun getEntry(value: T): RegistryEntry<T> { - return registryWrapper.getOptional(inverseLookup[value]!!).get() - } - - override fun getEntry(key: RegistryKey<T>?): Optional<RegistryEntry.Reference<T>> { - return registryWrapper.getOptional(key ?: return Optional.empty()) - } - - override fun getEntry(id: Identifier?): Optional<RegistryEntry.Reference<T>> { - TODO("Not yet implemented") - } - - override fun createEntry(value: T): RegistryEntry.Reference<T> { - TODO("Not yet implemented") - } - - override fun contains(key: RegistryKey<T>?): Boolean { - return getEntry(key).isPresent - } - - override fun getId(value: T): Identifier? { - return (inverseLookup[value] ?: return null).value - } - - override fun getKey(entry: T): Optional<RegistryKey<T>> { - return Optional.ofNullable(inverseLookup[entry ?: return Optional.empty()]) - } - } -} +import moe.nea.firmament.util.MC fun createDynamicRegistry(): DynamicRegistryManager.Immutable { - val wrapperLookup = BuiltinRegistries.createWrapperLookup() - return object : DynamicRegistryManager.Immutable { - override fun <E : Any?> getOptional(key: RegistryKey<out Registry<out E>>): Optional<Registry<E>> { - val lookup = wrapperLookup.getOptionalWrapper(key).getOrNull() ?: return Optional.empty() - val registry = makeRegistry(lookup, key as RegistryKey<out Registry<E>>) - return Optional.of(registry) - } - - fun <T> entry(reg: RegistryKey<out Registry<T>>): DynamicRegistryManager.Entry<T> { - return DynamicRegistryManager.Entry(reg, getOptional(reg).get()) - } - - override fun streamAllRegistries(): Stream<DynamicRegistryManager.Entry<*>> { - return wrapperLookup.streamAllRegistryKeys() - .map { entry(it as RegistryKey<out Registry<Any>>) } - } - } + // TODO: use SaveLoading.load() to properly load a full registry + return DynamicRegistryManager.of(Registries.REGISTRIES) } class FakeWorld( - registries: DynamicRegistryManager.Immutable = createDynamicRegistry(), + registries: DynamicRegistryManager.Immutable = createDynamicRegistry(), ) : World( - Properties, - RegistryKey.of(RegistryKeys.WORLD, Identifier.of("firmament", "fakeworld")), - registries, - registries[RegistryKeys.DIMENSION_TYPE].entryOf( - RegistryKey.of( - RegistryKeys.DIMENSION_TYPE, - Identifier.of("minecraft", "overworld") - ) - ), - { DummyProfiler.INSTANCE }, - true, - false, - 0, 0 + Properties, + RegistryKey.of(RegistryKeys.WORLD, Identifier.of("firmament", "fakeworld")), + registries, + MC.defaultRegistries.getOrThrow(RegistryKeys.DIMENSION_TYPE) + .getOrThrow(RegistryKey.of(RegistryKeys.DIMENSION_TYPE, Identifier.of("minecraft", "overworld"))), + true, + false, + 0L, + 0 ) { - object Properties : MutableWorldProperties { - override fun getSpawnPos(): BlockPos { - return BlockPos.ORIGIN - } + object Properties : MutableWorldProperties { + override fun getSpawnPos(): BlockPos { + return BlockPos.ORIGIN + } - override fun getSpawnAngle(): Float { - return 0F - } + override fun getSpawnAngle(): Float { + return 0F + } - override fun getTime(): Long { - return 0 - } + override fun getTime(): Long { + return 0 + } - override fun getTimeOfDay(): Long { - return 0 - } + override fun getTimeOfDay(): Long { + return 0 + } - override fun isThundering(): Boolean { - return false - } + override fun isThundering(): Boolean { + return false + } - override fun isRaining(): Boolean { - return false - } + override fun isRaining(): Boolean { + return false + } - override fun setRaining(raining: Boolean) { - } + override fun setRaining(raining: Boolean) { + } - override fun isHardcore(): Boolean { - return false - } - - override fun getGameRules(): GameRules { - return GameRules() - } - - override fun getDifficulty(): Difficulty { - return Difficulty.HARD - } - - override fun isDifficultyLocked(): Boolean { - return false - } - - override fun setSpawnPos(pos: BlockPos?, angle: Float) {} - } + override fun isHardcore(): Boolean { + return false + } + + override fun getDifficulty(): Difficulty { + return Difficulty.HARD + } + + override fun isDifficultyLocked(): Boolean { + return false + } + + override fun setSpawnPos(pos: BlockPos?, angle: Float) {} + } + + override fun getPlayers(): List<PlayerEntity> { + return emptyList() + } - override fun getPlayers(): List<PlayerEntity> { - return emptyList() - } - - override fun getBrightness(direction: Direction?, shaded: Boolean): Float { - return 1f - } - - override fun getGeneratorStoredBiome(biomeX: Int, biomeY: Int, biomeZ: Int): RegistryEntry<Biome> { - return registryManager.get(RegistryKeys.BIOME).entryOf(BiomeKeys.PLAINS) - } - - override fun getEnabledFeatures(): FeatureSet { - return FeatureFlags.VANILLA_FEATURES - } - - class FakeTickScheduler<T> : QueryableTickScheduler<T> { - override fun scheduleTick(orderedTick: OrderedTick<T>?) { - } - - override fun isQueued(pos: BlockPos?, type: T): Boolean { - return true - } - - override fun getTickCount(): Int { - return 0 - } - - override fun isTicking(pos: BlockPos?, type: T): Boolean { - return true - } - - } - - override fun getBlockTickScheduler(): QueryableTickScheduler<Block> { - return FakeTickScheduler() - } - - override fun getFluidTickScheduler(): QueryableTickScheduler<Fluid> { - return FakeTickScheduler() - } - - - class FakeChunkManager(val world: FakeWorld) : ChunkManager() { - override fun getChunk(x: Int, z: Int, leastStatus: ChunkStatus?, create: Boolean): Chunk { - return EmptyChunk( - world, - ChunkPos(x, z), - world.registryManager.get(RegistryKeys.BIOME).entryOf(BiomeKeys.PLAINS) - ) - } - - override fun getWorld(): BlockView { - return world - } - - override fun tick(shouldKeepTicking: BooleanSupplier?, tickChunks: Boolean) { - } - - override fun getDebugString(): String { - return "FakeChunkManager" - } - - override fun getLoadedChunkCount(): Int { - return 0 - } - - override fun getLightingProvider(): LightingProvider { - return FakeLightingProvider(this) - } - } - - class FakeLightingProvider(chunkManager: FakeChunkManager) : LightingProvider(chunkManager, false, false) - - override fun getChunkManager(): ChunkManager { - return FakeChunkManager(this) - } - - override fun playSound( - source: PlayerEntity?, - x: Double, - y: Double, - z: Double, - sound: RegistryEntry<SoundEvent>?, - category: SoundCategory?, - volume: Float, - pitch: Float, - seed: Long - ) { - } - - override fun syncWorldEvent(player: PlayerEntity?, eventId: Int, pos: BlockPos?, data: Int) { - } - - override fun emitGameEvent(event: RegistryEntry<GameEvent>?, emitterPos: Vec3d?, emitter: GameEvent.Emitter?) { - } - - override fun updateListeners(pos: BlockPos?, oldState: BlockState?, newState: BlockState?, flags: Int) { - } - - override fun playSoundFromEntity( - source: PlayerEntity?, - entity: Entity?, - sound: RegistryEntry<SoundEvent>?, - category: SoundCategory?, - volume: Float, - pitch: Float, - seed: Long - ) { - } - - override fun asString(): String { - return "FakeWorld" - } - - override fun getEntityById(id: Int): Entity? { - return null - } - - override fun getTickManager(): TickManager { - return TickManager() - } - - override fun getMapState(id: MapIdComponent?): MapState? { - return null - } - - override fun putMapState(id: MapIdComponent?, state: MapState?) { - } - - override fun increaseAndGetMapId(): MapIdComponent { - return MapIdComponent(0) - } - - override fun setBlockBreakingInfo(entityId: Int, pos: BlockPos?, progress: Int) { - } - - override fun getScoreboard(): Scoreboard { - return Scoreboard() - } - - override fun getRecipeManager(): RecipeManager { - return RecipeManager(registryManager) - } - - object FakeEntityLookup : EntityLookup<Entity> { - override fun get(id: Int): Entity? { - return null - } - - override fun get(uuid: UUID?): Entity? { - return null - } - - override fun iterate(): MutableIterable<Entity> { - return mutableListOf() - } - - override fun <U : Entity?> forEachIntersects( - filter: TypeFilter<Entity, U>?, - box: Box?, - consumer: LazyIterationConsumer<U>? - ) { - } - - override fun forEachIntersects(box: Box?, action: Consumer<Entity>?) { - } - - override fun <U : Entity?> forEach(filter: TypeFilter<Entity, U>?, consumer: LazyIterationConsumer<U>?) { - } - - } - - override fun getEntityLookup(): EntityLookup<Entity> { - return FakeEntityLookup - } - - override fun getBrewingRecipeRegistry(): BrewingRecipeRegistry { - return BrewingRecipeRegistry.EMPTY - } + override fun getBrightness(direction: Direction?, shaded: Boolean): Float { + return 1f + } + + override fun getGeneratorStoredBiome(biomeX: Int, biomeY: Int, biomeZ: Int): RegistryEntry<Biome> { + return registryManager.getOptionalEntry(BiomeKeys.PLAINS).get() + } + + override fun getSeaLevel(): Int { + return 0 + } + + override fun getEnabledFeatures(): FeatureSet { + return FeatureFlags.VANILLA_FEATURES + } + + class FakeTickScheduler<T> : QueryableTickScheduler<T> { + override fun scheduleTick(orderedTick: OrderedTick<T>?) { + } + + override fun isQueued(pos: BlockPos?, type: T): Boolean { + return true + } + + override fun getTickCount(): Int { + return 0 + } + + override fun isTicking(pos: BlockPos?, type: T): Boolean { + return true + } + + } + + override fun getBlockTickScheduler(): QueryableTickScheduler<Block> { + return FakeTickScheduler() + } + + override fun getFluidTickScheduler(): QueryableTickScheduler<Fluid> { + return FakeTickScheduler() + } + + + class FakeChunkManager(val world: FakeWorld) : ChunkManager() { + override fun getChunk(x: Int, z: Int, leastStatus: ChunkStatus?, create: Boolean): Chunk { + return EmptyChunk( + world, + ChunkPos(x, z), + world.registryManager.getOptionalEntry(BiomeKeys.PLAINS).get() + ) + } + + override fun getWorld(): BlockView { + return world + } + + override fun tick(shouldKeepTicking: BooleanSupplier?, tickChunks: Boolean) { + } + + override fun getDebugString(): String { + return "FakeChunkManager" + } + + override fun getLoadedChunkCount(): Int { + return 0 + } + + override fun getLightingProvider(): LightingProvider { + return FakeLightingProvider(this) + } + } + + class FakeLightingProvider(chunkManager: FakeChunkManager) : LightingProvider(chunkManager, false, false) + + override fun getChunkManager(): ChunkManager { + return FakeChunkManager(this) + } + + override fun playSound( + source: PlayerEntity?, + x: Double, + y: Double, + z: Double, + sound: RegistryEntry<SoundEvent>?, + category: SoundCategory?, + volume: Float, + pitch: Float, + seed: Long + ) { + } + + override fun syncWorldEvent(player: PlayerEntity?, eventId: Int, pos: BlockPos?, data: Int) { + } + + override fun emitGameEvent(event: RegistryEntry<GameEvent>?, emitterPos: Vec3d?, emitter: GameEvent.Emitter?) { + } + + override fun updateListeners(pos: BlockPos?, oldState: BlockState?, newState: BlockState?, flags: Int) { + } + + override fun playSoundFromEntity( + source: PlayerEntity?, + entity: Entity?, + sound: RegistryEntry<SoundEvent>?, + category: SoundCategory?, + volume: Float, + pitch: Float, + seed: Long + ) { + } + + override fun createExplosion( + entity: Entity?, + damageSource: DamageSource?, + behavior: ExplosionBehavior?, + x: Double, + y: Double, + z: Double, + power: Float, + createFire: Boolean, + explosionSourceType: ExplosionSourceType?, + smallParticle: ParticleEffect?, + largeParticle: ParticleEffect?, + soundEvent: RegistryEntry<SoundEvent>? + ) { + TODO("Not yet implemented") + } + + override fun asString(): String { + return "FakeWorld" + } + + override fun getEntityById(id: Int): Entity? { + return null + } + + override fun getTickManager(): TickManager { + return TickManager() + } + + override fun getMapState(id: MapIdComponent?): MapState? { + return null + } + + override fun putMapState(id: MapIdComponent?, state: MapState?) { + } + + override fun increaseAndGetMapId(): MapIdComponent { + return MapIdComponent(0) + } + + override fun setBlockBreakingInfo(entityId: Int, pos: BlockPos?, progress: Int) { + } + + override fun getScoreboard(): Scoreboard { + return Scoreboard() + } + + override fun getRecipeManager(): RecipeManager { + return object : RecipeManager { + override fun getPropertySet(key: RegistryKey<RecipePropertySet>?): RecipePropertySet { + return RecipePropertySet.EMPTY + } + + override fun getStonecutterRecipes(): CuttingRecipeDisplay.Grouping<StonecuttingRecipe> { + return CuttingRecipeDisplay.Grouping.empty() + } + } + } + + object FakeEntityLookup : EntityLookup<Entity> { + override fun get(id: Int): Entity? { + return null + } + + override fun get(uuid: UUID?): Entity? { + return null + } + + override fun iterate(): MutableIterable<Entity> { + return mutableListOf() + } + + override fun <U : Entity?> forEachIntersects( + filter: TypeFilter<Entity, U>?, + box: Box?, + consumer: LazyIterationConsumer<U>? + ) { + } + + override fun forEachIntersects(box: Box?, action: Consumer<Entity>?) { + } + + override fun <U : Entity?> forEach(filter: TypeFilter<Entity, U>?, consumer: LazyIterationConsumer<U>?) { + } + + } + + override fun getEntityLookup(): EntityLookup<Entity> { + return FakeEntityLookup + } + + override fun getBrewingRecipeRegistry(): BrewingRecipeRegistry { + return BrewingRecipeRegistry.EMPTY + } + + override fun getFuelRegistry(): FuelRegistry { + TODO("Not yet implemented") + } } diff --git a/src/main/kotlin/gui/entity/GuiPlayer.kt b/src/main/kotlin/gui/entity/GuiPlayer.kt index d00b44d..aa0bea8 100644 --- a/src/main/kotlin/gui/entity/GuiPlayer.kt +++ b/src/main/kotlin/gui/entity/GuiPlayer.kt @@ -1,8 +1,7 @@ - package moe.nea.firmament.gui.entity import com.mojang.authlib.GameProfile -import java.util.* +import java.util.UUID import net.minecraft.client.network.AbstractClientPlayerEntity import net.minecraft.client.util.DefaultSkinHelper import net.minecraft.client.util.SkinTextures @@ -15,40 +14,40 @@ import net.minecraft.world.World /** * @see moe.nea.firmament.init.EarlyRiser */ -fun makeGuiPlayer(world: FakeWorld): GuiPlayer { - val constructor = GuiPlayer::class.java.getDeclaredConstructor( - World::class.java, - BlockPos::class.java, - Float::class.javaPrimitiveType, - GameProfile::class.java - ) - return constructor.newInstance(world, BlockPos.ORIGIN, 0F, GameProfile(UUID.randomUUID(), "Linnea")) +fun makeGuiPlayer(world: World): GuiPlayer { + val constructor = GuiPlayer::class.java.getDeclaredConstructor( + World::class.java, + BlockPos::class.java, + Float::class.javaPrimitiveType, + GameProfile::class.java + ) + return constructor.newInstance(world, BlockPos.ORIGIN, 0F, GameProfile(UUID.randomUUID(), "Linnea")) } class GuiPlayer(world: ClientWorld?, profile: GameProfile?) : AbstractClientPlayerEntity(world, profile) { - override fun isSpectator(): Boolean { - return false - } + override fun isSpectator(): Boolean { + return false + } - override fun isCreative(): Boolean { - return false - } + override fun isCreative(): Boolean { + return false + } - override fun shouldRenderName(): Boolean { - return false - } + override fun shouldRenderName(): Boolean { + return false + } - var skinTexture: Identifier = DefaultSkinHelper.getSkinTextures(this.getUuid()).texture - var capeTexture: Identifier? = null - var model: Model = Model.WIDE - override fun getSkinTextures(): SkinTextures { - return SkinTextures( - skinTexture, - null, - capeTexture, - null, - model, - true - ) - } + var skinTexture: Identifier = DefaultSkinHelper.getSkinTextures(this.getUuid()).texture + var capeTexture: Identifier? = null + var model: Model = Model.WIDE + override fun getSkinTextures(): SkinTextures { + return SkinTextures( + skinTexture, + null, + capeTexture, + null, + model, + true + ) + } } diff --git a/src/main/kotlin/gui/entity/ModifyHorse.kt b/src/main/kotlin/gui/entity/ModifyHorse.kt index 8ac011b..f094ca4 100644 --- a/src/main/kotlin/gui/entity/ModifyHorse.kt +++ b/src/main/kotlin/gui/entity/ModifyHorse.kt @@ -8,6 +8,7 @@ import kotlin.experimental.inv import kotlin.experimental.or import net.minecraft.entity.EntityType import net.minecraft.entity.LivingEntity +import net.minecraft.entity.SpawnReason import net.minecraft.entity.passive.AbstractHorseEntity import net.minecraft.item.ItemStack import net.minecraft.item.Items @@ -19,11 +20,11 @@ object ModifyHorse : EntityModifier { var entity: AbstractHorseEntity = entity info["kind"]?.let { entity = when (it.asString) { - "skeleton" -> EntityType.SKELETON_HORSE.create(fakeWorld)!! - "zombie" -> EntityType.ZOMBIE_HORSE.create(fakeWorld)!! - "mule" -> EntityType.MULE.create(fakeWorld)!! - "donkey" -> EntityType.DONKEY.create(fakeWorld)!! - "horse" -> EntityType.HORSE.create(fakeWorld)!! + "skeleton" -> EntityType.SKELETON_HORSE.create(fakeWorld, SpawnReason.LOAD)!! + "zombie" -> EntityType.ZOMBIE_HORSE.create(fakeWorld, SpawnReason.LOAD)!! + "mule" -> EntityType.MULE.create(fakeWorld, SpawnReason.LOAD)!! + "donkey" -> EntityType.DONKEY.create(fakeWorld, SpawnReason.LOAD)!! + "horse" -> EntityType.HORSE.create(fakeWorld, SpawnReason.LOAD)!! else -> error("Unknown horse kind $it") } } diff --git a/src/main/kotlin/repo/RepoDownloadManager.kt b/src/main/kotlin/repo/RepoDownloadManager.kt index d674f23..3efd83b 100644 --- a/src/main/kotlin/repo/RepoDownloadManager.kt +++ b/src/main/kotlin/repo/RepoDownloadManager.kt @@ -5,7 +5,7 @@ package moe.nea.firmament.repo import io.ktor.client.call.body import io.ktor.client.request.get import io.ktor.client.statement.bodyAsChannel -import io.ktor.utils.io.jvm.nio.copyTo +import io.ktor.utils.io.copyTo import java.io.IOException import java.nio.file.Files import java.nio.file.Path diff --git a/src/main/kotlin/repo/RepoManager.kt b/src/main/kotlin/repo/RepoManager.kt index c052fb9..725642e 100644 --- a/src/main/kotlin/repo/RepoManager.kt +++ b/src/main/kotlin/repo/RepoManager.kt @@ -9,10 +9,12 @@ import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents import kotlinx.coroutines.launch import net.minecraft.client.MinecraftClient import net.minecraft.network.packet.s2c.play.SynchronizeRecipesS2CPacket +import net.minecraft.recipe.display.CuttingRecipeDisplay import moe.nea.firmament.Firmament import moe.nea.firmament.Firmament.logger import moe.nea.firmament.events.ReloadRegistrationEvent import moe.nea.firmament.gui.config.ManagedConfig +import moe.nea.firmament.util.MC import moe.nea.firmament.util.MinecraftDispatcher import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.tr @@ -77,7 +79,7 @@ object RepoManager { private fun trySendClientboundUpdateRecipesPacket(): Boolean { return MinecraftClient.getInstance().world != null && MinecraftClient.getInstance().networkHandler?.onSynchronizeRecipes( - SynchronizeRecipesS2CPacket(mutableListOf()) + SynchronizeRecipesS2CPacket(mutableMapOf(), CuttingRecipeDisplay.Grouping.empty()) ) != null } @@ -92,7 +94,7 @@ object RepoManager { fun launchAsyncUpdate(force: Boolean = false) { Firmament.coroutineScope.launch { - ItemCache.ReloadProgressHud.reportProgress("Downloading", 0, -1) // TODO: replace with a proper boundy bar + ItemCache.ReloadProgressHud.reportProgress("Downloading", 0, -1) // TODO: replace with a proper bouncy bar ItemCache.ReloadProgressHud.isEnabled = true try { RepoDownloadManager.downloadUpdate(force) @@ -112,7 +114,7 @@ object RepoManager { ItemCache.ReloadProgressHud.isEnabled = true neuRepo.reload() } catch (exc: NEURepositoryException) { - MinecraftClient.getInstance().player?.sendMessage( + MC.sendChat( tr("firmament.repo.reloadfail", "Failed to reload repository. This will result in some mod features not working.") ) diff --git a/src/main/kotlin/repo/SBItemStack.kt b/src/main/kotlin/repo/SBItemStack.kt index 281075d..18126ee 100644 --- a/src/main/kotlin/repo/SBItemStack.kt +++ b/src/main/kotlin/repo/SBItemStack.kt @@ -1,9 +1,14 @@ package moe.nea.firmament.repo +import com.mojang.serialization.Codec +import com.mojang.serialization.codecs.RecordCodecBuilder import io.github.moulberry.repo.constants.PetNumbers import io.github.moulberry.repo.data.NEUIngredient import io.github.moulberry.repo.data.NEUItem import net.minecraft.item.ItemStack +import net.minecraft.network.RegistryByteBuf +import net.minecraft.network.codec.PacketCodec +import net.minecraft.network.codec.PacketCodecs import net.minecraft.text.Text import net.minecraft.util.Formatting import moe.nea.firmament.repo.ItemCache.asItemStack @@ -40,6 +45,21 @@ data class SBItemStack constructor( } companion object { + val PACKET_CODEC: PacketCodec<in RegistryByteBuf, SBItemStack> = PacketCodec.tuple( + SkyblockId.PACKET_CODEC, { it.skyblockId }, + PacketCodecs.VAR_INT, { it.stackSize }, + { id, count -> SBItemStack(id, count) } + ) + val CODEC: Codec<SBItemStack> = RecordCodecBuilder.create { + it.group( + SkyblockId.CODEC.fieldOf("skyblockId").forGetter { it.skyblockId }, + Codec.INT.fieldOf("count").forGetter { it.stackSize }, + ).apply(it) { id, count -> + SBItemStack(id, count) + } + } + val EMPTY = SBItemStack(SkyblockId.NULL, 0) + operator fun invoke(itemStack: ItemStack): SBItemStack { val skyblockId = itemStack.skyBlockId ?: SkyblockId.NULL return SBItemStack( @@ -114,6 +134,8 @@ data class SBItemStack constructor( val itemStack = itemStack_ ?: run { if (skyblockId == SkyblockId.COINS) return@run ItemCache.coinItem(stackSize).also { it.appendLore(extraLore) } + if (stackSize == 0) + return@run ItemStack.EMPTY val replacementData = mutableMapOf<String, String>() injectReplacementDataForPets(replacementData) return@run neuItem.asItemStack(idHint = skyblockId, replacementData) diff --git a/src/main/kotlin/repo/recipes/GenericRecipeRenderer.kt b/src/main/kotlin/repo/recipes/GenericRecipeRenderer.kt new file mode 100644 index 0000000..9a1aea5 --- /dev/null +++ b/src/main/kotlin/repo/recipes/GenericRecipeRenderer.kt @@ -0,0 +1,19 @@ +package moe.nea.firmament.repo.recipes + +import io.github.moulberry.repo.NEURepository +import io.github.moulberry.repo.data.NEURecipe +import me.shedaniel.math.Rectangle +import net.minecraft.item.ItemStack +import net.minecraft.text.Text +import net.minecraft.util.Identifier +import moe.nea.firmament.repo.SBItemStack + +interface GenericRecipeRenderer<T : NEURecipe> { + fun render(recipe: T, bounds: Rectangle, layouter: RecipeLayouter) + fun getInputs(recipe: T): Collection<SBItemStack> + fun getOutputs(recipe: T): Collection<SBItemStack> + val icon: ItemStack + val title: Text + val identifier: Identifier + fun findAllRecipes(neuRepository: NEURepository): Iterable<T> +} diff --git a/src/main/kotlin/repo/recipes/RecipeLayouter.kt b/src/main/kotlin/repo/recipes/RecipeLayouter.kt new file mode 100644 index 0000000..109bff5 --- /dev/null +++ b/src/main/kotlin/repo/recipes/RecipeLayouter.kt @@ -0,0 +1,33 @@ +package moe.nea.firmament.repo.recipes + +import io.github.notenoughupdates.moulconfig.gui.GuiComponent +import net.minecraft.text.Text +import moe.nea.firmament.repo.SBItemStack + +interface RecipeLayouter { + enum class SlotKind { + SMALL_INPUT, + SMALL_OUTPUT, + + /** + * Create a bigger background and mark the slot as output. The coordinates should still refer the upper left corner of the item stack, not of the bigger background. + */ + BIG_OUTPUT, + } + + fun createItemSlot( + x: Int, y: Int, + content: SBItemStack?, + slotKind: SlotKind, + ) + + fun createLabel( + x: Int, y: Int, + text: Text + ) + + fun createArrow(x: Int, y: Int) + + fun createMoulConfig(x: Int, y: Int, w: Int, h: Int, component: GuiComponent) +} + diff --git a/src/main/kotlin/repo/recipes/SBCraftingRecipeRenderer.kt b/src/main/kotlin/repo/recipes/SBCraftingRecipeRenderer.kt new file mode 100644 index 0000000..fd0c750 --- /dev/null +++ b/src/main/kotlin/repo/recipes/SBCraftingRecipeRenderer.kt @@ -0,0 +1,50 @@ +package moe.nea.firmament.repo.recipes + +import io.github.moulberry.repo.NEURepository +import io.github.moulberry.repo.data.NEUCraftingRecipe +import me.shedaniel.math.Point +import me.shedaniel.math.Rectangle +import net.minecraft.block.Blocks +import net.minecraft.item.ItemStack +import net.minecraft.text.Text +import net.minecraft.util.Identifier +import moe.nea.firmament.Firmament +import moe.nea.firmament.repo.SBItemStack +import moe.nea.firmament.util.tr + +class SBCraftingRecipeRenderer : GenericRecipeRenderer<NEUCraftingRecipe> { + override fun render(recipe: NEUCraftingRecipe, bounds: Rectangle, layouter: RecipeLayouter) { + val point = Point(bounds.centerX - 58, bounds.centerY - 27) + layouter.createArrow(point.x + 60, point.y + 18) + for (i in 0 until 3) { + for (j in 0 until 3) { + val item = recipe.inputs[i + j * 3] + layouter.createItemSlot(point.x + 1 + i * 18, + point.y + 1 + j * 18, + SBItemStack(item), + RecipeLayouter.SlotKind.SMALL_INPUT) + } + } + layouter.createItemSlot( + point.x + 95, point.y + 19, + SBItemStack(recipe.output), + RecipeLayouter.SlotKind.BIG_OUTPUT + ) + } + + override fun getInputs(recipe: NEUCraftingRecipe): Collection<SBItemStack> { + return recipe.allInputs.mapNotNull { SBItemStack(it) } + } + + override fun getOutputs(recipe: NEUCraftingRecipe): Collection<SBItemStack> { + return SBItemStack(recipe.output)?.let(::listOf) ?: emptyList() + } + + override fun findAllRecipes(neuRepository: NEURepository): Iterable<NEUCraftingRecipe> { + return neuRepository.items.items.values.flatMap { it.recipes }.filterIsInstance<NEUCraftingRecipe>() + } + + override val icon: ItemStack = ItemStack(Blocks.CRAFTING_TABLE) + override val title: Text = tr("firmament.category.crafting", "SkyBlock Crafting") + override val identifier: Identifier = Firmament.identifier("crafting_recipe") +} diff --git a/src/main/kotlin/util/ErrorUtil.kt b/src/main/kotlin/util/ErrorUtil.kt index afecf25..b06093b 100644 --- a/src/main/kotlin/util/ErrorUtil.kt +++ b/src/main/kotlin/util/ErrorUtil.kt @@ -1,25 +1,46 @@ +@file:OptIn(ExperimentalContracts::class) + package moe.nea.firmament.util +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract import moe.nea.firmament.Firmament +@Suppress("NOTHING_TO_INLINE") // Suppressed since i want the logger to not pick up the ErrorUtil stack-frame object ErrorUtil { var aggressiveErrors = run { Thread.currentThread().stackTrace.any { it.className.startsWith("org.junit.") } || Firmament.DEBUG + || ErrorUtil::class.java.desiredAssertionStatus() + } + + inline fun softCheck(message: String, check: Boolean) { + if (!check) softError(message) } - inline fun softCheck(message: String, func: () -> Boolean) { + inline fun lazyCheck(message: String, func: () -> Boolean) { + contract { + callsInPlace(func, InvocationKind.AT_MOST_ONCE) + } if (!aggressiveErrors) return if (func()) return error(message) } - @Suppress("NOTHING_TO_INLINE") // Suppressed since i want the logger to not pick up the ErrorUtil stack-frame + inline fun softError(message: String, exception: Throwable) { + if (aggressiveErrors) throw IllegalStateException(message, exception) + else Firmament.logger.error(message, exception) + } + inline fun softError(message: String) { if (aggressiveErrors) error(message) else Firmament.logger.error(message) } inline fun <T : Any> notNullOr(nullable: T?, message: String, orElse: () -> T): T { + contract { + callsInPlace(orElse, InvocationKind.AT_MOST_ONCE) + } if (nullable == null) { softError(message) return orElse() diff --git a/src/main/kotlin/util/MC.kt b/src/main/kotlin/util/MC.kt index fc42be9..1b7739f 100644 --- a/src/main/kotlin/util/MC.kt +++ b/src/main/kotlin/util/MC.kt @@ -3,9 +3,13 @@ package moe.nea.firmament.util import io.github.moulberry.repo.data.Coordinate import java.util.concurrent.ConcurrentLinkedQueue import net.minecraft.client.MinecraftClient +import net.minecraft.client.gui.screen.Screen import net.minecraft.client.gui.screen.ingame.HandledScreen -import net.minecraft.client.option.GameOptions +import net.minecraft.client.network.ClientPlayerEntity import net.minecraft.client.render.WorldRenderer +import net.minecraft.client.render.item.ItemRenderer +import net.minecraft.client.world.ClientWorld +import net.minecraft.entity.Entity import net.minecraft.item.Item import net.minecraft.network.packet.c2s.play.CommandExecutionC2SPacket import net.minecraft.registry.BuiltinRegistries @@ -14,7 +18,9 @@ import net.minecraft.registry.RegistryWrapper import net.minecraft.resource.ReloadableResourceManagerImpl import net.minecraft.text.Text import net.minecraft.util.math.BlockPos +import net.minecraft.world.World import moe.nea.firmament.events.TickEvent +import moe.nea.firmament.events.WorldReadyEvent object MC { @@ -29,6 +35,9 @@ object MC { (nextTickTodos.poll() ?: break).invoke() } } + WorldReadyEvent.subscribe("MC:ready") { + this.lastWorld + } } fun sendChat(text: Text) { @@ -69,6 +78,7 @@ object MC { inline val resourceManager get() = (instance.resourceManager as ReloadableResourceManagerImpl) + inline val itemRenderer: ItemRenderer get() = instance.itemRenderer inline val worldRenderer: WorldRenderer get() = instance.worldRenderer inline val networkHandler get() = player?.networkHandler inline val instance get() = MinecraftClient.getInstance() @@ -79,11 +89,11 @@ object MC { inline val inGameHud get() = instance.inGameHud inline val font get() = instance.textRenderer inline val soundManager get() = instance.soundManager - inline val player get() = instance.player - inline val camera get() = instance.cameraEntity + inline val player: ClientPlayerEntity? get() = instance.player + inline val camera: Entity? get() = instance.cameraEntity inline val guiAtlasManager get() = instance.guiAtlasManager - inline val world get() = instance.world - inline var screen + inline val world: ClientWorld? get() = instance.world + inline var screen: Screen? get() = instance.currentScreen set(value) = instance.setScreen(value) val screenName get() = screen?.title?.unformattedString?.trim() @@ -92,7 +102,13 @@ object MC { inline val currentRegistries: RegistryWrapper.WrapperLookup? get() = world?.registryManager val defaultRegistries: RegistryWrapper.WrapperLookup = BuiltinRegistries.createWrapperLookup() inline val currentOrDefaultRegistries get() = currentRegistries ?: defaultRegistries - val defaultItems: RegistryWrapper.Impl<Item> = defaultRegistries.getWrapperOrThrow(RegistryKeys.ITEM) + val defaultItems: RegistryWrapper.Impl<Item> = defaultRegistries.getOrThrow(RegistryKeys.ITEM) + var lastWorld: World? = null + get() { + field = world ?: field + return field + } + private set } diff --git a/src/main/kotlin/util/SBData.kt b/src/main/kotlin/util/SBData.kt index 0b2c404..051d070 100644 --- a/src/main/kotlin/util/SBData.kt +++ b/src/main/kotlin/util/SBData.kt @@ -37,7 +37,7 @@ object SBData { it.serverType.getOrNull()?.name?.uppercase(), it.mode.getOrNull(), it.map.getOrNull()) - SkyblockServerUpdateEvent.publish(SkyblockServerUpdateEvent(lastLocraw, null)) + SkyblockServerUpdateEvent.publish(SkyblockServerUpdateEvent(lastLocraw, locraw)) profileIdCommandDebounce = TimeMark.now() } } diff --git a/src/main/kotlin/util/SkyblockId.kt b/src/main/kotlin/util/SkyblockId.kt index 059e746..9c9287b 100644 --- a/src/main/kotlin/util/SkyblockId.kt +++ b/src/main/kotlin/util/SkyblockId.kt @@ -2,6 +2,7 @@ package moe.nea.firmament.util +import com.mojang.serialization.Codec import io.github.moulberry.repo.data.NEUIngredient import io.github.moulberry.repo.data.NEUItem import io.github.moulberry.repo.data.Rarity @@ -16,6 +17,9 @@ import net.minecraft.component.type.NbtComponent import net.minecraft.item.ItemStack import net.minecraft.item.Items import net.minecraft.nbt.NbtCompound +import net.minecraft.network.RegistryByteBuf +import net.minecraft.network.codec.PacketCodec +import net.minecraft.network.codec.PacketCodecs import net.minecraft.util.Identifier import moe.nea.firmament.repo.ItemCache.asItemStack import moe.nea.firmament.repo.set @@ -68,6 +72,9 @@ value class SkyblockId(val neuItem: String) { val NULL: SkyblockId = SkyblockId("null") val PET_NULL: SkyblockId = SkyblockId("null_pet") private val illlegalPathRegex = "[^a-z0-9_.-/]".toRegex() + val CODEC = Codec.STRING.xmap({ SkyblockId(it) }, { it.neuItem }) + val PACKET_CODEC: PacketCodec<in RegistryByteBuf, SkyblockId> = + PacketCodecs.STRING.xmap({ SkyblockId(it) }, { it.neuItem }) } } diff --git a/src/main/kotlin/util/data/IDataHolder.kt b/src/main/kotlin/util/data/IDataHolder.kt index cc97b58..1e9ba98 100644 --- a/src/main/kotlin/util/data/IDataHolder.kt +++ b/src/main/kotlin/util/data/IDataHolder.kt @@ -1,77 +1,71 @@ - - package moe.nea.firmament.util.data import java.util.concurrent.CopyOnWriteArrayList import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents import kotlin.reflect.KClass -import net.minecraft.client.MinecraftClient -import net.minecraft.server.command.CommandOutput import net.minecraft.text.Text import moe.nea.firmament.Firmament import moe.nea.firmament.events.ScreenChangeEvent +import moe.nea.firmament.util.MC interface IDataHolder<T> { - companion object { - internal var badLoads: MutableList<String> = CopyOnWriteArrayList() - private val allConfigs: MutableMap<KClass<out IDataHolder<*>>, IDataHolder<*>> = mutableMapOf() - private val dirty: MutableSet<KClass<out IDataHolder<*>>> = mutableSetOf() + companion object { + internal var badLoads: MutableList<String> = CopyOnWriteArrayList() + private val allConfigs: MutableMap<KClass<out IDataHolder<*>>, IDataHolder<*>> = mutableMapOf() + private val dirty: MutableSet<KClass<out IDataHolder<*>>> = mutableSetOf() - internal fun <T : IDataHolder<K>, K> putDataHolder(kClass: KClass<T>, inst: IDataHolder<K>) { - allConfigs[kClass] = inst - } + internal fun <T : IDataHolder<K>, K> putDataHolder(kClass: KClass<T>, inst: IDataHolder<K>) { + allConfigs[kClass] = inst + } - fun <T : IDataHolder<K>, K> markDirty(kClass: KClass<T>) { - if (kClass !in allConfigs) { - Firmament.logger.error("Tried to markDirty '${kClass.qualifiedName}', which isn't registered as 'IConfigHolder'") - return - } - dirty.add(kClass) - } + fun <T : IDataHolder<K>, K> markDirty(kClass: KClass<T>) { + if (kClass !in allConfigs) { + Firmament.logger.error("Tried to markDirty '${kClass.qualifiedName}', which isn't registered as 'IConfigHolder'") + return + } + dirty.add(kClass) + } - private fun performSaves() { - val toSave = dirty.toList().also { - dirty.clear() - } - for (it in toSave) { - val obj = allConfigs[it] - if (obj == null) { - Firmament.logger.error("Tried to save '${it}', which isn't registered as 'ConfigHolder'") - continue - } - obj.save() - } - } + private fun performSaves() { + val toSave = dirty.toList().also { + dirty.clear() + } + for (it in toSave) { + val obj = allConfigs[it] + if (obj == null) { + Firmament.logger.error("Tried to save '${it}', which isn't registered as 'ConfigHolder'") + continue + } + obj.save() + } + } - private fun warnForResetConfigs(player: CommandOutput) { - if (badLoads.isNotEmpty()) { - player.sendMessage( - Text.literal( - "The following configs have been reset: ${badLoads.joinToString(", ")}. " + - "This can be intentional, but probably isn't." - ) - ) - badLoads.clear() - } - } + private fun warnForResetConfigs() { + if (badLoads.isNotEmpty()) { + MC.sendChat( + Text.literal( + "The following configs have been reset: ${badLoads.joinToString(", ")}. " + + "This can be intentional, but probably isn't." + ) + ) + badLoads.clear() + } + } - fun registerEvents() { - ScreenChangeEvent.subscribe("IDataHolder:saveOnScreenChange") { event -> - performSaves() - val p = MinecraftClient.getInstance().player - if (p != null) { - warnForResetConfigs(p) - } - } - ClientLifecycleEvents.CLIENT_STOPPING.register(ClientLifecycleEvents.ClientStopping { - performSaves() - }) - } + fun registerEvents() { + ScreenChangeEvent.subscribe("IDataHolder:saveOnScreenChange") { event -> + performSaves() + warnForResetConfigs() + } + ClientLifecycleEvents.CLIENT_STOPPING.register(ClientLifecycleEvents.ClientStopping { + performSaves() + }) + } - } + } - val data: T - fun save() - fun markDirty() - fun load() + val data: T + fun save() + fun markDirty() + fun load() } diff --git a/src/main/kotlin/util/render/DrawContextExt.kt b/src/main/kotlin/util/render/DrawContextExt.kt index fc38aa6..da0b0b0 100644 --- a/src/main/kotlin/util/render/DrawContextExt.kt +++ b/src/main/kotlin/util/render/DrawContextExt.kt @@ -4,12 +4,70 @@ import com.mojang.blaze3d.systems.RenderSystem import me.shedaniel.math.Color import org.joml.Matrix4f import net.minecraft.client.gui.DrawContext +import net.minecraft.client.render.RenderLayer +import net.minecraft.client.render.RenderLayer.MultiPhaseParameters +import net.minecraft.client.render.RenderPhase +import net.minecraft.client.render.VertexFormat +import net.minecraft.client.render.VertexFormats +import net.minecraft.util.Identifier +import net.minecraft.util.TriState +import net.minecraft.util.Util import moe.nea.firmament.util.MC fun DrawContext.isUntranslatedGuiDrawContext(): Boolean { return (matrices.peek().positionMatrix.properties() and Matrix4f.PROPERTY_IDENTITY.toInt()) != 0 } +object GuiRenderLayers { + val GUI_TEXTURED_NO_DEPTH = Util.memoize<Identifier, RenderLayer> { texture: Identifier -> + RenderLayer.of("firmament_gui_textured_no_depth", + VertexFormats.POSITION_TEXTURE_COLOR, + VertexFormat.DrawMode.QUADS, + RenderLayer.CUTOUT_BUFFER_SIZE, + MultiPhaseParameters.builder() + .texture(RenderPhase.Texture(texture, TriState.FALSE, false)) + .program(RenderPhase.POSITION_TEXTURE_COLOR_PROGRAM) + .transparency(RenderPhase.TRANSLUCENT_TRANSPARENCY) + .depthTest(RenderPhase.ALWAYS_DEPTH_TEST) + .build(false)) + } +} + +@Deprecated("Use the other drawGuiTexture") +fun DrawContext.drawGuiTexture( + x: Int, y: Int, z: Int, width: Int, height: Int, sprite: Identifier +) = this.drawGuiTexture(RenderLayer::getGuiTextured, sprite, x, y, width, height) + +fun DrawContext.drawGuiTexture( + sprite: Identifier, + x: Int, y: Int, width: Int, height: Int +) = this.drawGuiTexture(RenderLayer::getGuiTextured, sprite, x, y, width, height) + +fun DrawContext.drawTexture( + sprite: Identifier, + x: Int, + y: Int, + u: Float, + v: Float, + width: Int, + height: Int, + textureWidth: Int, + textureHeight: Int +) { + this.drawTexture(RenderLayer::getGuiTextured, + sprite, + x, + y, + u, + v, + width, + height, + width, + height, + textureWidth, + textureHeight) +} + fun DrawContext.drawLine(fromX: Int, fromY: Int, toX: Int, toY: Int, color: Color) { // TODO: push scissors // TODO: use matrix translations and a different render layer @@ -18,11 +76,12 @@ fun DrawContext.drawLine(fromX: Int, fromY: Int, toX: Int, toY: Int, color: Colo return } RenderSystem.lineWidth(MC.window.scaleFactor.toFloat()) - val buf = this.vertexConsumers.getBuffer(RenderInWorldContext.RenderLayers.LINES) - buf.vertex(fromX.toFloat(), fromY.toFloat(), 0F).color(color.color) - .normal(toX - fromX.toFloat(), toY - fromY.toFloat(), 0F) - buf.vertex(toX.toFloat(), toY.toFloat(), 0F).color(color.color) - .normal(toX - fromX.toFloat(), toY - fromY.toFloat(), 0F) - this.draw() + draw { vertexConsumers -> + val buf = vertexConsumers.getBuffer(RenderInWorldContext.RenderLayers.LINES) + buf.vertex(fromX.toFloat(), fromY.toFloat(), 0F).color(color.color) + .normal(toX - fromX.toFloat(), toY - fromY.toFloat(), 0F) + buf.vertex(toX.toFloat(), toY.toFloat(), 0F).color(color.color) + .normal(toX - fromX.toFloat(), toY - fromY.toFloat(), 0F) + } } diff --git a/src/main/kotlin/util/render/FacingThePlayerContext.kt b/src/main/kotlin/util/render/FacingThePlayerContext.kt index eb37e35..daa8da9 100644 --- a/src/main/kotlin/util/render/FacingThePlayerContext.kt +++ b/src/main/kotlin/util/render/FacingThePlayerContext.kt @@ -76,13 +76,10 @@ class FacingThePlayerContext(val worldContext: RenderInWorldContext) { u1: Float, v1: Float, u2: Float, v2: Float, ) { - RenderSystem.setShaderTexture(0, texture) - RenderSystem.setShader(GameRenderer::getPositionTexColorProgram) + val buf = worldContext.vertexConsumers.getBuffer(RenderLayer.getGuiTexturedOverlay(texture)) val hw = width / 2F val hh = height / 2F val matrix4f: Matrix4f = worldContext.matrixStack.peek().positionMatrix - val buf = Tessellator.getInstance() - .begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR) buf.vertex(matrix4f, -hw, -hh, 0F) .color(-1) .texture(u1, v1).next() @@ -95,7 +92,7 @@ class FacingThePlayerContext(val worldContext: RenderInWorldContext) { buf.vertex(matrix4f, +hw, -hh, 0F) .color(-1) .texture(u2, v1).next() - BufferRenderer.drawWithGlobalProgram(buf.end()) + worldContext.vertexConsumers.draw() } } diff --git a/src/main/kotlin/util/render/FirmamentShaders.kt b/src/main/kotlin/util/render/FirmamentShaders.kt index 1094bc2..ba67dbb 100644 --- a/src/main/kotlin/util/render/FirmamentShaders.kt +++ b/src/main/kotlin/util/render/FirmamentShaders.kt @@ -1,23 +1,30 @@ package moe.nea.firmament.util.render -import net.minecraft.client.gl.ShaderProgram +import net.minecraft.client.gl.Defines +import net.minecraft.client.gl.ShaderProgramKey import net.minecraft.client.render.RenderPhase +import net.minecraft.client.render.VertexFormat import net.minecraft.client.render.VertexFormats +import moe.nea.firmament.Firmament import moe.nea.firmament.annotations.Subscribe -import moe.nea.firmament.events.RegisterCustomShadersEvent +import moe.nea.firmament.events.DebugInstantiateEvent +import moe.nea.firmament.util.MC object FirmamentShaders { + val shaders = mutableListOf<ShaderProgramKey>() + private fun shader(name: String, format: VertexFormat, defines: Defines): ShaderProgramKey { + val key = ShaderProgramKey(Firmament.identifier(name), format, defines) + shaders.add(key) + return key + } - private lateinit var _LINES: ShaderProgram - val LINES = RenderPhase.ShaderProgram({ _LINES }) + val LINES = RenderPhase.ShaderProgram(shader("core/rendertype_lines", VertexFormats.LINES, Defines.EMPTY)) - @Subscribe - fun registerCustomShaders(event: RegisterCustomShadersEvent) { - event.register( - "firmament_rendertype_lines", - VertexFormats.LINES, - { _LINES = it }, - ) - } + @Subscribe + fun debugLoad(event: DebugInstantiateEvent) { + shaders.forEach { + MC.instance.shaderLoader.getOrCreateProgram(it) + } + } } diff --git a/src/main/kotlin/util/render/RenderCircleProgress.kt b/src/main/kotlin/util/render/RenderCircleProgress.kt index a2f42b5..9cc383c 100644 --- a/src/main/kotlin/util/render/RenderCircleProgress.kt +++ b/src/main/kotlin/util/render/RenderCircleProgress.kt @@ -1,4 +1,3 @@ - package moe.nea.firmament.util.render import com.mojang.blaze3d.systems.RenderSystem @@ -9,7 +8,8 @@ import kotlin.math.atan2 import kotlin.math.tan import net.minecraft.client.gui.DrawContext import net.minecraft.client.render.BufferRenderer -import net.minecraft.client.render.GameRenderer +import net.minecraft.client.render.RenderLayer +import net.minecraft.client.render.RenderPhase import net.minecraft.client.render.Tessellator import net.minecraft.client.render.VertexFormat.DrawMode import net.minecraft.client.render.VertexFormats @@ -17,79 +17,77 @@ import net.minecraft.util.Identifier object RenderCircleProgress { - fun renderCircle( - drawContext: DrawContext, - texture: Identifier, - progress: Float, - u1: Float, - u2: Float, - v1: Float, - v2: Float, - ) { - RenderSystem.setShaderTexture(0, texture) - RenderSystem.setShader(GameRenderer::getPositionTexColorProgram) - RenderSystem.enableBlend() - val matrix: Matrix4f = drawContext.matrices.peek().positionMatrix - val bufferBuilder = Tessellator.getInstance().begin(DrawMode.TRIANGLES, VertexFormats.POSITION_TEXTURE_COLOR) - - val corners = listOf( - Vector2f(0F, -1F), - Vector2f(1F, -1F), - Vector2f(1F, 0F), - Vector2f(1F, 1F), - Vector2f(0F, 1F), - Vector2f(-1F, 1F), - Vector2f(-1F, 0F), - Vector2f(-1F, -1F), - ) + fun renderCircle( + drawContext: DrawContext, + texture: Identifier, + progress: Float, + u1: Float, + u2: Float, + v1: Float, + v2: Float, + ) { + RenderSystem.enableBlend() + drawContext.draw { + val bufferBuilder = it.getBuffer(RenderLayer.getGuiTexturedOverlay(texture)) + val matrix: Matrix4f = drawContext.matrices.peek().positionMatrix - for (i in (0 until 8)) { - if (progress < i / 8F) { - break - } - val second = corners[(i + 1) % 8] - val first = corners[i] - if (progress <= (i + 1) / 8F) { - val internalProgress = 1 - (progress - i / 8F) * 8F - val angle = lerpAngle( - atan2(second.y, second.x), - atan2(first.y, first.x), - internalProgress - ) - if (angle < tau / 8 || angle >= tau * 7 / 8) { - second.set(1F, tan(angle)) - } else if (angle < tau * 3 / 8) { - second.set(1 / tan(angle), 1F) - } else if (angle < tau * 5 / 8) { - second.set(-1F, -tan(angle)) - } else { - second.set(-1 / tan(angle), -1F) - } - } + val corners = listOf( + Vector2f(0F, -1F), + Vector2f(1F, -1F), + Vector2f(1F, 0F), + Vector2f(1F, 1F), + Vector2f(0F, 1F), + Vector2f(-1F, 1F), + Vector2f(-1F, 0F), + Vector2f(-1F, -1F), + ) - fun ilerp(f: Float): Float = - ilerp(-1f, 1f, f) + for (i in (0 until 8)) { + if (progress < i / 8F) { + break + } + val second = corners[(i + 1) % 8] + val first = corners[i] + if (progress <= (i + 1) / 8F) { + val internalProgress = 1 - (progress - i / 8F) * 8F + val angle = lerpAngle( + atan2(second.y, second.x), + atan2(first.y, first.x), + internalProgress + ) + if (angle < tau / 8 || angle >= tau * 7 / 8) { + second.set(1F, tan(angle)) + } else if (angle < tau * 3 / 8) { + second.set(1 / tan(angle), 1F) + } else if (angle < tau * 5 / 8) { + second.set(-1F, -tan(angle)) + } else { + second.set(-1 / tan(angle), -1F) + } + } - bufferBuilder - .vertex(matrix, second.x, second.y, 0F) - .texture(lerp(u1, u2, ilerp(second.x)), lerp(v1, v2, ilerp(second.y))) - .color(-1) - .next() - bufferBuilder - .vertex(matrix, first.x, first.y, 0F) - .texture(lerp(u1, u2, ilerp(first.x)), lerp(v1, v2, ilerp(first.y))) - .color(-1) - .next() - bufferBuilder - .vertex(matrix, 0F, 0F, 0F) - .texture(lerp(u1, u2, ilerp(0F)), lerp(v1, v2, ilerp(0F))) - .color(-1) - .next() - } - BufferRenderer.drawWithGlobalProgram(bufferBuilder.end()) - RenderSystem.disableBlend() - } + fun ilerp(f: Float): Float = + ilerp(-1f, 1f, f) + bufferBuilder + .vertex(matrix, second.x, second.y, 0F) + .texture(lerp(u1, u2, ilerp(second.x)), lerp(v1, v2, ilerp(second.y))) + .color(-1) + .next() + bufferBuilder + .vertex(matrix, first.x, first.y, 0F) + .texture(lerp(u1, u2, ilerp(first.x)), lerp(v1, v2, ilerp(first.y))) + .color(-1) + .next() + bufferBuilder + .vertex(matrix, 0F, 0F, 0F) + .texture(lerp(u1, u2, ilerp(0F)), lerp(v1, v2, ilerp(0F))) + .color(-1) + .next() + } + } + RenderSystem.disableBlend() + } } diff --git a/src/main/kotlin/util/render/RenderInWorldContext.kt b/src/main/kotlin/util/render/RenderInWorldContext.kt index b61b9aa..bb58200 100644 --- a/src/main/kotlin/util/render/RenderInWorldContext.kt +++ b/src/main/kotlin/util/render/RenderInWorldContext.kt @@ -1,5 +1,3 @@ - - package moe.nea.firmament.util.render import com.mojang.blaze3d.systems.RenderSystem @@ -8,14 +6,12 @@ import java.lang.Math.pow import org.joml.Matrix4f import org.joml.Vector3f import net.minecraft.client.gl.VertexBuffer -import net.minecraft.client.render.BufferBuilder -import net.minecraft.client.render.BufferRenderer import net.minecraft.client.render.Camera -import net.minecraft.client.render.GameRenderer import net.minecraft.client.render.RenderLayer import net.minecraft.client.render.RenderPhase import net.minecraft.client.render.RenderTickCounter import net.minecraft.client.render.Tessellator +import net.minecraft.client.render.VertexConsumer import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.render.VertexFormat import net.minecraft.client.render.VertexFormats @@ -31,273 +27,287 @@ import moe.nea.firmament.util.MC @RenderContextDSL class RenderInWorldContext private constructor( - private val tesselator: Tessellator, - val matrixStack: MatrixStack, - private val camera: Camera, - private val tickCounter: RenderTickCounter, - val vertexConsumers: VertexConsumerProvider.Immediate, + private val tesselator: Tessellator, + val matrixStack: MatrixStack, + private val camera: Camera, + private val tickCounter: RenderTickCounter, + val vertexConsumers: VertexConsumerProvider.Immediate, ) { - object RenderLayers { - val TRANSLUCENT_TRIS = RenderLayer.of("firmament_translucent_tris", - VertexFormats.POSITION_COLOR, - VertexFormat.DrawMode.TRIANGLES, - RenderLayer.DEFAULT_BUFFER_SIZE, - false, true, - RenderLayer.MultiPhaseParameters.builder() - .depthTest(RenderPhase.ALWAYS_DEPTH_TEST) - .transparency(RenderPhase.TRANSLUCENT_TRANSPARENCY) - .program(RenderPhase.COLOR_PROGRAM) - .build(false)) - val LINES = RenderLayer.of("firmament_rendertype_lines", - VertexFormats.LINES, - VertexFormat.DrawMode.LINES, - RenderLayer.DEFAULT_BUFFER_SIZE, - false, false, // do we need translucent? i dont think so - RenderLayer.MultiPhaseParameters.builder() - .depthTest(RenderPhase.ALWAYS_DEPTH_TEST) - .program(FirmamentShaders.LINES) - .build(false) - ) - } - - fun color(color: me.shedaniel.math.Color) { - color(color.red / 255F, color.green / 255f, color.blue / 255f, color.alpha / 255f) - } - - fun color(red: Float, green: Float, blue: Float, alpha: Float) { - RenderSystem.setShaderColor(red, green, blue, alpha) - } - - fun block(blockPos: BlockPos) { - matrixStack.push() - matrixStack.translate(blockPos.x.toFloat(), blockPos.y.toFloat(), blockPos.z.toFloat()) - buildCube(matrixStack.peek().positionMatrix, tesselator) - matrixStack.pop() - } - - enum class VerticalAlign { - TOP, BOTTOM, CENTER; - - fun align(index: Int, count: Int): Float { - return when (this) { - CENTER -> (index - count / 2F) * (1 + MC.font.fontHeight.toFloat()) - BOTTOM -> (index - count) * (1 + MC.font.fontHeight.toFloat()) - TOP -> (index) * (1 + MC.font.fontHeight.toFloat()) - } - } - } - - fun waypoint(position: BlockPos, vararg label: Text) { - text( - position.toCenterPos(), - *label, - Text.literal("§e${FirmFormatters.formatDistance(MC.player?.pos?.distanceTo(position.toCenterPos()) ?: 42069.0)}"), - background = 0xAA202020.toInt() - ) - } - - fun withFacingThePlayer(position: Vec3d, block: FacingThePlayerContext.() -> Unit) { - matrixStack.push() - matrixStack.translate(position.x, position.y, position.z) - val actualCameraDistance = position.distanceTo(camera.pos) - val distanceToMoveTowardsCamera = if (actualCameraDistance < 10) 0.0 else -(actualCameraDistance - 10.0) - val vec = position.subtract(camera.pos).multiply(distanceToMoveTowardsCamera / actualCameraDistance) - matrixStack.translate(vec.x, vec.y, vec.z) - matrixStack.multiply(camera.rotation) - matrixStack.scale(0.025F, -0.025F, 1F) - - FacingThePlayerContext(this).run(block) - - matrixStack.pop() - vertexConsumers.drawCurrentLayer() - } - - fun sprite(position: Vec3d, sprite: Sprite, width: Int, height: Int) { - texture( - position, sprite.atlasId, width, height, sprite.minU, sprite.minV, sprite.maxU, sprite.maxV - ) - } - - fun texture( - position: Vec3d, texture: Identifier, width: Int, height: Int, - u1: Float, v1: Float, - u2: Float, v2: Float, - ) { - withFacingThePlayer(position) { - texture(texture, width, height, u1, v1, u2, v2) - } - } - - fun text(position: Vec3d, vararg texts: Text, verticalAlign: VerticalAlign = VerticalAlign.CENTER, background: Int = 0x70808080) { - withFacingThePlayer(position) { - text(*texts, verticalAlign = verticalAlign, background = background) - } - } - - fun tinyBlock(vec3d: Vec3d, size: Float) { - RenderSystem.setShader(GameRenderer::getPositionColorProgram) - matrixStack.push() - matrixStack.translate(vec3d.x, vec3d.y, vec3d.z) - matrixStack.scale(size, size, size) - matrixStack.translate(-.5, -.5, -.5) - buildCube(matrixStack.peek().positionMatrix, tesselator) - matrixStack.pop() - } - - fun wireframeCube(blockPos: BlockPos, lineWidth: Float = 10F) { - RenderSystem.setShader(GameRenderer::getRenderTypeLinesProgram) - matrixStack.push() - RenderSystem.lineWidth(lineWidth / pow(camera.pos.squaredDistanceTo(blockPos.toCenterPos()), 0.25).toFloat()) - matrixStack.translate(blockPos.x.toFloat(), blockPos.y.toFloat(), blockPos.z.toFloat()) - buildWireFrameCube(matrixStack.peek(), tesselator) - matrixStack.pop() - } - - fun line(vararg points: Vec3d, lineWidth: Float = 10F) { - line(points.toList(), lineWidth) - } - - fun tracer(toWhere: Vec3d, lineWidth: Float = 3f) { - val cameraForward = Vector3f(0f, 0f, -1f).rotate(camera.rotation) - line(camera.pos.add(Vec3d(cameraForward)), toWhere, lineWidth = lineWidth) - } - - fun line(points: List<Vec3d>, lineWidth: Float = 10F) { - RenderSystem.lineWidth(lineWidth) - val buffer = tesselator.begin(VertexFormat.DrawMode.LINES, VertexFormats.LINES) - - val matrix = matrixStack.peek() - var lastNormal: Vector3f? = null - points.zipWithNext().forEach { (a, b) -> - val normal = Vector3f(b.x.toFloat(), b.y.toFloat(), b.z.toFloat()) - .sub(a.x.toFloat(), a.y.toFloat(), a.z.toFloat()) - .normalize() - val lastNormal0 = lastNormal ?: normal - lastNormal = normal - buffer.vertex(matrix.positionMatrix, a.x.toFloat(), a.y.toFloat(), a.z.toFloat()) - .color(-1) - .normal(matrix, lastNormal0.x, lastNormal0.y, lastNormal0.z) - .next() - buffer.vertex(matrix.positionMatrix, b.x.toFloat(), b.y.toFloat(), b.z.toFloat()) - .color(-1) - .normal(matrix, normal.x, normal.y, normal.z) - .next() - } - - RenderLayers.LINES.draw(buffer.end()) - } - - companion object { - private fun doLine( - matrix: MatrixStack.Entry, - buf: BufferBuilder, - i: Float, - j: Float, - k: Float, - x: Float, - y: Float, - z: Float - ) { - val normal = Vector3f(x, y, z) - .sub(i, j, k) - .normalize() - buf.vertex(matrix.positionMatrix, i, j, k) - .normal(matrix, normal.x, normal.y, normal.z) - .color(-1) - .next() - buf.vertex(matrix.positionMatrix, x, y, z) - .normal(matrix, normal.x, normal.y, normal.z) - .color(-1) - .next() - } - - - private fun buildWireFrameCube(matrix: MatrixStack.Entry, tessellator: Tessellator) { - val buf = tessellator.begin(VertexFormat.DrawMode.LINES, VertexFormats.LINES) - - for (i in 0..1) { - for (j in 0..1) { - val i = i.toFloat() - val j = j.toFloat() - doLine(matrix, buf, 0F, i, j, 1F, i, j) - doLine(matrix, buf, i, 0F, j, i, 1F, j) - doLine(matrix, buf, i, j, 0F, i, j, 1F) - } - } - BufferRenderer.drawWithGlobalProgram(buf.end()) - } - - private fun buildCube(matrix: Matrix4f, tessellator: Tessellator) { - val buf = tessellator.begin(VertexFormat.DrawMode.TRIANGLES, VertexFormats.POSITION_COLOR) - buf.vertex(matrix, 0.0F, 0.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 0.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 1.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 1.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 0.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 1.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 0.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 0.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 0.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 1.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 0.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 0.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 0.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 1.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 1.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 0.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 0.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 0.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 1.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 0.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 0.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 1.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 0.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 1.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 0.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 1.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 0.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 1.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 1.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 1.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 1.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 1.0F, 0.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 1.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 1.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 0.0F, 1.0F, 1.0F).color(-1).next() - buf.vertex(matrix, 1.0F, 0.0F, 1.0F).color(-1).next() - RenderLayers.TRANSLUCENT_TRIS.draw(buf.end()) - } - - - fun renderInWorld(event: WorldRenderLastEvent, block: RenderInWorldContext. () -> Unit) { - RenderSystem.disableDepthTest() - RenderSystem.enableBlend() - RenderSystem.defaultBlendFunc() - RenderSystem.disableCull() - - event.matrices.push() - event.matrices.translate(-event.camera.pos.x, -event.camera.pos.y, -event.camera.pos.z) - - val ctx = RenderInWorldContext( - RenderSystem.renderThreadTesselator(), - event.matrices, - event.camera, - event.tickCounter, - event.vertexConsumers - ) - - block(ctx) - - event.matrices.pop() - - RenderSystem.setShaderColor(1F, 1F, 1F, 1F) - VertexBuffer.unbind() - RenderSystem.enableDepthTest() - RenderSystem.enableCull() - RenderSystem.disableBlend() - } - } + object RenderLayers { + val TRANSLUCENT_TRIS = RenderLayer.of("firmament_translucent_tris", + VertexFormats.POSITION_COLOR, + VertexFormat.DrawMode.TRIANGLES, + RenderLayer.CUTOUT_BUFFER_SIZE, + false, true, + RenderLayer.MultiPhaseParameters.builder() + .depthTest(RenderPhase.ALWAYS_DEPTH_TEST) + .transparency(RenderPhase.TRANSLUCENT_TRANSPARENCY) + .program(RenderPhase.POSITION_COLOR_PROGRAM) + .build(false)) + val LINES = RenderLayer.of("firmament_rendertype_lines", + VertexFormats.LINES, + VertexFormat.DrawMode.LINES, + RenderLayer.CUTOUT_BUFFER_SIZE, + false, false, // do we need translucent? i dont think so + RenderLayer.MultiPhaseParameters.builder() + .depthTest(RenderPhase.ALWAYS_DEPTH_TEST) + .program(FirmamentShaders.LINES) + .build(false) + ) + val COLORED_QUADS = RenderLayer.of( + "firmament_quads", + VertexFormats.POSITION_COLOR, + VertexFormat.DrawMode.QUADS, + RenderLayer.CUTOUT_BUFFER_SIZE, + false, true, + RenderLayer.MultiPhaseParameters.builder() + .depthTest(RenderPhase.ALWAYS_DEPTH_TEST) + .program(RenderPhase.POSITION_COLOR_PROGRAM) + .transparency(RenderPhase.TRANSLUCENT_TRANSPARENCY) + .build(false) + ) + } + + @Deprecated("stateful color management is no longer a thing") + fun color(color: me.shedaniel.math.Color) { + color(color.red / 255F, color.green / 255f, color.blue / 255f, color.alpha / 255f) + } + + @Deprecated("stateful color management is no longer a thing") + fun color(red: Float, green: Float, blue: Float, alpha: Float) { + RenderSystem.setShaderColor(red, green, blue, alpha) + } + + fun block(blockPos: BlockPos, color: Int) { + matrixStack.push() + matrixStack.translate(blockPos.x.toFloat(), blockPos.y.toFloat(), blockPos.z.toFloat()) + buildCube(matrixStack.peek().positionMatrix, vertexConsumers.getBuffer(RenderLayers.COLORED_QUADS), color) + matrixStack.pop() + } + + enum class VerticalAlign { + TOP, BOTTOM, CENTER; + + fun align(index: Int, count: Int): Float { + return when (this) { + CENTER -> (index - count / 2F) * (1 + MC.font.fontHeight.toFloat()) + BOTTOM -> (index - count) * (1 + MC.font.fontHeight.toFloat()) + TOP -> (index) * (1 + MC.font.fontHeight.toFloat()) + } + } + } + + fun waypoint(position: BlockPos, vararg label: Text) { + text( + position.toCenterPos(), + *label, + Text.literal("§e${FirmFormatters.formatDistance(MC.player?.pos?.distanceTo(position.toCenterPos()) ?: 42069.0)}"), + background = 0xAA202020.toInt() + ) + } + + fun withFacingThePlayer(position: Vec3d, block: FacingThePlayerContext.() -> Unit) { + matrixStack.push() + matrixStack.translate(position.x, position.y, position.z) + val actualCameraDistance = position.distanceTo(camera.pos) + val distanceToMoveTowardsCamera = if (actualCameraDistance < 10) 0.0 else -(actualCameraDistance - 10.0) + val vec = position.subtract(camera.pos).multiply(distanceToMoveTowardsCamera / actualCameraDistance) + matrixStack.translate(vec.x, vec.y, vec.z) + matrixStack.multiply(camera.rotation) + matrixStack.scale(0.025F, -0.025F, 1F) + + FacingThePlayerContext(this).run(block) + + matrixStack.pop() + vertexConsumers.drawCurrentLayer() + } + + fun sprite(position: Vec3d, sprite: Sprite, width: Int, height: Int) { + texture( + position, sprite.atlasId, width, height, sprite.minU, sprite.minV, sprite.maxU, sprite.maxV + ) + } + + fun texture( + position: Vec3d, texture: Identifier, width: Int, height: Int, + u1: Float, v1: Float, + u2: Float, v2: Float, + ) { + withFacingThePlayer(position) { + texture(texture, width, height, u1, v1, u2, v2) + } + } + + fun text( + position: Vec3d, + vararg texts: Text, + verticalAlign: VerticalAlign = VerticalAlign.CENTER, + background: Int = 0x70808080 + ) { + withFacingThePlayer(position) { + text(*texts, verticalAlign = verticalAlign, background = background) + } + } + + fun tinyBlock(vec3d: Vec3d, size: Float, color: Int) { + matrixStack.push() + matrixStack.translate(vec3d.x, vec3d.y, vec3d.z) + matrixStack.scale(size, size, size) + matrixStack.translate(-.5, -.5, -.5) + buildCube(matrixStack.peek().positionMatrix, vertexConsumers.getBuffer(RenderLayers.COLORED_QUADS), color) + matrixStack.pop() + vertexConsumers.draw() + } + + fun wireframeCube(blockPos: BlockPos, lineWidth: Float = 10F) { + val buf = vertexConsumers.getBuffer(RenderLayer.LINES) + matrixStack.push() + // TODO: this does not render through blocks (or water layers) anymore + RenderSystem.lineWidth(lineWidth / pow(camera.pos.squaredDistanceTo(blockPos.toCenterPos()), 0.25).toFloat()) + matrixStack.translate(blockPos.x.toFloat(), blockPos.y.toFloat(), blockPos.z.toFloat()) + buildWireFrameCube(matrixStack.peek(), buf) + matrixStack.pop() + vertexConsumers.draw() + } + + fun line(vararg points: Vec3d, lineWidth: Float = 10F) { + line(points.toList(), lineWidth) + } + + fun tracer(toWhere: Vec3d, lineWidth: Float = 3f) { + val cameraForward = Vector3f(0f, 0f, -1f).rotate(camera.rotation) + line(camera.pos.add(Vec3d(cameraForward)), toWhere, lineWidth = lineWidth) + } + + fun line(points: List<Vec3d>, lineWidth: Float = 10F) { + RenderSystem.lineWidth(lineWidth) + // TODO: replace with renderlayers + val buffer = tesselator.begin(VertexFormat.DrawMode.LINES, VertexFormats.LINES) + + val matrix = matrixStack.peek() + var lastNormal: Vector3f? = null + points.zipWithNext().forEach { (a, b) -> + val normal = Vector3f(b.x.toFloat(), b.y.toFloat(), b.z.toFloat()) + .sub(a.x.toFloat(), a.y.toFloat(), a.z.toFloat()) + .normalize() + val lastNormal0 = lastNormal ?: normal + lastNormal = normal + buffer.vertex(matrix.positionMatrix, a.x.toFloat(), a.y.toFloat(), a.z.toFloat()) + .color(-1) + .normal(matrix, lastNormal0.x, lastNormal0.y, lastNormal0.z) + .next() + buffer.vertex(matrix.positionMatrix, b.x.toFloat(), b.y.toFloat(), b.z.toFloat()) + .color(-1) + .normal(matrix, normal.x, normal.y, normal.z) + .next() + } + + RenderLayers.LINES.draw(buffer.end()) + } + // TODO: put the favourite icons in front of items again + + companion object { + private fun doLine( + matrix: MatrixStack.Entry, + buf: VertexConsumer, + i: Float, + j: Float, + k: Float, + x: Float, + y: Float, + z: Float + ) { + val normal = Vector3f(x, y, z) + .sub(i, j, k) + .normalize() + buf.vertex(matrix.positionMatrix, i, j, k) + .normal(matrix, normal.x, normal.y, normal.z) + .color(-1) + .next() + buf.vertex(matrix.positionMatrix, x, y, z) + .normal(matrix, normal.x, normal.y, normal.z) + .color(-1) + .next() + } + + + private fun buildWireFrameCube(matrix: MatrixStack.Entry, buf: VertexConsumer) { + for (i in 0..1) { + for (j in 0..1) { + val i = i.toFloat() + val j = j.toFloat() + doLine(matrix, buf, 0F, i, j, 1F, i, j) + doLine(matrix, buf, i, 0F, j, i, 1F, j) + doLine(matrix, buf, i, j, 0F, i, j, 1F) + } + } + } + + private fun buildCube(matrix: Matrix4f, buf: VertexConsumer, color: Int) { + // Y- + buf.vertex(matrix, 0F, 0F, 0F).color(color) + buf.vertex(matrix, 0F, 0F, 1F).color(color) + buf.vertex(matrix, 1F, 0F, 1F).color(color) + buf.vertex(matrix, 1F, 0F, 0F).color(color) + // Y+ + buf.vertex(matrix, 0F, 1F, 0F).color(color) + buf.vertex(matrix, 1F, 1F, 0F).color(color) + buf.vertex(matrix, 1F, 1F, 1F).color(color) + buf.vertex(matrix, 0F, 1F, 1F).color(color) + // X- + buf.vertex(matrix, 0F, 0F, 0F).color(color) + buf.vertex(matrix, 0F, 0F, 1F).color(color) + buf.vertex(matrix, 0F, 1F, 1F).color(color) + buf.vertex(matrix, 0F, 1F, 0F).color(color) + // X+ + buf.vertex(matrix, 1F, 0F, 0F).color(color) + buf.vertex(matrix, 1F, 1F, 0F).color(color) + buf.vertex(matrix, 1F, 1F, 1F).color(color) + buf.vertex(matrix, 1F, 0F, 1F).color(color) + // Z- + buf.vertex(matrix, 0F, 0F, 0F).color(color) + buf.vertex(matrix, 1F, 0F, 0F).color(color) + buf.vertex(matrix, 1F, 1F, 0F).color(color) + buf.vertex(matrix, 0F, 1F, 0F).color(color) + // Z+ + buf.vertex(matrix, 0F, 0F, 1F).color(color) + buf.vertex(matrix, 0F, 1F, 1F).color(color) + buf.vertex(matrix, 1F, 1F, 1F).color(color) + buf.vertex(matrix, 1F, 0F, 1F).color(color) + } + + + fun renderInWorld(event: WorldRenderLastEvent, block: RenderInWorldContext. () -> Unit) { + // TODO: there should be *no more global state*. the only thing we should be doing is render layers. that includes settings like culling, blending, shader color, and depth testing + // For now i will let these functions remain, but this needs to go before i do a full (non-beta) release + RenderSystem.disableDepthTest() + RenderSystem.enableBlend() + RenderSystem.defaultBlendFunc() + RenderSystem.disableCull() + + event.matrices.push() + event.matrices.translate(-event.camera.pos.x, -event.camera.pos.y, -event.camera.pos.z) + + val ctx = RenderInWorldContext( + RenderSystem.renderThreadTesselator(), + event.matrices, + event.camera, + event.tickCounter, + event.vertexConsumers + ) + + block(ctx) + + event.matrices.pop() + event.vertexConsumers.draw() + RenderSystem.setShaderColor(1F, 1F, 1F, 1F) + VertexBuffer.unbind() + RenderSystem.enableDepthTest() + RenderSystem.enableCull() + RenderSystem.disableBlend() + } + } } |