diff options
| author | Linnea Gräf <nea@nea.moe> | 2024-11-03 01:24:24 +0100 | 
|---|---|---|
| committer | Linnea Gräf <nea@nea.moe> | 2024-11-09 01:01:18 +0100 | 
| commit | 22f0cc59a2d3bc7900764e3916c670075ff9d35e (patch) | |
| tree | b503ff607cf818a539cbbaa403f6851ef979e03d /src | |
| parent | 646843ba3b960ac48f9866b3640438d3cc1dafc4 (diff) | |
| download | Firmament-22f0cc59a2d3bc7900764e3916c670075ff9d35e.tar.gz Firmament-22f0cc59a2d3bc7900764e3916c670075ff9d35e.tar.bz2 Firmament-22f0cc59a2d3bc7900764e3916c670075ff9d35e.zip | |
1.21.3 WIP
Diffstat (limited to 'src')
102 files changed, 2932 insertions, 2733 deletions
| diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/EntityWidget.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/EntityWidget.kt index 9b7b190..b0efc98 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/EntityWidget.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/EntityWidget.kt @@ -4,32 +4,32 @@ import me.shedaniel.math.Dimension  import me.shedaniel.math.Point  import me.shedaniel.math.Rectangle  import me.shedaniel.rei.api.client.gui.widgets.WidgetWithBounds -import moe.nea.firmament.gui.entity.EntityRenderer  import net.minecraft.client.gui.DrawContext  import net.minecraft.client.gui.Element  import net.minecraft.entity.LivingEntity +import moe.nea.firmament.gui.entity.EntityRenderer  class EntityWidget(val entity: LivingEntity, val point: Point) : WidgetWithBounds() { -    override fun children(): List<Element> { -        return emptyList() -    } +	override fun children(): List<Element> { +		return emptyList() +	} -    var hasErrored = false +	var hasErrored = false -    override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) { -        try { -            if (!hasErrored) -	            EntityRenderer.renderEntity(entity, context, point.x, point.y, mouseX.toFloat(), mouseY.toFloat()) -        } catch (ex: Exception) { -            EntityRenderer.logger.error("Failed to render constructed entity: $entity", ex) -            hasErrored = true -        } -        if (hasErrored) { -            context.fill(point.x, point.y, point.x + 50, point.y + 80, 0xFFAA2222.toInt()) -        } -    } +	override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) { +		try { +			if (!hasErrored) +				EntityRenderer.renderEntity(entity, context, point.x, point.y, mouseX.toFloat(), mouseY.toFloat()) +		} catch (ex: Exception) { +			EntityRenderer.logger.error("Failed to render constructed entity: $entity", ex) +			hasErrored = true +		} +		if (hasErrored) { +			context.fill(point.x, point.y, point.x + 50, point.y + 80, 0xFFAA2222.toInt()) +		} +	} -    override fun getBounds(): Rectangle { -        return Rectangle(point, Dimension(50, 80)) -    } +	override fun getBounds(): Rectangle { +		return Rectangle(point, Dimension(50, 80)) +	}  } diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/FirmamentReiCommonPlugin.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/FirmamentReiCommonPlugin.kt new file mode 100644 index 0000000..98ac276 --- /dev/null +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/FirmamentReiCommonPlugin.kt @@ -0,0 +1,10 @@ +package moe.nea.firmament.compat.rei + +import me.shedaniel.rei.api.common.entry.type.EntryTypeRegistry +import me.shedaniel.rei.api.common.plugins.REICommonPlugin + +class FirmamentReiCommonPlugin : REICommonPlugin { +	override fun registerEntryTypes(registry: EntryTypeRegistry) { +		registry.register(FirmamentReiPlugin.SKYBLOCK_ITEM_TYPE_ID, SBItemEntryDefinition) +	} +} diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/FirmamentReiPlugin.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/FirmamentReiPlugin.kt index d95d2d1..f576eda 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/FirmamentReiPlugin.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/FirmamentReiPlugin.kt @@ -11,7 +11,6 @@ import me.shedaniel.rei.api.client.registry.screen.ScreenRegistry  import me.shedaniel.rei.api.client.registry.transfer.TransferHandler  import me.shedaniel.rei.api.client.registry.transfer.TransferHandlerRegistry  import me.shedaniel.rei.api.common.entry.EntryStack -import me.shedaniel.rei.api.common.entry.type.EntryTypeRegistry  import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes  import net.minecraft.client.gui.screen.Screen  import net.minecraft.client.gui.screen.ingame.GenericContainerScreen @@ -20,18 +19,17 @@ import net.minecraft.item.ItemStack  import net.minecraft.text.Text  import net.minecraft.util.ActionResult  import net.minecraft.util.Identifier -import moe.nea.firmament.events.HandledScreenPushREIEvent -import moe.nea.firmament.features.inventory.CraftingOverlay -import moe.nea.firmament.features.inventory.storageoverlay.StorageOverlayScreen  import moe.nea.firmament.compat.rei.recipes.SBCraftingRecipe  import moe.nea.firmament.compat.rei.recipes.SBEssenceUpgradeRecipe  import moe.nea.firmament.compat.rei.recipes.SBForgeRecipe  import moe.nea.firmament.compat.rei.recipes.SBKatRecipe  import moe.nea.firmament.compat.rei.recipes.SBMobDropRecipe +import moe.nea.firmament.events.HandledScreenPushREIEvent +import moe.nea.firmament.features.inventory.CraftingOverlay +import moe.nea.firmament.features.inventory.storageoverlay.StorageOverlayScreen  import moe.nea.firmament.repo.RepoManager  import moe.nea.firmament.repo.SBItemStack  import moe.nea.firmament.util.MC -import moe.nea.firmament.util.ScreenUtil  import moe.nea.firmament.util.SkyblockId  import moe.nea.firmament.util.guessRecipeId  import moe.nea.firmament.util.skyblockId @@ -74,9 +72,6 @@ class FirmamentReiPlugin : REIClientPlugin {  		})  	} -	override fun registerEntryTypes(registry: EntryTypeRegistry) { -		registry.register(SKYBLOCK_ITEM_TYPE_ID, SBItemEntryDefinition) -	}  	override fun registerCategories(registry: CategoryRegistry) {  		registry.add(SBCraftingRecipe.Category) diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/HoveredItemStackProvider.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/HoveredItemStackProvider.kt index 3d21b66..b917c3e 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/HoveredItemStackProvider.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/HoveredItemStackProvider.kt @@ -21,6 +21,7 @@ class ScreenRegistryHoveredItemStackProvider : HoveredItemStackProvider {  		return entryStack.value as? ItemStack ?: entryStack.cheatsAs().value  	}  } +  @AutoService(HoveredItemStackProvider::class)  @CompatLoader.RequireMod("roughlyenoughitems")  class OverlayHoveredItemStackProvider : HoveredItemStackProvider { diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/NEUItemEntryRenderer.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/NEUItemEntryRenderer.kt index a02742b..de173ff 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/NEUItemEntryRenderer.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/NEUItemEntryRenderer.kt @@ -25,163 +25,128 @@ import net.minecraft.client.render.LightmapTextureManager  import net.minecraft.client.render.OverlayTexture  import net.minecraft.client.render.VertexConsumerProvider  import net.minecraft.client.render.model.BakedModel -import net.minecraft.client.render.model.json.ModelTransformationMode  import net.minecraft.client.texture.SpriteAtlasTexture -import net.minecraft.item.Item -import net.minecraft.item.ItemStack -import net.minecraft.item.tooltip.TooltipType +import net.minecraft.item.ModelTransformationMode  import moe.nea.firmament.compat.rei.FirmamentReiPlugin.Companion.asItemEntry  import moe.nea.firmament.repo.SBItemStack +import moe.nea.firmament.util.MC +import moe.nea.firmament.util.mc.displayNameAccordingToNbt +import moe.nea.firmament.util.mc.loreAccordingToNbt  object NEUItemEntryRenderer : EntryRenderer<SBItemStack>, BatchedEntryRenderer<SBItemStack, BakedModel> { -    override fun render( -        entry: EntryStack<SBItemStack>, -        context: DrawContext, -        bounds: Rectangle, -        mouseX: Int, -        mouseY: Int, -        delta: Float -    ) { -        entry.asItemEntry().render(context, bounds, mouseX, mouseY, delta) -    } - -    val minecraft = MinecraftClient.getInstance() - -    override fun getTooltip(entry: EntryStack<SBItemStack>, tooltipContext: TooltipContext): Tooltip? { -        val stack = entry.value.asImmutableItemStack() -        val lore = stack.getTooltip( -            Item.TooltipContext.DEFAULT, -            null, -            TooltipType.BASIC -        ) -        return Tooltip.create(lore) -    } - -    override fun getExtraData(entry: EntryStack<SBItemStack>): BakedModel { -        return minecraft.itemRenderer.getModel(entry.asItemEntry().value, minecraft.world, minecraft.player, 0) -    } - -    override fun getBatchIdentifier(entry: EntryStack<SBItemStack>?, bounds: Rectangle?, extraData: BakedModel): Int { -        return 1738923 + if (extraData.isSideLit) 1 else 0 -    } - -    override fun startBatch( -        entry: EntryStack<SBItemStack>, -        model: BakedModel, -        graphics: DrawContext, -        delta: Float -    ) { -        val modelViewStack = RenderSystem.getModelViewStack() -        modelViewStack.pushMatrix() -        modelViewStack.scale(20.0f, 20.0f, 1.0f) -        RenderSystem.applyModelViewMatrix() -        setupGL(model) -    } - -    fun setupGL(model: BakedModel) { -        minecraft.textureManager.getTexture(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE) -            .setFilter(false, false) -        RenderSystem.setShaderTexture(0, SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE) -        RenderSystem.enableBlend() -        RenderSystem.blendFunc(SrcFactor.SRC_ALPHA, DstFactor.ONE_MINUS_SRC_ALPHA) -        RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f) -        val sideLit = model.isSideLit -        if (!sideLit) { -            DiffuseLighting.disableGuiDepthLighting() -        } -    } - -    override fun renderBase( -        entry: EntryStack<SBItemStack>, -        model: BakedModel, -        graphics: DrawContext, -        immediate: VertexConsumerProvider.Immediate, -        bounds: Rectangle, -        mouseX: Int, -        mouseY: Int, -        delta: Float -    ) { -        if (entry.isEmpty) return -        val value = entry.asItemEntry().value -        graphics.matrices.push() -        graphics.matrices.translate(bounds.centerX.toFloat() / 20.0f, bounds.centerY.toFloat() / 20.0f, 0.0f) -        graphics.matrices.scale( -            bounds.getWidth().toFloat() / 20.0f, -            -(bounds.getWidth() + bounds.getHeight()).toFloat() / 2.0f / 20.0f, -            1.0f -        ) -        minecraft -            .itemRenderer -            .renderItem( -                value, -                ModelTransformationMode.GUI, -                false, -                graphics.matrices, -                immediate, -                LightmapTextureManager.MAX_LIGHT_COORDINATE, -                OverlayTexture.DEFAULT_UV, -                model -            ) -        graphics.matrices.pop() - -    } - -    override fun afterBase( -        entry: EntryStack<SBItemStack>, -        model: BakedModel, -        graphics: DrawContext, -        delta: Float -    ) { -        RenderSystem.getModelViewStack().popMatrix() -        RenderSystem.applyModelViewMatrix() -        this.endGL(model) -    } - -    fun endGL(model: BakedModel) { -        RenderSystem.enableDepthTest() -        val sideLit = model.isSideLit -        if (!sideLit) { -            DiffuseLighting.enableGuiDepthLighting() -        } -    } - -    override fun renderOverlay( -        entry: EntryStack<SBItemStack>, -        extraData: BakedModel, -        graphics: DrawContext, -        immediate: VertexConsumerProvider.Immediate, -        bounds: Rectangle, -        mouseX: Int, -        mouseY: Int, -        delta: Float -    ) { -        val modelViewStack = RenderSystem.getModelViewStack() -        modelViewStack.pushMatrix() -        modelViewStack.mul(graphics.matrices.peek().positionMatrix) -        modelViewStack.translate(bounds.x.toFloat(), bounds.y.toFloat(), 0.0f) -        modelViewStack.scale( -            bounds.width.toFloat() / 16.0f, -            -(bounds.getWidth() + bounds.getHeight()).toFloat() / 2.0f / 16.0f, -            1.0f -        ) -        RenderSystem.applyModelViewMatrix() -        renderOverlay(DrawContext(minecraft, graphics.vertexConsumers), entry.asItemEntry()) -        modelViewStack.popMatrix() -        RenderSystem.applyModelViewMatrix() -    } - -    fun renderOverlay(graphics: DrawContext, entry: EntryStack<ItemStack>) { -        if (!entry.isEmpty) { -            graphics.drawItemInSlot(MinecraftClient.getInstance().textRenderer, entry.value, 0, 0, null) -        } -    } - -    override fun endBatch( -        entry: EntryStack<SBItemStack>?, -        extraData: BakedModel?, -        graphics: DrawContext?, -        delta: Float -    ) { -    } +	override fun render( +		entry: EntryStack<SBItemStack>, +		context: DrawContext, +		bounds: Rectangle, +		mouseX: Int, +		mouseY: Int, +		delta: Float +	) { +		entry.asItemEntry().render(context, bounds, mouseX, mouseY, delta) +	} + +	val minecraft = MinecraftClient.getInstance() + +	override fun getTooltip(entry: EntryStack<SBItemStack>, tooltipContext: TooltipContext): Tooltip? { +		val stack = entry.value.asImmutableItemStack() + +		val lore = mutableListOf(stack.displayNameAccordingToNbt) +		lore.addAll(stack.loreAccordingToNbt) + +		// TODO: tags aren't sent as early now so some tooltip components that use tags will crash the game +//		stack.getTooltip( +//			Item.TooltipContext.create( +//				tooltipContext.vanillaContext().registryLookup +//					?: MC.defaultRegistries +//			), +//			MC.player, +//			TooltipType.BASIC +//		) +		return Tooltip.create(lore) +	} + +	override fun getExtraData(entry: EntryStack<SBItemStack>): BakedModel { +		return MC.itemRenderer.getModel(entry.asItemEntry().value, +		                                MC.world, +		                                MC.player, 0) + +	} + +	override fun getBatchIdentifier(entry: EntryStack<SBItemStack>, bounds: Rectangle?, extraData: BakedModel): Int { +		return 1738923 + if (extraData.isSideLit) 1 else 0 +	} + + +	override fun startBatch(entryStack: EntryStack<SBItemStack>, e: BakedModel, drawContext: DrawContext, v: Float) { +		MC.textureManager.getTexture(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE) +			.setFilter(false, false) +		RenderSystem.setShaderTexture(0, SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE) +		RenderSystem.enableBlend() +		RenderSystem.blendFunc(SrcFactor.SRC_ALPHA, DstFactor.ONE_MINUS_SRC_ALPHA) +		RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f) +		if (!e.isSideLit) { +			DiffuseLighting.disableGuiDepthLighting() +		} +	} + +	override fun renderBase( +		entryStack: EntryStack<SBItemStack>, +		model: BakedModel, +		drawContext: DrawContext, +		immediate: VertexConsumerProvider.Immediate, +		bounds: Rectangle, +		i: Int, +		i1: Int, +		v: Float +	) { +		if (entryStack.isEmpty) return +		drawContext.matrices.push() +		drawContext.matrices.translate(bounds.centerX.toDouble(), bounds.centerY.toDouble(), 0.0) +		// TODO: check the scaling here again +		drawContext.matrices.scale( +			bounds.width.toFloat(), +			(bounds.height + bounds.height) / -2F, +			(bounds.width + bounds.height) / 2f) +		MC.itemRenderer.renderItem( +			entryStack.value.asImmutableItemStack(), +			ModelTransformationMode.GUI, +			false, drawContext.matrices, +			immediate, LightmapTextureManager.MAX_LIGHT_COORDINATE, +			OverlayTexture.DEFAULT_UV, +			model +		) +		drawContext.matrices.pop() +	} + +	override fun afterBase(entryStack: EntryStack<SBItemStack>?, e: BakedModel, drawContext: DrawContext?, v: Float) { +		RenderSystem.enableDepthTest() +		if (!e.isSideLit) +			DiffuseLighting.enableGuiDepthLighting() +	} + +	override fun renderOverlay( +		entryStack: EntryStack<SBItemStack>, +		e: BakedModel, +		drawContext: DrawContext, +		immediate: VertexConsumerProvider.Immediate, +		bounds: Rectangle, +		i: Int, +		i1: Int, +		v: Float +	) { +		if (entryStack.isEmpty) return +		val modelViewStack = RenderSystem.getModelViewStack() +		modelViewStack.pushMatrix() +		modelViewStack.mul(drawContext.matrices.peek().positionMatrix) +		modelViewStack.translate(bounds.x.toFloat(), bounds.y.toFloat(), 0F) +		modelViewStack.scale(bounds.width / 16.0f, +		                     (bounds.width + bounds.height) / 2.0f / 16.0f, +		                     1.0f) // TODO: weird scale again +		drawContext.drawStackOverlay(MC.font, entryStack.value.asImmutableItemStack(), 0, 0, null) +		modelViewStack.popMatrix() +	} + +	override fun endBatch(entryStack: EntryStack<SBItemStack>?, e: BakedModel?, drawContext: DrawContext?, v: Float) { +	}  } diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/NEUItemEntrySerializer.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/NEUItemEntrySerializer.kt index 6a03a48..724d193 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/NEUItemEntrySerializer.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/NEUItemEntrySerializer.kt @@ -1,30 +1,17 @@ - -  package moe.nea.firmament.compat.rei +import com.mojang.serialization.Codec  import me.shedaniel.rei.api.common.entry.EntrySerializer -import me.shedaniel.rei.api.common.entry.EntryStack -import net.minecraft.nbt.NbtCompound +import net.minecraft.network.RegistryByteBuf +import net.minecraft.network.codec.PacketCodec  import moe.nea.firmament.repo.SBItemStack -import moe.nea.firmament.util.SkyblockId  object NEUItemEntrySerializer : EntrySerializer<SBItemStack> { -    const val SKYBLOCK_ID_ENTRY = "SKYBLOCK_ID" -    const val SKYBLOCK_ITEM_COUNT = "SKYBLOCK_ITEM_COUNT" - -    override fun supportSaving(): Boolean = true -    override fun supportReading(): Boolean = true - -    override fun read(tag: NbtCompound): SBItemStack { -        val id = SkyblockId(tag.getString(SKYBLOCK_ID_ENTRY)) -        val count = if (tag.contains(SKYBLOCK_ITEM_COUNT)) tag.getInt(SKYBLOCK_ITEM_COUNT) else 1 -        return SBItemStack(id, count) -    } +	override fun codec(): Codec<SBItemStack> { +		return SBItemStack.CODEC +	} -    override fun save(entry: EntryStack<SBItemStack>, value: SBItemStack): NbtCompound { -        return NbtCompound().apply { -            putString(SKYBLOCK_ID_ENTRY, value.skyblockId.neuItem) -            putInt(SKYBLOCK_ITEM_COUNT, value.getStackSize()) -        } -    } +	override fun streamCodec(): PacketCodec<RegistryByteBuf, SBItemStack> { +		return SBItemStack.PACKET_CODEC.cast() +	}  } diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/math.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/math.kt index 7db36f2..f4808c7 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/math.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/math.kt @@ -1,10 +1,8 @@ - -  package moe.nea.firmament.compat.rei  import me.shedaniel.math.Point  operator fun Point.plus(other: Point): Point = Point( -    this.x + other.x, -    this.y + other.y, +	this.x + other.x, +	this.y + other.y,  ) diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBCraftingRecipe.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBCraftingRecipe.kt index ed18c6e..fd04abc 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBCraftingRecipe.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBCraftingRecipe.kt @@ -1,5 +1,3 @@ - -  package moe.nea.firmament.compat.rei.recipes  import io.github.moulberry.repo.data.NEUCraftingRecipe @@ -11,6 +9,8 @@ import me.shedaniel.rei.api.client.gui.widgets.Widget  import me.shedaniel.rei.api.client.gui.widgets.Widgets  import me.shedaniel.rei.api.client.registry.display.DisplayCategory  import me.shedaniel.rei.api.common.category.CategoryIdentifier +import me.shedaniel.rei.api.common.display.Display +import me.shedaniel.rei.api.common.display.DisplaySerializer  import me.shedaniel.rei.api.common.util.EntryStacks  import net.minecraft.block.Blocks  import net.minecraft.text.Text @@ -18,38 +18,38 @@ import moe.nea.firmament.Firmament  import moe.nea.firmament.compat.rei.SBItemEntryDefinition  class SBCraftingRecipe(override val neuRecipe: NEUCraftingRecipe) : SBRecipe() { -    override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.catIdentifier - -    object Category : DisplayCategory<SBCraftingRecipe> { -        val catIdentifier = CategoryIdentifier.of<SBCraftingRecipe>(Firmament.MOD_ID, "crafing_recipe") -        override fun getCategoryIdentifier(): CategoryIdentifier<out SBCraftingRecipe> = catIdentifier - -        override fun getTitle(): Text = Text.literal("SkyBlock Crafting") - -        override fun getIcon(): Renderer = EntryStacks.of(Blocks.CRAFTING_TABLE) -        override fun setupDisplay(display: SBCraftingRecipe, bounds: Rectangle): List<Widget> { -            val point = Point(bounds.centerX - 58, bounds.centerY - 27) -            return buildList { -                add(Widgets.createRecipeBase(bounds)) -                add(Widgets.createArrow(Point(point.x + 60, point.y + 18))) -                add(Widgets.createResultSlotBackground(Point(point.x + 95, point.y + 19))) -                for (i in 0 until 3) { -                    for (j in 0 until 3) { -                        val slot = Widgets.createSlot(Point(point.x + 1 + i * 18, point.y + 1 + j * 18)).markInput() -                        add(slot) -                        val item = display.neuRecipe.inputs[i + j * 3] -                        if (item == NEUIngredient.SENTINEL_EMPTY) continue -                        slot.entry(SBItemEntryDefinition.getEntry(item)) // TODO: make use of stackable item entries -                    } -                } -                add( -                    Widgets.createSlot(Point(point.x + 95, point.y + 19)) -                        .entry(SBItemEntryDefinition.getEntry(display.neuRecipe.output)) -                        .disableBackground().markOutput() -                ) -            } -        } - -    } +	override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.catIdentifier + +	object Category : DisplayCategory<SBCraftingRecipe> { +		val catIdentifier = CategoryIdentifier.of<SBCraftingRecipe>(Firmament.MOD_ID, "crafing_recipe") +		override fun getCategoryIdentifier(): CategoryIdentifier<out SBCraftingRecipe> = catIdentifier + +		override fun getTitle(): Text = Text.literal("SkyBlock Crafting") + +		override fun getIcon(): Renderer = EntryStacks.of(Blocks.CRAFTING_TABLE) +		override fun setupDisplay(display: SBCraftingRecipe, bounds: Rectangle): List<Widget> { +			val point = Point(bounds.centerX - 58, bounds.centerY - 27) +			return buildList { +				add(Widgets.createRecipeBase(bounds)) +				add(Widgets.createArrow(Point(point.x + 60, point.y + 18))) +				add(Widgets.createResultSlotBackground(Point(point.x + 95, point.y + 19))) +				for (i in 0 until 3) { +					for (j in 0 until 3) { +						val slot = Widgets.createSlot(Point(point.x + 1 + i * 18, point.y + 1 + j * 18)).markInput() +						add(slot) +						val item = display.neuRecipe.inputs[i + j * 3] +						if (item == NEUIngredient.SENTINEL_EMPTY) continue +						slot.entry(SBItemEntryDefinition.getEntry(item)) // TODO: make use of stackable item entries +					} +				} +				add( +					Widgets.createSlot(Point(point.x + 95, point.y + 19)) +						.entry(SBItemEntryDefinition.getEntry(display.neuRecipe.output)) +						.disableBackground().markOutput() +				) +			} +		} + +	}  } diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBEssenceUpgradeRecipe.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBEssenceUpgradeRecipe.kt index f81d529..ec71ec8 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBEssenceUpgradeRecipe.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBEssenceUpgradeRecipe.kt @@ -1,4 +1,3 @@ -  package moe.nea.firmament.compat.rei.recipes  import me.shedaniel.math.Point @@ -16,47 +15,47 @@ import moe.nea.firmament.repo.SBItemStack  import moe.nea.firmament.util.SkyblockId  class SBEssenceUpgradeRecipe(override val neuRecipe: EssenceRecipeProvider.EssenceUpgradeRecipe) : SBRecipe() { -    object Category : DisplayCategory<SBEssenceUpgradeRecipe> { -        override fun getCategoryIdentifier(): CategoryIdentifier<SBEssenceUpgradeRecipe> = -            CategoryIdentifier.of(Firmament.MOD_ID, "essence_upgrade") +	object Category : DisplayCategory<SBEssenceUpgradeRecipe> { +		override fun getCategoryIdentifier(): CategoryIdentifier<SBEssenceUpgradeRecipe> = +			CategoryIdentifier.of(Firmament.MOD_ID, "essence_upgrade") -        override fun getTitle(): Text { -            return Text.literal("Essence Upgrades") -        } +		override fun getTitle(): Text { +			return Text.literal("Essence Upgrades") +		} -        override fun getIcon(): Renderer { -            return SBItemEntryDefinition.getEntry(SkyblockId("ESSENCE_WITHER")) -        } +		override fun getIcon(): Renderer { +			return SBItemEntryDefinition.getEntry(SkyblockId("ESSENCE_WITHER")) +		} -        override fun setupDisplay(display: SBEssenceUpgradeRecipe, bounds: Rectangle): List<Widget> { -            val recipe = display.neuRecipe -            val list = mutableListOf<Widget>() -            list.add(Widgets.createRecipeBase(bounds)) -            list.add(Widgets.createSlot(Point(bounds.minX + 12, bounds.centerY - 8 - 18 / 2)) -                         .markInput() -                         .entry(SBItemEntryDefinition.getEntry(SBItemStack(recipe.itemId).copy(stars = recipe.starCountAfter - 1)))) -            list.add(Widgets.createSlot(Point(bounds.minX + 12, bounds.centerY - 8 + 18 / 2)) -                         .markInput() -                         .entry(SBItemEntryDefinition.getEntry(recipe.essenceIngredient))) -            list.add(Widgets.createSlot(Point(bounds.maxX - 12 - 16, bounds.centerY - 8)) -                         .markOutput() -                         .entry(SBItemEntryDefinition.getEntry(SBItemStack(recipe.itemId).copy(stars = recipe.starCountAfter)))) -            val extraItems = recipe.extraItems -            list.add(Widgets.createArrow(Point(bounds.centerX - 24 / 2, -                                               if (extraItems.isEmpty()) bounds.centerY - 17 / 2 -                                               else bounds.centerY + 18 / 2))) -            for ((index, item) in extraItems.withIndex()) { -                list.add(Widgets.createSlot( -                    Point(bounds.centerX - extraItems.size * 16 / 2 - 2 / 2 + index * 18, -                          bounds.centerY - 18 / 2)) -                             .markInput() -                             .entry(SBItemEntryDefinition.getEntry(item))) -            } -            return list -        } -    } +		override fun setupDisplay(display: SBEssenceUpgradeRecipe, bounds: Rectangle): List<Widget> { +			val recipe = display.neuRecipe +			val list = mutableListOf<Widget>() +			list.add(Widgets.createRecipeBase(bounds)) +			list.add(Widgets.createSlot(Point(bounds.minX + 12, bounds.centerY - 8 - 18 / 2)) +				         .markInput() +				         .entry(SBItemEntryDefinition.getEntry(SBItemStack(recipe.itemId).copy(stars = recipe.starCountAfter - 1)))) +			list.add(Widgets.createSlot(Point(bounds.minX + 12, bounds.centerY - 8 + 18 / 2)) +				         .markInput() +				         .entry(SBItemEntryDefinition.getEntry(recipe.essenceIngredient))) +			list.add(Widgets.createSlot(Point(bounds.maxX - 12 - 16, bounds.centerY - 8)) +				         .markOutput() +				         .entry(SBItemEntryDefinition.getEntry(SBItemStack(recipe.itemId).copy(stars = recipe.starCountAfter)))) +			val extraItems = recipe.extraItems +			list.add(Widgets.createArrow(Point(bounds.centerX - 24 / 2, +			                                   if (extraItems.isEmpty()) bounds.centerY - 17 / 2 +			                                   else bounds.centerY + 18 / 2))) +			for ((index, item) in extraItems.withIndex()) { +				list.add(Widgets.createSlot( +					Point(bounds.centerX - extraItems.size * 16 / 2 - 2 / 2 + index * 18, +					      bounds.centerY - 18 / 2)) +					         .markInput() +					         .entry(SBItemEntryDefinition.getEntry(item))) +			} +			return list +		} +	} -    override fun getCategoryIdentifier(): CategoryIdentifier<*> { -        return Category.categoryIdentifier -    } +	override fun getCategoryIdentifier(): CategoryIdentifier<*> { +		return Category.categoryIdentifier +	}  } diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBForgeRecipe.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBForgeRecipe.kt index bb51021..96af3fd 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBForgeRecipe.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBForgeRecipe.kt @@ -1,5 +1,3 @@ - -  package moe.nea.firmament.compat.rei.recipes  import io.github.moulberry.repo.data.NEUForgeRecipe @@ -21,51 +19,53 @@ import moe.nea.firmament.compat.rei.SBItemEntryDefinition  import moe.nea.firmament.compat.rei.plus  class SBForgeRecipe(override val neuRecipe: NEUForgeRecipe) : SBRecipe() { -    override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.categoryIdentifier +	override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.categoryIdentifier -    object Category : DisplayCategory<SBForgeRecipe> { -        override fun getCategoryIdentifier(): CategoryIdentifier<SBForgeRecipe> = -            CategoryIdentifier.of(Firmament.MOD_ID, "forge_recipe") +	object Category : DisplayCategory<SBForgeRecipe> { +		override fun getCategoryIdentifier(): CategoryIdentifier<SBForgeRecipe> = +			CategoryIdentifier.of(Firmament.MOD_ID, "forge_recipe") -        override fun getTitle(): Text = Text.literal("Forge Recipes") -        override fun getDisplayHeight(): Int { -            return 104 -        } +		override fun getTitle(): Text = Text.literal("Forge Recipes") +		override fun getDisplayHeight(): Int { +			return 104 +		} -        override fun getIcon(): Renderer = EntryStacks.of(Blocks.ANVIL) -        override fun setupDisplay(display: SBForgeRecipe, bounds: Rectangle): List<Widget> { -            return buildList { -                add(Widgets.createRecipeBase(bounds)) -                add(Widgets.createResultSlotBackground(Point(bounds.minX + 124, bounds.minY + 46))) -                val arrow = Widgets.createArrow(Point(bounds.minX + 90, bounds.minY + 54 - 18 / 2)) -                add(arrow) -                add(Widgets.createTooltip(arrow.bounds, Text.stringifiedTranslatable("firmament.recipe.forge.time", display.neuRecipe.duration.seconds))) -                val ingredientsCenter = Point(bounds.minX + 49 - 8, bounds.minY + 54 - 8) -                val count = display.neuRecipe.inputs.size -                if (count == 1) { -                    add( -                        Widgets.createSlot(Point(ingredientsCenter.x, ingredientsCenter.y)).markInput() -                            .entry(SBItemEntryDefinition.getEntry(display.neuRecipe.inputs.single())) -                    ) -                } else { -                    display.neuRecipe.inputs.forEachIndexed { idx, ingredient -> -                        val rad = Math.PI * 2 * idx / count -                        add( -                            Widgets.createSlot( -                                Point( -                                    cos(rad) * 30, -                                    sin(rad) * 30, -                                ) + ingredientsCenter -                            ).markInput().entry(SBItemEntryDefinition.getEntry(ingredient)) -                        ) -                    } -                } -                add( -                    Widgets.createSlot(Point(bounds.minX + 124, bounds.minY + 46)).markOutput().disableBackground() -                        .entry(SBItemEntryDefinition.getEntry(display.neuRecipe.outputStack)) -                ) -            } -        } -    } +		override fun getIcon(): Renderer = EntryStacks.of(Blocks.ANVIL) +		override fun setupDisplay(display: SBForgeRecipe, bounds: Rectangle): List<Widget> { +			return buildList { +				add(Widgets.createRecipeBase(bounds)) +				add(Widgets.createResultSlotBackground(Point(bounds.minX + 124, bounds.minY + 46))) +				val arrow = Widgets.createArrow(Point(bounds.minX + 90, bounds.minY + 54 - 18 / 2)) +				add(arrow) +				add(Widgets.createTooltip(arrow.bounds, +				                          Text.stringifiedTranslatable("firmament.recipe.forge.time", +				                                                       display.neuRecipe.duration.seconds))) +				val ingredientsCenter = Point(bounds.minX + 49 - 8, bounds.minY + 54 - 8) +				val count = display.neuRecipe.inputs.size +				if (count == 1) { +					add( +						Widgets.createSlot(Point(ingredientsCenter.x, ingredientsCenter.y)).markInput() +							.entry(SBItemEntryDefinition.getEntry(display.neuRecipe.inputs.single())) +					) +				} else { +					display.neuRecipe.inputs.forEachIndexed { idx, ingredient -> +						val rad = Math.PI * 2 * idx / count +						add( +							Widgets.createSlot( +								Point( +									cos(rad) * 30, +									sin(rad) * 30, +								) + ingredientsCenter +							).markInput().entry(SBItemEntryDefinition.getEntry(ingredient)) +						) +					} +				} +				add( +					Widgets.createSlot(Point(bounds.minX + 124, bounds.minY + 46)).markOutput().disableBackground() +						.entry(SBItemEntryDefinition.getEntry(display.neuRecipe.outputStack)) +				) +			} +		} +	}  } diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBKatRecipe.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBKatRecipe.kt index fc77fa6..bafbdcc 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBKatRecipe.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBKatRecipe.kt @@ -1,4 +1,3 @@ -  package moe.nea.firmament.compat.rei.recipes  import io.github.moulberry.repo.data.NEUKatUpgradeRecipe @@ -20,7 +19,6 @@ import me.shedaniel.rei.api.client.registry.display.DisplayCategory  import me.shedaniel.rei.api.common.category.CategoryIdentifier  import me.shedaniel.rei.api.common.util.EntryStacks  import kotlin.time.Duration.Companion.seconds -import net.minecraft.block.Blocks  import net.minecraft.client.gui.DrawContext  import net.minecraft.client.gui.Element  import net.minecraft.item.Items @@ -34,191 +32,191 @@ import moe.nea.firmament.util.MC  import moe.nea.firmament.util.SkyblockId  class SBKatRecipe(override val neuRecipe: NEUKatUpgradeRecipe) : SBRecipe() { -    override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.categoryIdentifier - -    object Category : DisplayCategory<SBKatRecipe> { -        override fun getCategoryIdentifier(): CategoryIdentifier<SBKatRecipe> = -            CategoryIdentifier.of(Firmament.MOD_ID, "kat_recipe") - -        override fun getTitle(): Text = Text.literal("Kat Pet Upgrade") -        override fun getDisplayHeight(): Int { -            return 100 -        } - -        override fun getIcon(): Renderer = EntryStacks.of(Items.BONE) -        override fun setupDisplay(display: SBKatRecipe, bounds: Rectangle): List<Widget> { -            return buildList { -                val arrowWidth = 24 -                val recipe = display.neuRecipe -                val levelValue = Property.upgrade(GetSetter.floating(0F)) -                val slider = SliderComponent(levelValue, 1F, 100F, 1f, 100) -                val outputStack = SBItemStack(SkyblockId(recipe.output.itemId)) -                val inputStack = SBItemStack(SkyblockId(recipe.input.itemId)) -                val inputLevelLabelCenter = Point(bounds.minX + 30 - 18 + 5 + 8, bounds.minY + 25) -                val inputLevelLabel = Widgets.createLabel( -                    inputLevelLabelCenter, -                    Text.literal("")).centered() -                val outputLevelLabelCenter = Point(bounds.maxX - 30 + 8, bounds.minY + 25) -                val outputLevelLabel = Widgets.createLabel( -                    outputLevelLabelCenter, -                    Text.literal("")).centered() -                val coinStack = SBItemStack(SkyblockId.COINS, recipe.coins.toInt()) -                levelValue.whenChanged { oldValue, newValue -> -                    if (oldValue.toInt() == newValue.toInt()) return@whenChanged -                    val oldInput = inputStack.getPetData() ?: return@whenChanged -                    val newInput = PetData.forLevel(oldInput.petId, oldInput.rarity, newValue.toInt()) -                    inputStack.setPetData(newInput) -                    val oldOutput = outputStack.getPetData() ?: return@whenChanged -                    val newOutput = PetData(oldOutput.rarity, oldOutput.petId, newInput.exp) -                    outputStack.setPetData(newOutput) -                    inputLevelLabel.message = Text.literal(newInput.levelData.currentLevel.toString()) -                    inputLevelLabel.bounds.location = Point( -                        inputLevelLabelCenter.x - MC.font.getWidth(inputLevelLabel.message) / 2, -                        inputLevelLabelCenter.y) -                    outputLevelLabel.message = Text.literal(newOutput.levelData.currentLevel.toString()) -                    outputLevelLabel.bounds.location = Point( -                        outputLevelLabelCenter.x - MC.font.getWidth(outputLevelLabel.message) / 2, -                        outputLevelLabelCenter.y) -                    coinStack.setStackSize((recipe.coins * (1 - 0.3 * newValue / 100)).toInt()) -                } -                levelValue.set(1F) -                add(Widgets.createRecipeBase(bounds)) -                add(wrapWidget(Rectangle(bounds.centerX - slider.width / 2, -                                         bounds.maxY - 30, -                                         slider.width, -                                         slider.height), -                               slider)) -                add(Widgets.withTooltip( -                    Widgets.createArrow(Point(bounds.centerX - arrowWidth / 2, bounds.minY + 40)), -                    Text.literal("Upgrade time: " + FirmFormatters.formatTimespan(recipe.seconds.seconds)))) - -                add(Widgets.createResultSlotBackground(Point(bounds.maxX - 30, bounds.minY + 40))) -                add(inputLevelLabel) -                add(outputLevelLabel) -                add(Widgets.createSlot(Point(bounds.maxX - 30, bounds.minY + 40)).markOutput().disableBackground() -                        .entry(SBItemEntryDefinition.getEntry(outputStack))) -                add(Widgets.createSlot(Point(bounds.minX + 30 - 18 + 5, bounds.minY + 40)).markInput() -                        .entry(SBItemEntryDefinition.getEntry(inputStack))) - -                val allInputs = recipe.items.map { SBItemEntryDefinition.getEntry(it) } + -                    listOf(SBItemEntryDefinition.getEntry(coinStack)) -                for ((index, item) in allInputs.withIndex()) { -                    add(Widgets.createSlot( -                        Point(bounds.centerX + index * 20 - allInputs.size * 18 / 2 - (allInputs.size - 1) * 2 / 2, -                              bounds.minY + 20)) -                            .markInput() -                            .entry(item)) -                } -            } -        } -    } +	override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.categoryIdentifier + +	object Category : DisplayCategory<SBKatRecipe> { +		override fun getCategoryIdentifier(): CategoryIdentifier<SBKatRecipe> = +			CategoryIdentifier.of(Firmament.MOD_ID, "kat_recipe") + +		override fun getTitle(): Text = Text.literal("Kat Pet Upgrade") +		override fun getDisplayHeight(): Int { +			return 100 +		} + +		override fun getIcon(): Renderer = EntryStacks.of(Items.BONE) +		override fun setupDisplay(display: SBKatRecipe, bounds: Rectangle): List<Widget> { +			return buildList { +				val arrowWidth = 24 +				val recipe = display.neuRecipe +				val levelValue = Property.upgrade(GetSetter.floating(0F)) +				val slider = SliderComponent(levelValue, 1F, 100F, 1f, 100) +				val outputStack = SBItemStack(SkyblockId(recipe.output.itemId)) +				val inputStack = SBItemStack(SkyblockId(recipe.input.itemId)) +				val inputLevelLabelCenter = Point(bounds.minX + 30 - 18 + 5 + 8, bounds.minY + 25) +				val inputLevelLabel = Widgets.createLabel( +					inputLevelLabelCenter, +					Text.literal("")).centered() +				val outputLevelLabelCenter = Point(bounds.maxX - 30 + 8, bounds.minY + 25) +				val outputLevelLabel = Widgets.createLabel( +					outputLevelLabelCenter, +					Text.literal("")).centered() +				val coinStack = SBItemStack(SkyblockId.COINS, recipe.coins.toInt()) +				levelValue.whenChanged { oldValue, newValue -> +					if (oldValue.toInt() == newValue.toInt()) return@whenChanged +					val oldInput = inputStack.getPetData() ?: return@whenChanged +					val newInput = PetData.forLevel(oldInput.petId, oldInput.rarity, newValue.toInt()) +					inputStack.setPetData(newInput) +					val oldOutput = outputStack.getPetData() ?: return@whenChanged +					val newOutput = PetData(oldOutput.rarity, oldOutput.petId, newInput.exp) +					outputStack.setPetData(newOutput) +					inputLevelLabel.message = Text.literal(newInput.levelData.currentLevel.toString()) +					inputLevelLabel.bounds.location = Point( +						inputLevelLabelCenter.x - MC.font.getWidth(inputLevelLabel.message) / 2, +						inputLevelLabelCenter.y) +					outputLevelLabel.message = Text.literal(newOutput.levelData.currentLevel.toString()) +					outputLevelLabel.bounds.location = Point( +						outputLevelLabelCenter.x - MC.font.getWidth(outputLevelLabel.message) / 2, +						outputLevelLabelCenter.y) +					coinStack.setStackSize((recipe.coins * (1 - 0.3 * newValue / 100)).toInt()) +				} +				levelValue.set(1F) +				add(Widgets.createRecipeBase(bounds)) +				add(wrapWidget(Rectangle(bounds.centerX - slider.width / 2, +				                         bounds.maxY - 30, +				                         slider.width, +				                         slider.height), +				               slider)) +				add(Widgets.withTooltip( +					Widgets.createArrow(Point(bounds.centerX - arrowWidth / 2, bounds.minY + 40)), +					Text.literal("Upgrade time: " + FirmFormatters.formatTimespan(recipe.seconds.seconds)))) + +				add(Widgets.createResultSlotBackground(Point(bounds.maxX - 30, bounds.minY + 40))) +				add(inputLevelLabel) +				add(outputLevelLabel) +				add(Widgets.createSlot(Point(bounds.maxX - 30, bounds.minY + 40)).markOutput().disableBackground() +					    .entry(SBItemEntryDefinition.getEntry(outputStack))) +				add(Widgets.createSlot(Point(bounds.minX + 30 - 18 + 5, bounds.minY + 40)).markInput() +					    .entry(SBItemEntryDefinition.getEntry(inputStack))) + +				val allInputs = recipe.items.map { SBItemEntryDefinition.getEntry(it) } + +					listOf(SBItemEntryDefinition.getEntry(coinStack)) +				for ((index, item) in allInputs.withIndex()) { +					add(Widgets.createSlot( +						Point(bounds.centerX + index * 20 - allInputs.size * 18 / 2 - (allInputs.size - 1) * 2 / 2, +						      bounds.minY + 20)) +						    .markInput() +						    .entry(item)) +				} +			} +		} +	}  }  fun wrapWidget(bounds: Rectangle, component: GuiComponent): Widget { -    return object : WidgetWithBounds() { -        override fun getBounds(): Rectangle { -            return bounds -        } - -        override fun children(): List<Element> { -            return listOf() -        } - -        override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) { -            context.matrices.push() -            context.matrices.translate(bounds.minX.toFloat(), bounds.minY.toFloat(), 0F) -            component.render( -                GuiImmediateContext( -                    ModernRenderContext(context), -                    bounds.minX, bounds.minY, -                    bounds.width, bounds.height, -                    mouseX - bounds.minX, mouseY - bounds.minY, -                    mouseX, mouseY, -                    mouseX.toFloat(), mouseY.toFloat() -                )) -            context.matrices.pop() -        } - -        override fun mouseMoved(mouseX: Double, mouseY: Double) { -            val mouseXInt = mouseX.toInt() -            val mouseYInt = mouseY.toInt() -            component.mouseEvent(MouseEvent.Move(0F, 0F), -                                 GuiImmediateContext( -                                     IMinecraft.instance.provideTopLevelRenderContext(), -                                     bounds.minX, bounds.minY, -                                     bounds.width, bounds.height, -                                     mouseXInt - bounds.minX, mouseYInt - bounds.minY, -                                     mouseXInt, mouseYInt, -                                     mouseX.toFloat(), mouseY.toFloat() -                                 )) -        } - -        override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { -            val mouseXInt = mouseX.toInt() -            val mouseYInt = mouseY.toInt() -            return component.mouseEvent(MouseEvent.Click(button, true), -                                        GuiImmediateContext( -                                            IMinecraft.instance.provideTopLevelRenderContext(), -                                            bounds.minX, bounds.minY, -                                            bounds.width, bounds.height, -                                            mouseXInt - bounds.minX, mouseYInt - bounds.minY, -                                            mouseXInt, mouseYInt, -                                            mouseX.toFloat(), mouseY.toFloat() -                                        )) -        } - -        override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean { -            val mouseXInt = mouseX.toInt() -            val mouseYInt = mouseY.toInt() -            return component.mouseEvent(MouseEvent.Click(button, false), -                                        GuiImmediateContext( -                                            IMinecraft.instance.provideTopLevelRenderContext(), -                                            bounds.minX, bounds.minY, -                                            bounds.width, bounds.height, -                                            mouseXInt - bounds.minX, mouseYInt - bounds.minY, -                                            mouseXInt, mouseYInt, -                                            mouseX.toFloat(), mouseY.toFloat() -                                        )) -        } - -        override fun mouseDragged( -            mouseX: Double, -            mouseY: Double, -            button: Int, -            deltaX: Double, -            deltaY: Double -        ): Boolean { -            val mouseXInt = mouseX.toInt() -            val mouseYInt = mouseY.toInt() -            return component.mouseEvent(MouseEvent.Move(deltaX.toFloat(), deltaY.toFloat()), -                                        GuiImmediateContext( -                                            IMinecraft.instance.provideTopLevelRenderContext(), -                                            bounds.minX, bounds.minY, -                                            bounds.width, bounds.height, -                                            mouseXInt - bounds.minX, mouseYInt - bounds.minY, -                                            mouseXInt, mouseYInt, -                                            mouseX.toFloat(), mouseY.toFloat() -                                        )) - -        } - -        override fun mouseScrolled( -            mouseX: Double, -            mouseY: Double, -            horizontalAmount: Double, -            verticalAmount: Double -        ): Boolean { -            val mouseXInt = mouseX.toInt() -            val mouseYInt = mouseY.toInt() -            return component.mouseEvent(MouseEvent.Scroll(verticalAmount.toFloat()), -                                        GuiImmediateContext( -                                            IMinecraft.instance.provideTopLevelRenderContext(), -                                            bounds.minX, bounds.minY, -                                            bounds.width, bounds.height, -                                            mouseXInt - bounds.minX, mouseYInt - bounds.minY, -                                            mouseXInt, mouseYInt, -                                            mouseX.toFloat(), mouseY.toFloat() -                                        )) -        } -    } +	return object : WidgetWithBounds() { +		override fun getBounds(): Rectangle { +			return bounds +		} + +		override fun children(): List<Element> { +			return listOf() +		} + +		override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) { +			context.matrices.push() +			context.matrices.translate(bounds.minX.toFloat(), bounds.minY.toFloat(), 0F) +			component.render( +				GuiImmediateContext( +					ModernRenderContext(context), +					bounds.minX, bounds.minY, +					bounds.width, bounds.height, +					mouseX - bounds.minX, mouseY - bounds.minY, +					mouseX, mouseY, +					mouseX.toFloat(), mouseY.toFloat() +				)) +			context.matrices.pop() +		} + +		override fun mouseMoved(mouseX: Double, mouseY: Double) { +			val mouseXInt = mouseX.toInt() +			val mouseYInt = mouseY.toInt() +			component.mouseEvent(MouseEvent.Move(0F, 0F), +			                     GuiImmediateContext( +				                     IMinecraft.instance.provideTopLevelRenderContext(), +				                     bounds.minX, bounds.minY, +				                     bounds.width, bounds.height, +				                     mouseXInt - bounds.minX, mouseYInt - bounds.minY, +				                     mouseXInt, mouseYInt, +				                     mouseX.toFloat(), mouseY.toFloat() +			                     )) +		} + +		override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { +			val mouseXInt = mouseX.toInt() +			val mouseYInt = mouseY.toInt() +			return component.mouseEvent(MouseEvent.Click(button, true), +			                            GuiImmediateContext( +				                            IMinecraft.instance.provideTopLevelRenderContext(), +				                            bounds.minX, bounds.minY, +				                            bounds.width, bounds.height, +				                            mouseXInt - bounds.minX, mouseYInt - bounds.minY, +				                            mouseXInt, mouseYInt, +				                            mouseX.toFloat(), mouseY.toFloat() +			                            )) +		} + +		override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean { +			val mouseXInt = mouseX.toInt() +			val mouseYInt = mouseY.toInt() +			return component.mouseEvent(MouseEvent.Click(button, false), +			                            GuiImmediateContext( +				                            IMinecraft.instance.provideTopLevelRenderContext(), +				                            bounds.minX, bounds.minY, +				                            bounds.width, bounds.height, +				                            mouseXInt - bounds.minX, mouseYInt - bounds.minY, +				                            mouseXInt, mouseYInt, +				                            mouseX.toFloat(), mouseY.toFloat() +			                            )) +		} + +		override fun mouseDragged( +			mouseX: Double, +			mouseY: Double, +			button: Int, +			deltaX: Double, +			deltaY: Double +		): Boolean { +			val mouseXInt = mouseX.toInt() +			val mouseYInt = mouseY.toInt() +			return component.mouseEvent(MouseEvent.Move(deltaX.toFloat(), deltaY.toFloat()), +			                            GuiImmediateContext( +				                            IMinecraft.instance.provideTopLevelRenderContext(), +				                            bounds.minX, bounds.minY, +				                            bounds.width, bounds.height, +				                            mouseXInt - bounds.minX, mouseYInt - bounds.minY, +				                            mouseXInt, mouseYInt, +				                            mouseX.toFloat(), mouseY.toFloat() +			                            )) + +		} + +		override fun mouseScrolled( +			mouseX: Double, +			mouseY: Double, +			horizontalAmount: Double, +			verticalAmount: Double +		): Boolean { +			val mouseXInt = mouseX.toInt() +			val mouseYInt = mouseY.toInt() +			return component.mouseEvent(MouseEvent.Scroll(verticalAmount.toFloat()), +			                            GuiImmediateContext( +				                            IMinecraft.instance.provideTopLevelRenderContext(), +				                            bounds.minX, bounds.minY, +				                            bounds.width, bounds.height, +				                            mouseXInt - bounds.minX, mouseYInt - bounds.minY, +				                            mouseXInt, mouseYInt, +				                            mouseX.toFloat(), mouseY.toFloat() +			                            )) +		} +	}  } diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBMobDropRecipe.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBMobDropRecipe.kt index cb240fc..81ec41b 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBMobDropRecipe.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBMobDropRecipe.kt @@ -1,4 +1,3 @@ -  package moe.nea.firmament.compat.rei.recipes  import io.github.moulberry.repo.data.NEUMobDropRecipe @@ -14,95 +13,95 @@ import net.minecraft.item.Items  import net.minecraft.text.Text  import net.minecraft.util.Identifier  import moe.nea.firmament.Firmament -import moe.nea.firmament.gui.entity.EntityRenderer  import moe.nea.firmament.compat.rei.EntityWidget  import moe.nea.firmament.compat.rei.SBItemEntryDefinition +import moe.nea.firmament.gui.entity.EntityRenderer  class SBMobDropRecipe(override val neuRecipe: NEUMobDropRecipe) : SBRecipe() { -    override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.categoryIdentifier +	override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.categoryIdentifier -    object Category : DisplayCategory<SBMobDropRecipe> { -        override fun getCategoryIdentifier(): CategoryIdentifier<SBMobDropRecipe> = -            CategoryIdentifier.of(Firmament.MOD_ID, "mob_drop_recipe") +	object Category : DisplayCategory<SBMobDropRecipe> { +		override fun getCategoryIdentifier(): CategoryIdentifier<SBMobDropRecipe> = +			CategoryIdentifier.of(Firmament.MOD_ID, "mob_drop_recipe") -        override fun getTitle(): Text = Text.literal("Mob Drops") -        override fun getDisplayHeight(): Int { -            return 100 -        } +		override fun getTitle(): Text = Text.literal("Mob Drops") +		override fun getDisplayHeight(): Int { +			return 100 +		} -        override fun getIcon(): Renderer = EntryStacks.of(Items.DIAMOND_SWORD) -        override fun setupDisplay(display: SBMobDropRecipe, bounds: Rectangle): List<Widget> { -            return buildList { -                add(Widgets.createRecipeBase(bounds)) -                val source = display.neuRecipe.render -                val entity = if (source.startsWith("@")) { -                    EntityRenderer.constructEntity(Identifier.of(source.substring(1))) -                } else { -                    EntityRenderer.applyModifiers(source, listOf()) -                } -                if (entity != null) { -                    val level = display.neuRecipe.level -                    val fullMobName = -                        if (level > 0) Text.translatable("firmament.recipe.mobs.name", level, display.neuRecipe.name) -                        else Text.translatable("firmament.recipe.mobs.name.nolevel", display.neuRecipe.name) -                    val tt = mutableListOf<Text>() -                    tt.add((fullMobName)) -                    tt.add(Text.literal("")) -                    if (display.neuRecipe.coins > 0) { -                        tt.add(Text.stringifiedTranslatable("firmament.recipe.mobs.coins", display.neuRecipe.coins)) -                    } -                    if (display.neuRecipe.combatExperience > 0) { -                        tt.add( -                            Text.stringifiedTranslatable( -                                "firmament.recipe.mobs.combat", -                                display.neuRecipe.combatExperience -                            ) -                        ) -                    } -                    if (display.neuRecipe.enchantingExperience > 0) { -                        tt.add( -                            Text.stringifiedTranslatable( -                                "firmament.recipe.mobs.exp", -                                display.neuRecipe.enchantingExperience -                            ) -                        ) -                    } -                    if (display.neuRecipe.extra != null) -                        display.neuRecipe.extra.mapTo(tt) { Text.literal(it) } -                    if (tt.size == 2) -                        tt.removeAt(1) -                    add( -                        Widgets.withTooltip( -	                        EntityWidget(entity, Point(bounds.minX + 5, bounds.minY + 15)), -	                        tt -                        ) -                    ) -                } -                add( -                    Widgets.createLabel(Point(bounds.minX + 15, bounds.minY + 5), Text.literal(display.neuRecipe.name)) -                        .leftAligned() -                ) -                var x = bounds.minX + 60 -                var y = bounds.minY + 20 -                for (drop in display.neuRecipe.drops) { -                    val lore = drop.extra.mapTo(mutableListOf()) { Text.literal(it) } -                    if (drop.chance != null) { -                        lore += listOf(Text.translatable("firmament.recipe.mobs.drops", drop.chance)) -                    } -                    val item = SBItemEntryDefinition.getEntry(drop.dropItem) -                        .value.copy(extraLore = lore) -                    add( -                        Widgets.createSlot(Point(x, y)).markOutput() -                            .entries(listOf(SBItemEntryDefinition.getEntry(item))) -                    ) -                    x += 18 -                    if (x > bounds.maxX - 30) { -                        x = bounds.minX + 60 -                        y += 18 -                    } -                } -            } -        } -    } +		override fun getIcon(): Renderer = EntryStacks.of(Items.DIAMOND_SWORD) +		override fun setupDisplay(display: SBMobDropRecipe, bounds: Rectangle): List<Widget> { +			return buildList { +				add(Widgets.createRecipeBase(bounds)) +				val source = display.neuRecipe.render +				val entity = if (source.startsWith("@")) { +					EntityRenderer.constructEntity(Identifier.of(source.substring(1))) +				} else { +					EntityRenderer.applyModifiers(source, listOf()) +				} +				if (entity != null) { +					val level = display.neuRecipe.level +					val fullMobName = +						if (level > 0) Text.translatable("firmament.recipe.mobs.name", level, display.neuRecipe.name) +						else Text.translatable("firmament.recipe.mobs.name.nolevel", display.neuRecipe.name) +					val tt = mutableListOf<Text>() +					tt.add((fullMobName)) +					tt.add(Text.literal("")) +					if (display.neuRecipe.coins > 0) { +						tt.add(Text.stringifiedTranslatable("firmament.recipe.mobs.coins", display.neuRecipe.coins)) +					} +					if (display.neuRecipe.combatExperience > 0) { +						tt.add( +							Text.stringifiedTranslatable( +								"firmament.recipe.mobs.combat", +								display.neuRecipe.combatExperience +							) +						) +					} +					if (display.neuRecipe.enchantingExperience > 0) { +						tt.add( +							Text.stringifiedTranslatable( +								"firmament.recipe.mobs.exp", +								display.neuRecipe.enchantingExperience +							) +						) +					} +					if (display.neuRecipe.extra != null) +						display.neuRecipe.extra.mapTo(tt) { Text.literal(it) } +					if (tt.size == 2) +						tt.removeAt(1) +					add( +						Widgets.withTooltip( +							EntityWidget(entity, Point(bounds.minX + 5, bounds.minY + 15)), +							tt +						) +					) +				} +				add( +					Widgets.createLabel(Point(bounds.minX + 15, bounds.minY + 5), Text.literal(display.neuRecipe.name)) +						.leftAligned() +				) +				var x = bounds.minX + 60 +				var y = bounds.minY + 20 +				for (drop in display.neuRecipe.drops) { +					val lore = drop.extra.mapTo(mutableListOf()) { Text.literal(it) } +					if (drop.chance != null) { +						lore += listOf(Text.translatable("firmament.recipe.mobs.drops", drop.chance)) +					} +					val item = SBItemEntryDefinition.getEntry(drop.dropItem) +						.value.copy(extraLore = lore) +					add( +						Widgets.createSlot(Point(x, y)).markOutput() +							.entries(listOf(SBItemEntryDefinition.getEntry(item))) +					) +					x += 18 +					if (x > bounds.maxX - 30) { +						x = bounds.minX + 60 +						y += 18 +					} +				} +			} +		} +	}  } diff --git a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBRecipe.kt b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBRecipe.kt index a66a529..de7779f 100644 --- a/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBRecipe.kt +++ b/src/compat/rei/java/moe/nea/firmament/compat/rei/recipes/SBRecipe.kt @@ -2,28 +2,41 @@ package moe.nea.firmament.compat.rei.recipes  import io.github.moulberry.repo.data.NEUIngredient  import io.github.moulberry.repo.data.NEURecipe +import java.util.Optional  import me.shedaniel.rei.api.common.display.Display +import me.shedaniel.rei.api.common.display.DisplaySerializer  import me.shedaniel.rei.api.common.entry.EntryIngredient +import net.minecraft.util.Identifier  import moe.nea.firmament.compat.rei.SBItemEntryDefinition  import moe.nea.firmament.util.SkyblockId  abstract class SBRecipe : Display { -    abstract val neuRecipe: NEURecipe -    override fun getInputEntries(): List<EntryIngredient> { -        return neuRecipe.allInputs -            .filter { it.itemId != NEUIngredient.NEU_SENTINEL_EMPTY } -            .map { -                val entryStack = SBItemEntryDefinition.getEntry(SkyblockId(it.itemId)) -                EntryIngredient.of(entryStack) -            } -    } +	override fun getDisplayLocation(): Optional<Identifier> { +		// In theory, we could return a location for the neuRecipe here. (Something along the lines of neurepo:items/item_id.json/0 for the 0th recipe in the items/item_id.json recipes array). +		return Optional.empty() +	} -    override fun getOutputEntries(): List<EntryIngredient> { -        return neuRecipe.allOutputs -            .filter { it.itemId != NEUIngredient.NEU_SENTINEL_EMPTY } -            .map { -                val entryStack = SBItemEntryDefinition.getEntry(SkyblockId(it.itemId)) -                EntryIngredient.of(entryStack) -            } -    } +	override fun getSerializer(): DisplaySerializer<out Display>? { +		// While returning null here is discouraged, we are fine to do so, since this recipe will never travel through the network +		return null +	} + +	abstract val neuRecipe: NEURecipe +	override fun getInputEntries(): List<EntryIngredient> { +		return neuRecipe.allInputs +			.filter { it.itemId != NEUIngredient.NEU_SENTINEL_EMPTY } +			.map { +				val entryStack = SBItemEntryDefinition.getEntry(SkyblockId(it.itemId)) +				EntryIngredient.of(entryStack) +			} +	} + +	override fun getOutputEntries(): List<EntryIngredient> { +		return neuRecipe.allOutputs +			.filter { it.itemId != NEUIngredient.NEU_SENTINEL_EMPTY } +			.map { +				val entryStack = SBItemEntryDefinition.getEntry(SkyblockId(it.itemId)) +				EntryIngredient.of(entryStack) +			} +	}  } diff --git a/src/compat/sodium/java/SodiumChunkReloader.kt b/src/compat/sodium/java/SodiumChunkReloader.kt index 932c338..0256b88 100644 --- a/src/compat/sodium/java/SodiumChunkReloader.kt +++ b/src/compat/sodium/java/SodiumChunkReloader.kt @@ -1,6 +1,6 @@  package moe.nea.firmament.compat.sodium -import me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer +import net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer  import moe.nea.firmament.mixins.accessor.sodium.AccessorSodiumWorldRenderer  class SodiumChunkReloader : Runnable { diff --git a/src/compat/sodium/java/moe/nea/firmament/mixins/accessor/sodium/AccessorSodiumWorldRenderer.java b/src/compat/sodium/java/moe/nea/firmament/mixins/accessor/sodium/AccessorSodiumWorldRenderer.java index d585cbc..f75874d 100644 --- a/src/compat/sodium/java/moe/nea/firmament/mixins/accessor/sodium/AccessorSodiumWorldRenderer.java +++ b/src/compat/sodium/java/moe/nea/firmament/mixins/accessor/sodium/AccessorSodiumWorldRenderer.java @@ -1,7 +1,7 @@  package moe.nea.firmament.mixins.accessor.sodium; -import me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer; -import me.jellysquid.mods.sodium.client.render.chunk.RenderSectionManager; +import net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer; +import net.caffeinemc.mods.sodium.client.render.chunk.RenderSectionManager;  import org.spongepowered.asm.mixin.Mixin;  import org.spongepowered.asm.mixin.Pseudo;  import org.spongepowered.asm.mixin.gen.Accessor; @@ -9,6 +9,6 @@ import org.spongepowered.asm.mixin.gen.Accessor;  @Mixin(SodiumWorldRenderer.class)  @Pseudo  public interface AccessorSodiumWorldRenderer { -    @Accessor(value = "renderSectionManager", remap = false) -    RenderSectionManager getRenderSectionManager_firmament(); +	@Accessor(value = "renderSectionManager", remap = false) +	RenderSectionManager getRenderSectionManager_firmament();  } diff --git a/src/compat/sodium/java/moe/nea/firmament/mixins/custommodels/PatchBlockModelInSodiumChunkGenerator.java b/src/compat/sodium/java/moe/nea/firmament/mixins/custommodels/PatchBlockModelInSodiumChunkGenerator.java index 90f20bc..fe87310 100644 --- a/src/compat/sodium/java/moe/nea/firmament/mixins/custommodels/PatchBlockModelInSodiumChunkGenerator.java +++ b/src/compat/sodium/java/moe/nea/firmament/mixins/custommodels/PatchBlockModelInSodiumChunkGenerator.java @@ -3,8 +3,8 @@ package moe.nea.firmament.mixins.custommodels;  import com.llamalad7.mixinextras.injector.wrapoperation.Operation;  import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;  import com.llamalad7.mixinextras.sugar.Local; -import me.jellysquid.mods.sodium.client.render.chunk.compile.tasks.ChunkBuilderMeshingTask;  import moe.nea.firmament.features.texturepack.CustomBlockTextures; +import net.caffeinemc.mods.sodium.client.render.chunk.compile.tasks.ChunkBuilderMeshingTask;  import net.minecraft.block.BlockState;  import net.minecraft.client.render.block.BlockModels;  import net.minecraft.client.render.model.BakedModel; @@ -15,7 +15,7 @@ import org.spongepowered.asm.mixin.injection.At;  @Mixin(ChunkBuilderMeshingTask.class)  public class PatchBlockModelInSodiumChunkGenerator {      @WrapOperation( -        method = "execute(Lme/jellysquid/mods/sodium/client/render/chunk/compile/ChunkBuildContext;Lme/jellysquid/mods/sodium/client/util/task/CancellationToken;)Lme/jellysquid/mods/sodium/client/render/chunk/compile/ChunkBuildOutput;", +        method = "execute(Lnet/caffeinemc/mods/sodium/client/render/chunk/compile/ChunkBuildContext;Lnet/caffeinemc/mods/sodium/client/util/task/CancellationToken;)Lnet/caffeinemc/mods/sodium/client/render/chunk/compile/ChunkBuildOutput;",          at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/block/BlockModels;getModel(Lnet/minecraft/block/BlockState;)Lnet/minecraft/client/render/model/BakedModel;"))      private BakedModel replaceBlockModel(BlockModels instance, BlockState state, Operation<BakedModel> original,                                           @Local(name = "blockPos") BlockPos.Mutable pos) { diff --git a/src/main/java/moe/nea/firmament/mixins/CustomDurabilityBarPatch.java b/src/main/java/moe/nea/firmament/mixins/CustomDurabilityBarPatch.java index 0f4d324..fde3580 100644 --- a/src/main/java/moe/nea/firmament/mixins/CustomDurabilityBarPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/CustomDurabilityBarPatch.java @@ -14,7 +14,7 @@ import org.spongepowered.asm.mixin.injection.At;  @Mixin(DrawContext.class)  public class CustomDurabilityBarPatch {      @WrapOperation( -        method = "drawItemInSlot(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V", +        method = "drawItemBar",          at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;isItemBarVisible()Z")      )      private boolean onIsItemBarVisible( @@ -29,7 +29,7 @@ public class CustomDurabilityBarPatch {          return barOverride.get() != null;      } -    @WrapOperation(method = "drawItemInSlot(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V", +    @WrapOperation(method = "drawItemBar",          at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;getItemBarStep()I"))      private int overrideItemStep(          ItemStack instance, Operation<Integer> original, @@ -40,7 +40,7 @@ public class CustomDurabilityBarPatch {          return original.call(instance);      } -    @WrapOperation(method = "drawItemInSlot(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V", +    @WrapOperation(method = "drawItemBar",          at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;getItemBarColor()I"))      private int overrideItemColor(          ItemStack instance, Operation<Integer> original, diff --git a/src/main/java/moe/nea/firmament/mixins/CustomModelBakerPatch.java b/src/main/java/moe/nea/firmament/mixins/CustomModelBakerPatch.java deleted file mode 100644 index c1e359d..0000000 --- a/src/main/java/moe/nea/firmament/mixins/CustomModelBakerPatch.java +++ /dev/null @@ -1,49 +0,0 @@ -package moe.nea.firmament.mixins; - -import moe.nea.firmament.events.BakeExtraModelsEvent; -import net.minecraft.client.render.model.ModelLoader; -import net.minecraft.client.render.model.UnbakedModel; -import net.minecraft.client.util.ModelIdentifier; -import net.minecraft.util.Identifier; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import java.util.Map; - -@Mixin(ModelLoader.class) -public abstract class CustomModelBakerPatch { - -    @Shadow -    @Final -    private Map<ModelIdentifier, UnbakedModel> modelsToBake; - -    @Shadow -    protected abstract void loadItemModel(ModelIdentifier id); - -    @Shadow -    abstract UnbakedModel getOrLoadModel(Identifier id); - -    @Shadow -    protected abstract void add(ModelIdentifier id, UnbakedModel model); - -    @Unique -    private void loadNonItemModel(ModelIdentifier identifier) { -        UnbakedModel unbakedModel = this.getOrLoadModel(identifier.id()); -        this.add(identifier, unbakedModel); -    } - - -    @Inject(method = "bake", at = @At("HEAD")) -    public void onBake(ModelLoader.SpriteGetter spliteGetter, CallbackInfo ci) { -        BakeExtraModelsEvent.Companion.publish(new BakeExtraModelsEvent(this::loadItemModel, this::loadNonItemModel)); -        modelsToBake.values().forEach(model -> model.setParents(this::getOrLoadModel)); -//        modelsToBake.keySet().stream() -//            .filter(it -> !it.id().getNamespace().equals("minecraft")) -//            .forEach(it -> System.out.println("Non minecraft texture is being loaded: " + it)); -    } -} diff --git a/src/main/java/moe/nea/firmament/mixins/CustomModelEventPatch.java b/src/main/java/moe/nea/firmament/mixins/CustomModelEventPatch.java index 61fc82e..e7207f4 100644 --- a/src/main/java/moe/nea/firmament/mixins/CustomModelEventPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/CustomModelEventPatch.java @@ -7,6 +7,7 @@ import net.minecraft.client.render.item.ItemModels;  import net.minecraft.client.render.model.BakedModel;  import net.minecraft.client.render.model.BakedModelManager;  import net.minecraft.item.ItemStack; +import net.minecraft.util.Identifier;  import org.spongepowered.asm.mixin.Final;  import org.spongepowered.asm.mixin.Mixin;  import org.spongepowered.asm.mixin.Shadow; @@ -14,16 +15,15 @@ import org.spongepowered.asm.mixin.injection.At;  import org.spongepowered.asm.mixin.injection.Inject;  import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import java.util.Map; +  @Mixin(ItemModels.class)  public class CustomModelEventPatch { -    @Shadow -    @Final -    private BakedModelManager modelManager; -    @Inject(method = "getModel(Lnet/minecraft/item/ItemStack;)Lnet/minecraft/client/render/model/BakedModel;", at = @At("HEAD"), cancellable = true) -    public void onGetModel(ItemStack stack, CallbackInfoReturnable<BakedModel> cir) { -        var model = CustomItemModelEvent.getModel(stack, modelManager); -        if (model != null) -            cir.setReturnValue(model); -    } +	@Inject(method = "getModel(Lnet/minecraft/item/ItemStack;)Lnet/minecraft/client/render/model/BakedModel;", at = @At("HEAD"), cancellable = true) +	public void onGetModel(ItemStack stack, CallbackInfoReturnable<BakedModel> cir) { +		var model = CustomItemModelEvent.getModel(stack, (ItemModels) (Object) this); +		if (model != null) +			cir.setReturnValue(model); +	}  } diff --git a/src/main/java/moe/nea/firmament/mixins/CustomSkullTexturePatch.java b/src/main/java/moe/nea/firmament/mixins/CustomSkullTexturePatch.java index 4b3f3c3..f3b616a 100644 --- a/src/main/java/moe/nea/firmament/mixins/CustomSkullTexturePatch.java +++ b/src/main/java/moe/nea/firmament/mixins/CustomSkullTexturePatch.java @@ -2,7 +2,6 @@  package moe.nea.firmament.mixins; -import com.mojang.authlib.GameProfile;  import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures;  import net.minecraft.block.SkullBlock;  import net.minecraft.client.render.RenderLayer; @@ -15,8 +14,8 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;  @Mixin(SkullBlockEntityRenderer.class)  public class CustomSkullTexturePatch { -    @Inject(method = "getRenderLayer", at = @At("HEAD"), cancellable = true) -    private static void onGetRenderLayer(SkullBlock.SkullType type, ProfileComponent profile, CallbackInfoReturnable<RenderLayer> cir) { -        CustomSkyBlockTextures.INSTANCE.modifySkullTexture(type, profile, cir); -    } +	@Inject(method = "getRenderLayer", at = @At("HEAD"), cancellable = true) +	private static void onGetRenderLayer(SkullBlock.SkullType type, ProfileComponent profile, CallbackInfoReturnable<RenderLayer> cir) { +		CustomSkyBlockTextures.INSTANCE.modifySkullTexture(type, profile, cir); +	}  } diff --git a/src/main/java/moe/nea/firmament/mixins/DFUEntityIdFixPatch.java b/src/main/java/moe/nea/firmament/mixins/DFUEntityIdFixPatch.java index da04ca2..717d404 100644 --- a/src/main/java/moe/nea/firmament/mixins/DFUEntityIdFixPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/DFUEntityIdFixPatch.java @@ -17,6 +17,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;  import java.util.Map; +// TODO: rework this  @Mixin(EntityIdFix.class)  public abstract class DFUEntityIdFixPatch extends DataFix {      @Shadow diff --git a/src/main/java/moe/nea/firmament/mixins/InjectCustomShaderPrograms.java b/src/main/java/moe/nea/firmament/mixins/InjectCustomShaderPrograms.java deleted file mode 100644 index 5306e42..0000000 --- a/src/main/java/moe/nea/firmament/mixins/InjectCustomShaderPrograms.java +++ /dev/null @@ -1,31 +0,0 @@ -package moe.nea.firmament.mixins; - -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import com.llamalad7.mixinextras.sugar.Local; -import com.mojang.datafixers.util.Pair; -import moe.nea.firmament.events.RegisterCustomShadersEvent; -import net.minecraft.client.gl.ShaderProgram; -import net.minecraft.client.render.GameRenderer; -import net.minecraft.resource.ResourceFactory; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import java.util.List; -import java.util.function.Consumer; - -@Mixin(GameRenderer.class) -public class InjectCustomShaderPrograms { - -    @Inject(method = "loadPrograms", -        at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/GameRenderer;loadBlurPostProcessor(Lnet/minecraft/resource/ResourceFactory;)V", -            shift = At.Shift.AFTER)) -    void addFirmamentShaders( -        ResourceFactory resourceFactory, CallbackInfo ci, -        @Local(index = 3) List<Pair<ShaderProgram, Consumer<ShaderProgram>>> list -    ) { -        var event = new RegisterCustomShadersEvent(list, resourceFactory); -        RegisterCustomShadersEvent.Companion.publish(event); -    } -} diff --git a/src/main/java/moe/nea/firmament/mixins/MainWindowFirstLoadPatch.java b/src/main/java/moe/nea/firmament/mixins/MainWindowFirstLoadPatch.java new file mode 100644 index 0000000..0a90b35 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/MainWindowFirstLoadPatch.java @@ -0,0 +1,31 @@ +package moe.nea.firmament.mixins; + +import moe.nea.firmament.Firmament; +import moe.nea.firmament.events.DebugInstantiateEvent; +import net.minecraft.client.gui.LogoDrawer; +import net.minecraft.client.gui.screen.TitleScreen; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(TitleScreen.class) +public class MainWindowFirstLoadPatch { +	@Unique +	private static boolean hasInited = false; + +	@Inject(method = "<init>(ZLnet/minecraft/client/gui/LogoDrawer;)V", at = @At("RETURN")) +	private void onCreate(boolean doBackgroundFade, LogoDrawer logoDrawer, CallbackInfo ci) { +		if (!hasInited) { +			try { +				DebugInstantiateEvent.Companion.publish(new DebugInstantiateEvent()); +			} catch (Throwable t) { +				Firmament.INSTANCE.getLogger().error("Failed to instantiate debug instances", t); +				System.exit(1); +				throw t; +			} +		} +		hasInited = true; +	} +} diff --git a/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java b/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java index fd50c72..1034a12 100644 --- a/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java +++ b/src/main/java/moe/nea/firmament/mixins/MixinHandledScreen.java @@ -2,6 +2,9 @@  package moe.nea.firmament.mixins; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local;  import moe.nea.firmament.events.*;  import net.minecraft.client.gui.DrawContext;  import net.minecraft.client.gui.screen.ingame.HandledScreen; @@ -21,6 +24,8 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;  import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;  import org.spongepowered.asm.mixin.injection.callback.LocalCapture; +import java.util.Iterator; +  @Mixin(HandledScreen.class)  public abstract class MixinHandledScreen<T extends ScreenHandler> { @@ -90,15 +95,12 @@ public abstract class MixinHandledScreen<T extends ScreenHandler> {  	} -	@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;drawSlot(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/screen/slot/Slot;)V", shift = At.Shift.AFTER), locals = LocalCapture.CAPTURE_FAILHARD) -	public void onAfterDrawSlot(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci, int i, int j, int k, Slot slot) { -		SlotRenderEvents.After event = new SlotRenderEvents.After(context, slot, mouseX, mouseY, delta); -		SlotRenderEvents.After.Companion.publish(event); -	} - -	@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;drawSlot(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/screen/slot/Slot;)V", shift = At.Shift.BEFORE), locals = LocalCapture.CAPTURE_FAILHARD) -	public void onBeforeDrawSlot(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci, int i, int j, int k, Slot slot) { -		SlotRenderEvents.Before event = new SlotRenderEvents.Before(context, slot, mouseX, mouseY, delta); -		SlotRenderEvents.Before.Companion.publish(event); +	@WrapOperation(method = "drawSlots", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;drawSlot(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/screen/slot/Slot;)V")) +	public void onDrawSlots(HandledScreen instance, DrawContext context, Slot slot, Operation<Void> original) { +		var before = new SlotRenderEvents.Before(context, slot); +		SlotRenderEvents.Before.Companion.publish(before); +		original.call(instance, context, slot); +		var after = new SlotRenderEvents.After(context, slot); +		SlotRenderEvents.After.Companion.publish(after);  	}  } diff --git a/src/main/java/moe/nea/firmament/mixins/ReplaceTextColorInHandledScreen.java b/src/main/java/moe/nea/firmament/mixins/ReplaceTextColorInHandledScreen.java index 190fda0..c9fb073 100644 --- a/src/main/java/moe/nea/firmament/mixins/ReplaceTextColorInHandledScreen.java +++ b/src/main/java/moe/nea/firmament/mixins/ReplaceTextColorInHandledScreen.java @@ -20,12 +20,16 @@ import org.spongepowered.asm.mixin.injection.At;  	AnvilScreen.class, BeaconScreen.class})  public class ReplaceTextColorInHandledScreen { +	// To my future self: double check those mixins, but don't be too concerned about errors. Some of the wrapopertions +	// only apply in some of the specified subclasses. +  	@WrapOperation(  		method = "drawForeground",  		at = @At(  			value = "INVOKE",  			target = "Lnet/minecraft/client/gui/DrawContext;drawText(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/text/Text;IIIZ)I"), -		expect = 0) +		expect = 0, +		require = 0)  	private int replaceTextColorWithVariableShadow(DrawContext instance, TextRenderer textRenderer, Text text, int x, int y, int color, boolean shadow, Operation<Integer> original) {  		return original.call(instance, textRenderer, text, x, y, CustomTextColors.INSTANCE.mapTextColor(text, color), shadow);  	} @@ -35,7 +39,8 @@ public class ReplaceTextColorInHandledScreen {  		at = @At(  			value = "INVOKE",  			target = "Lnet/minecraft/client/gui/DrawContext;drawTextWithShadow(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/text/Text;III)I"), -		expect = 0) +		expect = 0, +		require = 0)  	private int replaceTextColorWithShadow(DrawContext instance, TextRenderer textRenderer, Text text, int x, int y, int color, Operation<Integer> original) {  		return original.call(instance, textRenderer, text, x, y, CustomTextColors.INSTANCE.mapTextColor(text, color));  	} diff --git a/src/main/java/moe/nea/firmament/mixins/SlotUpdateListener.java b/src/main/java/moe/nea/firmament/mixins/SlotUpdateListener.java index 6c854d4..06ecbd4 100644 --- a/src/main/java/moe/nea/firmament/mixins/SlotUpdateListener.java +++ b/src/main/java/moe/nea/firmament/mixins/SlotUpdateListener.java @@ -23,14 +23,13 @@ public abstract class SlotUpdateListener extends ClientCommonNetworkHandler {  	@Inject(  		method = "onScreenHandlerSlotUpdate", -		at = @At(value = "INVOKE", target = "Lnet/minecraft/client/tutorial/TutorialManager;onSlotUpdate(Lnet/minecraft/item/ItemStack;)V")) +		at = @At(value = "TAIL"))  	private void onSingleSlotUpdate(  		ScreenHandlerSlotUpdateS2CPacket packet,  		CallbackInfo ci) {  		var player = this.client.player;  		assert player != null; -		if (packet.getSyncId() == ScreenHandlerSlotUpdateS2CPacket.UPDATE_PLAYER_INVENTORY_SYNC_ID -			|| packet.getSyncId() == 0) { +		if (packet.getSyncId() == 0) {  			PlayerInventoryUpdate.Companion.publish(new PlayerInventoryUpdate.Single(packet.getSlot(), packet.getStack()));  		} else if (packet.getSyncId() == player.currentScreenHandler.syncId) {  			ChestInventoryUpdateEvent.Companion.publish( @@ -40,8 +39,7 @@ public abstract class SlotUpdateListener extends ClientCommonNetworkHandler {  	}  	@Inject(method = "onInventory", -		at = @At(value = "INVOKE", target = "Lnet/minecraft/network/NetworkThreadUtils;forceMainThread(Lnet/minecraft/network/packet/Packet;Lnet/minecraft/network/listener/PacketListener;Lnet/minecraft/util/thread/ThreadExecutor;)V", -			shift = At.Shift.AFTER)) +		at = @At("TAIL"))  	private void onMultiSlotUpdate(InventoryS2CPacket packet, CallbackInfo ci) {  		var player = this.client.player;  		assert player != null; diff --git a/src/main/java/moe/nea/firmament/mixins/WorldReadyEventPatch.java b/src/main/java/moe/nea/firmament/mixins/WorldReadyEventPatch.java index c444f12..d4b8c9e 100644 --- a/src/main/java/moe/nea/firmament/mixins/WorldReadyEventPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/WorldReadyEventPatch.java @@ -3,16 +3,17 @@  package moe.nea.firmament.mixins;  import moe.nea.firmament.events.WorldReadyEvent; +import net.minecraft.client.MinecraftClient;  import net.minecraft.client.gui.screen.DownloadingTerrainScreen;  import org.spongepowered.asm.mixin.Mixin;  import org.spongepowered.asm.mixin.injection.At;  import org.spongepowered.asm.mixin.injection.Inject;  import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(DownloadingTerrainScreen.class) +@Mixin(MinecraftClient.class)  public class WorldReadyEventPatch { -    @Inject(method = "close", at = @At("HEAD")) -    public void onClose(CallbackInfo ci) { -        WorldReadyEvent.Companion.publish(new WorldReadyEvent()); -    } +	@Inject(method = "joinWorld", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;setWorld(Lnet/minecraft/client/world/ClientWorld;)V", shift = At.Shift.AFTER)) +	public void onClose(CallbackInfo ci) { +		WorldReadyEvent.Companion.publish(new WorldReadyEvent()); +	}  } diff --git a/src/main/java/moe/nea/firmament/mixins/WorldRenderLastEventPatch.java b/src/main/java/moe/nea/firmament/mixins/WorldRenderLastEventPatch.java index 2ff4560..847fb4d 100644 --- a/src/main/java/moe/nea/firmament/mixins/WorldRenderLastEventPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/WorldRenderLastEventPatch.java @@ -5,8 +5,10 @@ package moe.nea.firmament.mixins;  import com.llamalad7.mixinextras.sugar.Local;  import moe.nea.firmament.events.WorldRenderLastEvent;  import net.minecraft.client.render.*; +import net.minecraft.client.util.Handle; +import net.minecraft.client.util.ObjectAllocator;  import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.world.tick.TickManager; +import net.minecraft.util.profiler.Profiler;  import org.joml.Matrix4f;  import org.spongepowered.asm.mixin.Final;  import org.spongepowered.asm.mixin.Mixin; @@ -16,22 +18,30 @@ import org.spongepowered.asm.mixin.injection.Inject;  import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;  @Mixin(WorldRenderer.class) -public class WorldRenderLastEventPatch { -    @Shadow -    @Final -    private BufferBuilderStorage bufferBuilders; +public abstract class WorldRenderLastEventPatch { +	@Shadow +	@Final +	private BufferBuilderStorage bufferBuilders; -    @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/WorldRenderer;renderChunkDebugInfo(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;Lnet/minecraft/client/render/Camera;)V", shift = At.Shift.BEFORE)) -    public void onWorldRenderLast( -        RenderTickCounter tickCounter, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, -        LightmapTextureManager lightmapTextureManager, Matrix4f matrix4f, Matrix4f matrix4f2, -        CallbackInfo ci, @Local MatrixStack matrixStack -    ) { -        var event = new WorldRenderLastEvent( -            matrixStack, tickCounter, renderBlockOutline, -            camera, gameRenderer, lightmapTextureManager, -            this.bufferBuilders.getEntityVertexConsumers() -        ); -        WorldRenderLastEvent.Companion.publish(event); -    } +	@Shadow +	@Final +	private DefaultFramebufferSet framebufferSet; + +	@Shadow +	protected abstract void checkEmpty(MatrixStack matrices); + +	@Inject(method = "method_62214", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiler/Profiler;pop()V", shift = At.Shift.AFTER)) +	public void onWorldRenderLast(Fog fog, RenderTickCounter tickCounter, Camera camera, Profiler profiler, Matrix4f matrix4f, Matrix4f matrix4f2, Handle handle, Handle handle2, Handle handle3, Handle handle4, boolean bl, Frustum frustum, Handle handle5, CallbackInfo ci) { +		var imm = this.bufferBuilders.getEntityVertexConsumers(); +		var stack = new MatrixStack(); +		// TODO: pre-cancel this event if F1 is active +		var event = new WorldRenderLastEvent( +			stack, tickCounter, +			camera, +			imm +		); +		WorldRenderLastEvent.Companion.publish(event); +		imm.draw(); +		checkEmpty(stack); +	}  } diff --git a/src/main/java/moe/nea/firmament/mixins/accessor/AccessorGameRenderer.java b/src/main/java/moe/nea/firmament/mixins/accessor/AccessorGameRenderer.java deleted file mode 100644 index f5d2202..0000000 --- a/src/main/java/moe/nea/firmament/mixins/accessor/AccessorGameRenderer.java +++ /dev/null @@ -1,14 +0,0 @@ - - -package moe.nea.firmament.mixins.accessor; - -import net.minecraft.client.render.Camera; -import net.minecraft.client.render.GameRenderer; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Invoker; - -@Mixin(GameRenderer.class) -public interface AccessorGameRenderer { -    @Invoker("getFov") -    double getFov_firmament(Camera camera, float tickDelta, boolean changingFov); -} diff --git a/src/main/java/moe/nea/firmament/mixins/customgui/PatchHandledScreen.java b/src/main/java/moe/nea/firmament/mixins/customgui/PatchHandledScreen.java index deac0a4..814f172 100644 --- a/src/main/java/moe/nea/firmament/mixins/customgui/PatchHandledScreen.java +++ b/src/main/java/moe/nea/firmament/mixins/customgui/PatchHandledScreen.java @@ -14,7 +14,6 @@ import net.minecraft.client.gui.screen.ingame.HandledScreen;  import net.minecraft.screen.ScreenHandler;  import net.minecraft.screen.slot.Slot;  import net.minecraft.text.Text; -import net.minecraft.util.collection.DefaultedList;  import org.jetbrains.annotations.Nullable;  import org.spongepowered.asm.mixin.Final;  import org.spongepowered.asm.mixin.Mixin; @@ -88,29 +87,18 @@ public class PatchHandledScreen<T extends ScreenHandler> extends Screen implemen  	} -	@Unique -	private Slot didBeforeSlotRender; -  	@WrapOperation( -		method = "render", +		method = "drawSlots",  		at = @At(  			value = "INVOKE", -			target = "Lnet/minecraft/util/collection/DefaultedList;get(I)Ljava/lang/Object;")) -	private Object beforeSlotRender(DefaultedList instance, int index, Operation<Object> original, @Local(argsOnly = true) DrawContext context) { -		var slot = (Slot) original.call(instance, index); +			target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;drawSlot(Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/screen/slot/Slot;)V")) +	private void beforeSlotRender(HandledScreen instance, DrawContext context, Slot slot, Operation<Void> original) {  		if (override != null) { -			didBeforeSlotRender = slot;  			override.beforeSlotRender(context, slot);  		} -		return slot; -	} - -	@Inject(method = "render", -		at = @At(value = "INVOKE", target = "Lnet/minecraft/util/collection/DefaultedList;size()I")) -	private void afterSlotRender(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) { -		if (override != null && didBeforeSlotRender != null) { -			override.afterSlotRender(context, didBeforeSlotRender); -			didBeforeSlotRender = null; +		original.call(instance, context, slot); +		if (override != null) { +			override.afterSlotRender(context, slot);  		}  	} diff --git a/src/main/java/moe/nea/firmament/mixins/custommodels/ApplyHeadModelInItemRenderer.java b/src/main/java/moe/nea/firmament/mixins/custommodels/ApplyHeadModelInItemRenderer.java index 64b358f..dac65fe 100644 --- a/src/main/java/moe/nea/firmament/mixins/custommodels/ApplyHeadModelInItemRenderer.java +++ b/src/main/java/moe/nea/firmament/mixins/custommodels/ApplyHeadModelInItemRenderer.java @@ -1,33 +1,35 @@  package moe.nea.firmament.mixins.custommodels; -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;  import com.llamalad7.mixinextras.sugar.Local; +import com.llamalad7.mixinextras.sugar.ref.LocalRef;  import moe.nea.firmament.features.texturepack.BakedModelExtra; +import net.minecraft.client.render.VertexConsumerProvider;  import net.minecraft.client.render.item.ItemRenderer;  import net.minecraft.client.render.model.BakedModel; -import net.minecraft.client.render.model.json.ModelTransformationMode; -import net.minecraft.entity.LivingEntity; +import net.minecraft.client.util.math.MatrixStack;  import net.minecraft.item.ItemStack; -import net.minecraft.world.World; +import net.minecraft.item.ModelTransformationMode;  import org.spongepowered.asm.mixin.Mixin;  import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;  @Mixin(ItemRenderer.class)  public class ApplyHeadModelInItemRenderer { -    @WrapOperation(method = "renderItem(Lnet/minecraft/entity/LivingEntity;Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/render/model/json/ModelTransformationMode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;Lnet/minecraft/world/World;III)V", -        at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/item/ItemRenderer;getModel(Lnet/minecraft/item/ItemStack;Lnet/minecraft/world/World;Lnet/minecraft/entity/LivingEntity;I)Lnet/minecraft/client/render/model/BakedModel;")) -    private BakedModel applyHeadModel(ItemRenderer instance, ItemStack stack, World world, LivingEntity entity, int seed, Operation<BakedModel> original, -                                      @Local(argsOnly = true) ModelTransformationMode modelTransformationMode) { -        var model = original.call(instance, stack, world, entity, seed); -        if (modelTransformationMode == ModelTransformationMode.HEAD -            && model instanceof BakedModelExtra extra) { -            var headModel = extra.getHeadModel_firmament(); -            if (headModel != null) { -                model = headModel; -            } -        } -        return model; -    } +	@Inject(method = "renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ModelTransformationMode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;ZF)V", +		at = @At("HEAD")) +	private void applyHeadModel(ItemStack stack, ModelTransformationMode transformationMode, boolean leftHanded, +	                            MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, +	                            BakedModel model, boolean useInventoryModel, float z, CallbackInfo ci, +	                            @Local(argsOnly = true) LocalRef<BakedModel> modelMut +	) { +		var extra = BakedModelExtra.cast(model); +		if (transformationMode == ModelTransformationMode.HEAD && extra != null) { +			var headModel = extra.getHeadModel_firmament(); +			if (headModel != null) { +				modelMut.set(headModel); +			} +		} +	}  } diff --git a/src/main/java/moe/nea/firmament/mixins/custommodels/GlobalModelOverridePatch.java b/src/main/java/moe/nea/firmament/mixins/custommodels/GlobalModelOverridePatch.java index eee7557..c708862 100644 --- a/src/main/java/moe/nea/firmament/mixins/custommodels/GlobalModelOverridePatch.java +++ b/src/main/java/moe/nea/firmament/mixins/custommodels/GlobalModelOverridePatch.java @@ -8,6 +8,7 @@ import net.minecraft.client.render.model.BakedModel;  import net.minecraft.entity.LivingEntity;  import net.minecraft.item.ItemStack;  import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Final;  import org.spongepowered.asm.mixin.Mixin;  import org.spongepowered.asm.mixin.Shadow;  import org.spongepowered.asm.mixin.injection.At; @@ -17,13 +18,14 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;  @Mixin(ItemRenderer.class)  public abstract class GlobalModelOverridePatch { -    @Shadow -    public abstract ItemModels getModels(); +	@Shadow +	@Final +	private ItemModels models; -    @Inject(method = "getModel", at = @At("HEAD"), cancellable = true) -    private void overrideGlobalModel( -        ItemStack stack, World world, LivingEntity entity, -        int seed, CallbackInfoReturnable<BakedModel> cir) { -        CustomGlobalTextures.replaceGlobalModel(this.getModels(), stack, cir); -    } +	@Inject(method = "getModel(Lnet/minecraft/item/ItemStack;Lnet/minecraft/world/World;Lnet/minecraft/entity/LivingEntity;I)Lnet/minecraft/client/render/model/BakedModel;", at = @At("HEAD"), cancellable = true) +	private void overrideGlobalModel( +		ItemStack stack, World world, LivingEntity entity, +		int seed, CallbackInfoReturnable<BakedModel> cir) { +		CustomGlobalTextures.replaceGlobalModel(this.models, stack, cir); +	}  } diff --git a/src/main/java/moe/nea/firmament/mixins/custommodels/HeadModelReplacerPatch.java b/src/main/java/moe/nea/firmament/mixins/custommodels/HeadModelReplacerPatch.java new file mode 100644 index 0000000..26c331e --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/custommodels/HeadModelReplacerPatch.java @@ -0,0 +1,57 @@ + +package moe.nea.firmament.mixins.custommodels; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import moe.nea.firmament.features.texturepack.BakedModelExtra; +import net.minecraft.block.AbstractSkullBlock; +import net.minecraft.block.Block; +import net.minecraft.block.Blocks; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.entity.LivingEntityRenderer; +import net.minecraft.client.render.entity.feature.HeadFeatureRenderer; +import net.minecraft.client.render.entity.model.EntityModel; +import net.minecraft.client.render.entity.model.ModelWithHead; +import net.minecraft.client.render.entity.state.LivingEntityRenderState; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.item.BlockItem; +import net.minecraft.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(HeadFeatureRenderer.class) +public class HeadModelReplacerPatch<S extends LivingEntityRenderState, M extends EntityModel<S> & ModelWithHead> { +	/** +	 * This class serves to disable the replacing of head models with the vanilla block model. Vanilla first selects loads +	 * the model containing the head model regularly in {@link LivingEntityRenderer#updateRenderState}, but then discards +	 * the model in {@link HeadFeatureRenderer#render(MatrixStack, VertexConsumerProvider, int, LivingEntityRenderState, float, float)} +	 * if it detects a skull block. This serves to disable that functionality if a head model override is present. +	 */ +	@WrapOperation(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/client/render/entity/state/LivingEntityRenderState;FF)V", +		at = @At(value = "INVOKE", target = "Lnet/minecraft/item/BlockItem;getBlock()Lnet/minecraft/block/Block;")) +	private Block replaceSkull(BlockItem instance, Operation<Block> original, @Local BakedModel bakedModel) { +		var oldBlock = original.call(instance); +		if (oldBlock instanceof AbstractSkullBlock) { +			var extra = BakedModelExtra.cast(bakedModel); +			if (extra != null && extra.getHeadModel_firmament() != null) +				return Blocks.ENCHANTING_TABLE; // Any non skull block. Let's choose the enchanting table because it is very distinct. +		} +		return oldBlock; +	} + +	/** +	 * We disable the has model override, since texture packs get precedent to server data. +	 */ +	@WrapOperation(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/client/render/entity/state/LivingEntityRenderState;FF)V", +		at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/entity/feature/ArmorFeatureRenderer;hasModel(Lnet/minecraft/item/ItemStack;Lnet/minecraft/entity/EquipmentSlot;)Z")) +	private boolean replaceHasModel(ItemStack stack, EquipmentSlot slot, Operation<Boolean> original, +	                                @Local BakedModel bakedModel) { +		var extra = BakedModelExtra.cast(bakedModel); +		if (extra != null && extra.getHeadModel_firmament() != null) +			return false; +		return original.call(stack, slot); +	} +} diff --git a/src/main/java/moe/nea/firmament/mixins/custommodels/ItemRendererTintContextPatch.java b/src/main/java/moe/nea/firmament/mixins/custommodels/ItemRendererTintContextPatch.java index 5ed0fbe..8c5411b 100644 --- a/src/main/java/moe/nea/firmament/mixins/custommodels/ItemRendererTintContextPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/custommodels/ItemRendererTintContextPatch.java @@ -5,28 +5,30 @@ import moe.nea.firmament.features.texturepack.TintOverrides;  import net.minecraft.client.render.VertexConsumerProvider;  import net.minecraft.client.render.item.ItemRenderer;  import net.minecraft.client.render.model.BakedModel; -import net.minecraft.client.render.model.json.ModelTransformationMode;  import net.minecraft.client.util.math.MatrixStack;  import net.minecraft.item.ItemStack; +import net.minecraft.item.ModelTransformationMode;  import org.spongepowered.asm.mixin.Mixin;  import org.spongepowered.asm.mixin.injection.At;  import org.spongepowered.asm.mixin.injection.Inject;  import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(ItemRenderer.class) +@Mixin(value = ItemRenderer.class, priority = 1010)  public class ItemRendererTintContextPatch { -	@Inject(method = "renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/render/model/json/ModelTransformationMode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;)V", -		at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/model/BakedModel;getTransformation()Lnet/minecraft/client/render/model/json/ModelTransformation;"), allow = 1) -	private void onStartRendering(ItemStack stack, ModelTransformationMode renderMode, boolean leftHanded, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, BakedModel model, CallbackInfo ci) { -		if (model instanceof BakedModelExtra extra) { +	@Inject(method = "renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ModelTransformationMode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;ZF)V", +		at = @At(value = "HEAD"), allow = 1) +	private void onStartRendering(ItemStack stack, ModelTransformationMode transformationMode, boolean leftHanded, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, BakedModel model, boolean useInventoryModel, float z, CallbackInfo ci) { +		var extra = BakedModelExtra.cast(model); +		if (extra != null) {  			TintOverrides.Companion.enter(extra.getTintOverrides_firmament());  		}  	} -	@Inject(method = "renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/render/model/json/ModelTransformationMode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;)V", -		at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/math/MatrixStack;pop()V"), allow = 1) -	private void onEndRendering(ItemStack stack, ModelTransformationMode renderMode, boolean leftHanded, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, BakedModel model, CallbackInfo ci) { -		if (model instanceof BakedModelExtra extra) { +	@Inject(method = "renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ModelTransformationMode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;ZF)V", +		at = @At("TAIL"), allow = 1) +	private void onEndRendering(ItemStack stack, ModelTransformationMode transformationMode, boolean leftHanded, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, BakedModel model, boolean useInventoryModel, float z, CallbackInfo ci) { +		var extra = BakedModelExtra.cast(model); +		if (extra != null) {  			TintOverrides.Companion.exit(extra.getTintOverrides_firmament());  		}  	} diff --git a/src/main/java/moe/nea/firmament/mixins/custommodels/JsonUnbakedModelDataHolder.java b/src/main/java/moe/nea/firmament/mixins/custommodels/JsonUnbakedModelDataHolder.java index 20c69e2..a5bb34f 100644 --- a/src/main/java/moe/nea/firmament/mixins/custommodels/JsonUnbakedModelDataHolder.java +++ b/src/main/java/moe/nea/firmament/mixins/custommodels/JsonUnbakedModelDataHolder.java @@ -1,11 +1,11 @@  package moe.nea.firmament.mixins.custommodels; -import com.google.gson.annotations.SerializedName;  import com.llamalad7.mixinextras.injector.ModifyReturnValue;  import com.llamalad7.mixinextras.sugar.Local;  import moe.nea.firmament.features.texturepack.BakedModelExtra;  import moe.nea.firmament.features.texturepack.JsonUnbakedModelFirmExtra;  import moe.nea.firmament.features.texturepack.TintOverrides; +import moe.nea.firmament.util.ErrorUtil;  import net.minecraft.client.render.model.BakedModel;  import net.minecraft.client.render.model.Baker;  import net.minecraft.client.render.model.ModelRotation; @@ -18,15 +18,20 @@ import org.spongepowered.asm.mixin.Mixin;  import org.spongepowered.asm.mixin.Shadow;  import org.spongepowered.asm.mixin.Unique;  import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import java.util.Collection;  import java.util.Objects;  @Mixin(JsonUnbakedModel.class) -public class JsonUnbakedModelDataHolder implements JsonUnbakedModelFirmExtra { +public abstract class JsonUnbakedModelDataHolder implements JsonUnbakedModelFirmExtra {  	@Shadow  	@Nullable  	protected JsonUnbakedModel parent; + +	@Shadow +	public abstract String toString(); +  	@Unique  	@Nullable  	public Identifier headModel; @@ -67,31 +72,59 @@ public class JsonUnbakedModelDataHolder implements JsonUnbakedModelFirmExtra {  		return ((JsonUnbakedModelFirmExtra) this.parent).getHeadModel_firmament();  	} -	@ModifyReturnValue(method = "getModelDependencies", at = @At("RETURN")) -	private Collection<Identifier> addDependencies(Collection<Identifier> original) { +	@Inject(method = "resolve", at = @At("HEAD")) +	private void addDependencies(UnbakedModel.Resolver resolver, CallbackInfo ci) {  		var headModel = getHeadModel_firmament();  		if (headModel != null) { -			original.add(headModel); +			resolver.resolve(headModel);  		} -		return original;  	} -	@ModifyReturnValue( -		method = "bake(Lnet/minecraft/client/render/model/Baker;Lnet/minecraft/client/render/model/json/JsonUnbakedModel;Ljava/util/function/Function;Lnet/minecraft/client/render/model/ModelBakeSettings;Z)Lnet/minecraft/client/render/model/BakedModel;", -		at = @At(value = "RETURN")) -	private BakedModel bakeExtraInfo(BakedModel original, @Local(argsOnly = true) Baker baker) { -		if (original instanceof BakedModelExtra extra) { +	private void addExtraBakeInfo(BakedModel bakedModel, Baker baker) { +		if (!this.toString().contains("minecraft") && this.toString().contains("crimson")) { +			System.out.println("Found non minecraft model " + this); +		} +		var extra = BakedModelExtra.cast(bakedModel); +		if (extra != null) {  			var headModel = getHeadModel_firmament();  			if (headModel != null) { -				UnbakedModel unbakedModel = baker.getOrLoadModel(headModel); -				extra.setHeadModel_firmament( -					Objects.equals(unbakedModel, parent) -						? null -						: baker.bake(headModel, ModelRotation.X0_Y0)); +				extra.setHeadModel_firmament(baker.bake(headModel, ModelRotation.X0_Y0));  			} -			if (getTintOverrides_firmament().hasOverrides()) +			if (getTintOverrides_firmament().hasOverrides()) {  				extra.setTintOverrides_firmament(getTintOverrides_firmament()); +			} +		} +	} + +	/** +	 * @see ProvideBakerToJsonUnbakedModelPatch +	 */ +	@Override +	public void storeExtraBaker_firmament(@NotNull Baker baker) { +		this.storedBaker = baker; +	} + +	@Unique +	private Baker storedBaker; + +	@ModifyReturnValue( +		method = "bake(Ljava/util/function/Function;Lnet/minecraft/client/render/model/ModelBakeSettings;Z)Lnet/minecraft/client/render/model/BakedModel;", +		at = @At("RETURN")) +	private BakedModel bakeExtraInfoWithoutBaker(BakedModel original) { +		if (storedBaker != null) { +			addExtraBakeInfo(original, storedBaker); +			storedBaker = null;  		}  		return original;  	} + +	@ModifyReturnValue( +		method = { +			"bake(Lnet/minecraft/client/render/model/Baker;Ljava/util/function/Function;Lnet/minecraft/client/render/model/ModelBakeSettings;)Lnet/minecraft/client/render/model/BakedModel;" +		}, +		at = @At(value = "RETURN")) +	private BakedModel bakeExtraInfo(BakedModel original, @Local(argsOnly = true) Baker baker) { +		addExtraBakeInfo(original, baker); +		return original; +	}  } diff --git a/src/main/java/moe/nea/firmament/mixins/custommodels/PatchArmorTexture.java b/src/main/java/moe/nea/firmament/mixins/custommodels/PatchArmorTexture.java index 7c6f69e..4468150 100644 --- a/src/main/java/moe/nea/firmament/mixins/custommodels/PatchArmorTexture.java +++ b/src/main/java/moe/nea/firmament/mixins/custommodels/PatchArmorTexture.java @@ -6,26 +6,24 @@ import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;  import com.llamalad7.mixinextras.sugar.Local;  import moe.nea.firmament.features.texturepack.CustomGlobalArmorOverrides;  import net.minecraft.client.render.entity.feature.ArmorFeatureRenderer; -import net.minecraft.item.ArmorMaterial; +import net.minecraft.component.type.EquippableComponent;  import net.minecraft.item.ItemStack; +import net.minecraft.util.Identifier;  import org.spongepowered.asm.mixin.Mixin;  import org.spongepowered.asm.mixin.injection.At; -import java.util.List; +import java.util.Optional;  @Mixin(ArmorFeatureRenderer.class)  public class PatchArmorTexture { -    @WrapOperation( -        method = "renderArmor", -        at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ArmorMaterial;layers()Ljava/util/List;")) -    private List<ArmorMaterial.Layer> overrideLayers( -        ArmorMaterial instance, -        Operation<List<ArmorMaterial.Layer>> original, -        @Local ItemStack itemStack -    ) { -        var overrides = CustomGlobalArmorOverrides.overrideArmor(itemStack); -        if (overrides == null) -            return original.call(instance); -        return overrides; -    } +	@WrapOperation( +		method = "renderArmor", +		at = @At(value = "INVOKE", target = "Lnet/minecraft/component/type/EquippableComponent;model()Ljava/util/Optional;")) +	private Optional<Identifier> overrideLayers( +		EquippableComponent instance, Operation<Optional<Identifier>> original, @Local(argsOnly = true) ItemStack itemStack +	) { +		// TODO: check that all armour items are naturally equippable and have the equppable component. otherwise our call here will not be reached. +		var overrides = CustomGlobalArmorOverrides.overrideArmor(itemStack); +		return overrides.or(() -> original.call(instance)); +	}  } diff --git a/src/main/java/moe/nea/firmament/mixins/custommodels/PatchHeadFeatureRenderer.java b/src/main/java/moe/nea/firmament/mixins/custommodels/PatchHeadFeatureRenderer.java deleted file mode 100644 index 610a106..0000000 --- a/src/main/java/moe/nea/firmament/mixins/custommodels/PatchHeadFeatureRenderer.java +++ /dev/null @@ -1,45 +0,0 @@ - -package moe.nea.firmament.mixins.custommodels; - -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import com.llamalad7.mixinextras.sugar.Local; -import moe.nea.firmament.features.texturepack.BakedModelExtra; -import net.minecraft.block.AbstractSkullBlock; -import net.minecraft.block.Block; -import net.minecraft.block.Blocks; -import net.minecraft.client.render.entity.feature.HeadFeatureRenderer; -import net.minecraft.client.render.entity.model.EntityModel; -import net.minecraft.client.render.item.HeldItemRenderer; -import net.minecraft.entity.LivingEntity; -import net.minecraft.item.BlockItem; -import net.minecraft.item.ItemStack; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; - -@Mixin(HeadFeatureRenderer.class) -public class PatchHeadFeatureRenderer<T extends LivingEntity, M extends EntityModel<T>> { - -    @Shadow -    @Final -    private HeldItemRenderer heldItemRenderer; - -    @WrapOperation(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/entity/LivingEntity;FFFFFF)V", -        at = @At(value = "INVOKE", target = "Lnet/minecraft/item/BlockItem;getBlock()Lnet/minecraft/block/Block;")) -    private Block replaceSkull(BlockItem instance, Operation<Block> original, @Local ItemStack itemStack, @Local(argsOnly = true) T entity) { -        var oldBlock = original.call(instance); -        if (oldBlock instanceof AbstractSkullBlock) { -            var bakedModel = this.heldItemRenderer.itemRenderer -                .getModel(itemStack, entity.getWorld(), entity, 0); -            if (bakedModel instanceof BakedModelExtra extra && extra.getHeadModel_firmament() != null) -                return Blocks.ENCHANTING_TABLE; // Any non skull block. Let's choose the enchanting table because it is very distinct. -        } -        return oldBlock; -    } - - - - -} diff --git a/src/main/java/moe/nea/firmament/mixins/custommodels/PatchLegacyArmorLayerSupport.java b/src/main/java/moe/nea/firmament/mixins/custommodels/PatchLegacyArmorLayerSupport.java new file mode 100644 index 0000000..8c0b3f8 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/custommodels/PatchLegacyArmorLayerSupport.java @@ -0,0 +1,22 @@ +package moe.nea.firmament.mixins.custommodels; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import moe.nea.firmament.features.texturepack.CustomGlobalArmorOverrides; +import net.minecraft.client.render.entity.equipment.EquipmentModelLoader; +import net.minecraft.client.render.entity.equipment.EquipmentRenderer; +import net.minecraft.item.equipment.EquipmentModel; +import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(EquipmentRenderer.class) +public class PatchLegacyArmorLayerSupport { +	@WrapOperation(method = "render(Lnet/minecraft/item/equipment/EquipmentModel$LayerType;Lnet/minecraft/util/Identifier;Lnet/minecraft/client/model/Model;Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;ILnet/minecraft/util/Identifier;)V", +		at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/entity/equipment/EquipmentModelLoader;get(Lnet/minecraft/util/Identifier;)Lnet/minecraft/item/equipment/EquipmentModel;")) +	private EquipmentModel patchModelLayers(EquipmentModelLoader instance, Identifier id, Operation<EquipmentModel> original) { +		var modelOverride = CustomGlobalArmorOverrides.overrideArmorLayer(id); +		if (modelOverride != null) return modelOverride; +		return original.call(instance, id); +	} +} diff --git a/src/main/java/moe/nea/firmament/mixins/custommodels/PatchOverrideDeserializer.java b/src/main/java/moe/nea/firmament/mixins/custommodels/PatchOverrideDeserializer.java index 938d14d..abb1792 100644 --- a/src/main/java/moe/nea/firmament/mixins/custommodels/PatchOverrideDeserializer.java +++ b/src/main/java/moe/nea/firmament/mixins/custommodels/PatchOverrideDeserializer.java @@ -22,29 +22,29 @@ import java.util.Map;  @Mixin(ModelOverride.Deserializer.class)  public class PatchOverrideDeserializer { -    @ModifyReturnValue( -        method = "deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lnet/minecraft/client/render/model/json/ModelOverride;", -        at = @At(value = "RETURN")) -    private ModelOverride addCustomOverrides(ModelOverride original, @Local JsonObject jsonObject) { -        var originalData = (ModelOverrideData) original; -        originalData.setFirmamentOverrides(CustomModelOverrideParser.parseCustomModelOverrides(jsonObject)); -        return original; -    } +	@ModifyReturnValue( +		method = "deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lnet/minecraft/client/render/model/json/ModelOverride;", +		at = @At(value = "RETURN")) +	private ModelOverride addCustomOverrides(ModelOverride original, @Local JsonObject jsonObject) { +		var originalData = (ModelOverrideData) (Object) original; +		originalData.setFirmamentOverrides(CustomModelOverrideParser.parseCustomModelOverrides(jsonObject)); +		return original; +	} -    @ModifyExpressionValue( -        method = "deserializeMinPropertyValues(Lcom/google/gson/JsonObject;)Ljava/util/List;", -        at = @At(value = "INVOKE", target = "Ljava/util/Map$Entry;getValue()Ljava/lang/Object;")) -    private Object removeFirmamentPredicatesFromJsonIteration(Object original, @Local Map.Entry<String, JsonElement> entry) { -        if (entry.getKey().startsWith("firmament:")) return new JsonPrimitive(0F); -        return original; -    } +	@ModifyExpressionValue( +		method = "deserializeMinPropertyValues(Lcom/google/gson/JsonObject;)Ljava/util/List;", +		at = @At(value = "INVOKE", target = "Ljava/util/Map$Entry;getValue()Ljava/lang/Object;")) +	private Object removeFirmamentPredicatesFromJsonIteration(Object original, @Local Map.Entry<String, JsonElement> entry) { +		if (entry.getKey().startsWith("firmament:")) return new JsonPrimitive(0F); +		return original; +	} -    @Inject( -        method = "deserializeMinPropertyValues", -        at = @At(value = "INVOKE", target = "Ljava/util/Map;entrySet()Ljava/util/Set;") -    ) -    private void whatever(JsonObject object, CallbackInfoReturnable<List<ModelOverride.Condition>> cir, -                          @Local Map<Identifier, Float> maps) { -        maps.entrySet().removeIf(it -> it.getKey().getNamespace().equals("firmament")); -    } +	@Inject( +		method = "deserializeMinPropertyValues", +		at = @At(value = "INVOKE", target = "Ljava/util/Map;entrySet()Ljava/util/Set;") +	) +	private void whatever(JsonObject object, CallbackInfoReturnable<List<ModelOverride.Condition>> cir, +	                      @Local Map<Identifier, Float> maps) { +		maps.entrySet().removeIf(it -> it.getKey().getNamespace().equals("firmament")); +	}  } diff --git a/src/main/java/moe/nea/firmament/mixins/custommodels/ProvideBakerToJsonUnbakedModelPatch.java b/src/main/java/moe/nea/firmament/mixins/custommodels/ProvideBakerToJsonUnbakedModelPatch.java new file mode 100644 index 0000000..c1ac119 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/custommodels/ProvideBakerToJsonUnbakedModelPatch.java @@ -0,0 +1,27 @@ +package moe.nea.firmament.mixins.custommodels; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import moe.nea.firmament.features.texturepack.JsonUnbakedModelFirmExtra; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.render.model.Baker; +import net.minecraft.client.render.model.ModelBakeSettings; +import net.minecraft.client.render.model.json.JsonUnbakedModel; +import net.minecraft.client.texture.Sprite; +import net.minecraft.client.util.SpriteIdentifier; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import java.util.function.Function; + +/** + * @see JsonUnbakedModelDataHolder#storeExtraBaker_firmament + */ +@Mixin(targets = "net.minecraft.client.render.model.ModelBaker$BakerImpl") +public abstract class ProvideBakerToJsonUnbakedModelPatch implements Baker { +	@WrapOperation(method = "bake(Lnet/minecraft/client/render/model/UnbakedModel;Lnet/minecraft/client/render/model/ModelBakeSettings;)Lnet/minecraft/client/render/model/BakedModel;", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/model/json/JsonUnbakedModel;bake(Ljava/util/function/Function;Lnet/minecraft/client/render/model/ModelBakeSettings;Z)Lnet/minecraft/client/render/model/BakedModel;")) +	private BakedModel provideExtraBakerToModel(JsonUnbakedModel instance, Function<SpriteIdentifier, Sprite> function, ModelBakeSettings modelBakeSettings, boolean bl, Operation<BakedModel> original) { +		((JsonUnbakedModelFirmExtra) instance).storeExtraBaker_firmament(this); +		return original.call(instance, function, modelBakeSettings, bl); +	} +} diff --git a/src/main/java/moe/nea/firmament/mixins/custommodels/ReferenceCustomModelsPatch.java b/src/main/java/moe/nea/firmament/mixins/custommodels/ReferenceCustomModelsPatch.java new file mode 100644 index 0000000..14c84ab --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/custommodels/ReferenceCustomModelsPatch.java @@ -0,0 +1,37 @@ +package moe.nea.firmament.mixins.custommodels; + +import moe.nea.firmament.events.BakeExtraModelsEvent; +import net.minecraft.client.render.model.BlockStatesLoader; +import net.minecraft.client.render.model.ItemModel; +import net.minecraft.client.render.model.ReferencedModelsCollector; +import net.minecraft.client.render.model.UnbakedModel; +import net.minecraft.client.util.ModelIdentifier; +import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.Map; + +@Mixin(ReferencedModelsCollector.class) +public abstract class ReferenceCustomModelsPatch { +	@Shadow +	protected abstract void addTopLevelModel(ModelIdentifier modelId, UnbakedModel model); + +	@Shadow +	@Final +	private Map<Identifier, UnbakedModel> inputs; + +	@Inject(method = "addBlockStates", at = @At("RETURN")) +	private void addFirmamentReferencedModels( +		BlockStatesLoader.BlockStateDefinition definition, CallbackInfo ci +	) { +		inputs.keySet().stream().filter(it->it.toString().contains("firm")).forEach(System.out::println); +		BakeExtraModelsEvent.Companion.publish(new BakeExtraModelsEvent( +			(modelIdentifier, identifier) -> addTopLevelModel(modelIdentifier, new ItemModel(identifier)))); + +	} +} diff --git a/src/main/java/moe/nea/firmament/mixins/custommodels/TestForFirmamentOverridePredicatesPatch.java b/src/main/java/moe/nea/firmament/mixins/custommodels/TestForFirmamentOverridePredicatesPatch.java index bc47d74..81ae3b8 100644 --- a/src/main/java/moe/nea/firmament/mixins/custommodels/TestForFirmamentOverridePredicatesPatch.java +++ b/src/main/java/moe/nea/firmament/mixins/custommodels/TestForFirmamentOverridePredicatesPatch.java @@ -17,33 +17,33 @@ import org.spongepowered.asm.mixin.injection.ModifyArg;  @Mixin(ModelOverrideList.class)  public class TestForFirmamentOverridePredicatesPatch { -    @ModifyArg(method = "<init>(Lnet/minecraft/client/render/model/Baker;Lnet/minecraft/client/render/model/json/JsonUnbakedModel;Ljava/util/List;)V", -        at = @At( -            value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z" -        )) -    public Object onInit( -        Object element, -        @Local ModelOverride modelOverride -    ) { -        var bakedOverride = (ModelOverrideList.BakedOverride) element; -        ((BakedOverrideData) bakedOverride) -            .setFirmamentOverrides(((ModelOverrideData) modelOverride).getFirmamentOverrides()); -        return element; -    } +	@ModifyArg(method = "<init>(Lnet/minecraft/client/render/model/Baker;Ljava/util/List;)V", +		at = @At( +			value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z" +		)) +	public Object onInit( +		Object element, +		@Local ModelOverride modelOverride +	) { +		var bakedOverride = (ModelOverrideList.BakedOverride) element; +		((BakedOverrideData) (Object) bakedOverride) +			.setFirmamentOverrides(((ModelOverrideData) (Object) modelOverride).getFirmamentOverrides()); +		return element; +	} -    @ModifyExpressionValue(method = "apply", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/model/json/ModelOverrideList$BakedOverride;test([F)Z")) -    public boolean testFirmamentOverrides(boolean originalValue, -                                          @Local ModelOverrideList.BakedOverride bakedOverride, -                                          @Local(argsOnly = true) ItemStack stack) { -        if (!originalValue) return false; -        var overrideData = (BakedOverrideData) bakedOverride; -        var overrides = overrideData.getFirmamentOverrides(); -        if (overrides == null) return true; -        if (!CustomSkyBlockTextures.TConfig.INSTANCE.getEnableModelOverrides()) return false; -        for (FirmamentModelPredicate firmamentOverride : overrides) { -            if (!firmamentOverride.test(stack)) -                return false; -        } -        return true; -    } +	@ModifyExpressionValue(method = "getModel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/model/json/ModelOverrideList$BakedOverride;test([F)Z")) +	public boolean testFirmamentOverrides(boolean originalValue, +	                                      @Local ModelOverrideList.BakedOverride bakedOverride, +	                                      @Local(argsOnly = true) ItemStack stack) { +		if (!originalValue) return false; +		var overrideData = (BakedOverrideData) (Object) bakedOverride; +		var overrides = overrideData.getFirmamentOverrides(); +		if (overrides == null) return true; +		if (!CustomSkyBlockTextures.TConfig.INSTANCE.getEnableModelOverrides()) return false; +		for (FirmamentModelPredicate firmamentOverride : overrides) { +			if (!firmamentOverride.test(stack)) +				return false; +		} +		return true; +	}  } 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() +		} +	}  } diff --git a/src/main/resources/assets/minecraft/shaders/core/firmament_rendertype_lines.fsh b/src/main/resources/assets/firmament/shaders/core/rendertype_lines.fsh index 057f31f..057f31f 100644 --- a/src/main/resources/assets/minecraft/shaders/core/firmament_rendertype_lines.fsh +++ b/src/main/resources/assets/firmament/shaders/core/rendertype_lines.fsh diff --git a/src/main/resources/assets/minecraft/shaders/core/firmament_rendertype_lines.json b/src/main/resources/assets/firmament/shaders/core/rendertype_lines.json index 0828480..e4537ca 100644 --- a/src/main/resources/assets/minecraft/shaders/core/firmament_rendertype_lines.json +++ b/src/main/resources/assets/firmament/shaders/core/rendertype_lines.json @@ -1,6 +1,6 @@  { -    "vertex": "firmament_rendertype_lines", -    "fragment": "firmament_rendertype_lines", +    "vertex": "firmament:core/rendertype_lines", +    "fragment": "firmament:core/rendertype_lines",      "samplers": [      ],      "uniforms": [ @@ -8,7 +8,7 @@          { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },          { "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] },          { "name": "LineWidth", "type": "float", "count": 1, "values": [ 1.0 ] }, -        { "name": "ScreenSize", "type": "float",     "count": 2,  "values": [ 1.0, 1.0 ] }, +        { "name": "ScreenSize", "type": "float", "count": 2, "values": [ 1.0, 1.0 ] },          { "name": "FogStart", "type": "float", "count": 1, "values": [ 0.0 ] },          { "name": "FogEnd", "type": "float", "count": 1, "values": [ 1.0 ] },          { "name": "FogColor", "type": "float", "count": 4, "values": [ 0.0, 0.0, 0.0, 0.0 ] }, diff --git a/src/main/resources/assets/minecraft/shaders/core/firmament_rendertype_lines.vsh b/src/main/resources/assets/firmament/shaders/core/rendertype_lines.vsh index b2d0f99..b2d0f99 100644 --- a/src/main/resources/assets/minecraft/shaders/core/firmament_rendertype_lines.vsh +++ b/src/main/resources/assets/firmament/shaders/core/rendertype_lines.vsh diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 9997018..9b00adf 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -33,6 +33,9 @@          "rei_client": [              "moe.nea.firmament.compat.rei.FirmamentReiPlugin"          ], +		"rei_common": [ +			"moe.nea.firmament.compat.rei.FirmamentReiCommonPlugin" +		],          "modmenu": [              "moe.nea.firmament.compat.modmenu.FirmamentModMenuPlugin"          ], diff --git a/src/main/resources/firmament.accesswidener b/src/main/resources/firmament.accesswidener index 60b31e3..f1a897b 100644 --- a/src/main/resources/firmament.accesswidener +++ b/src/main/resources/firmament.accesswidener @@ -5,6 +5,7 @@ accessible class net/minecraft/client/font/TextRenderer$Drawer  accessible field net/minecraft/client/gui/hud/InGameHud SCOREBOARD_ENTRY_COMPARATOR Ljava/util/Comparator;  accessible field net/minecraft/client/render/item/HeldItemRenderer itemRenderer Lnet/minecraft/client/render/item/ItemRenderer; +accessible field net/minecraft/client/render/item/ItemModels missingModelSupplier Ljava/util/function/Supplier;  accessible class net/minecraft/client/render/model/json/ModelOverride$Deserializer  accessible class net/minecraft/client/render/model/json/ModelOverrideList$BakedOverride @@ -14,7 +15,7 @@ accessible field net/minecraft/entity/passive/AbstractHorseEntity items Lnet/min  accessible field net/minecraft/entity/passive/AbstractHorseEntity SADDLED_FLAG I  accessible field net/minecraft/entity/passive/AbstractHorseEntity HORSE_FLAGS Lnet/minecraft/entity/data/TrackedData;  accessible method net/minecraft/resource/NamespaceResourceManager loadMetadata (Lnet/minecraft/resource/InputSupplier;)Lnet/minecraft/resource/metadata/ResourceMetadata; -accessible method net/minecraft/client/gui/DrawContext drawTexturedQuad (Lnet/minecraft/util/Identifier;IIIIIFFFFFFFF)V +accessible method net/minecraft/client/gui/DrawContext drawTexturedQuad (Ljava/util/function/Function;Lnet/minecraft/util/Identifier;IIIIFFFFI)V  mutable field net/minecraft/screen/slot/Slot x I  mutable field net/minecraft/screen/slot/Slot y I @@ -27,3 +28,4 @@ accessible method net/minecraft/registry/entry/RegistryEntry$Reference setRegist  accessible method net/minecraft/entity/LivingEntity getHitbox ()Lnet/minecraft/util/math/Box;  accessible method net/minecraft/registry/entry/RegistryEntryList$Named <init> (Lnet/minecraft/registry/entry/RegistryEntryOwner;Lnet/minecraft/registry/tag/TagKey;)V  accessible method net/minecraft/registry/entry/RegistryEntry$Reference setValue (Ljava/lang/Object;)V +accessible field net/minecraft/client/render/model/WrapperBakedModel wrapped Lnet/minecraft/client/render/model/BakedModel; diff --git a/src/main/resources/firmament.mixins.json b/src/main/resources/firmament.mixins.json index dbb8290..d78d124 100644 --- a/src/main/resources/firmament.mixins.json +++ b/src/main/resources/firmament.mixins.json @@ -1,7 +1,10 @@  { -    "required": true, -    "plugin": "moe.nea.firmament.init.MixinPlugin", -    "package": "moe.nea.firmament.mixins", -    "compatibilityLevel": "JAVA_21", -    "refmap": "Firmament-refmap.json" +	"required": true, +	"plugin": "moe.nea.firmament.init.MixinPlugin", +	"package": "moe.nea.firmament.mixins", +	"compatibilityLevel": "JAVA_21", +	"refmap": "Firmament-refmap.json", +	"injectors": { +		"defaultRequire": 1 +	}  } | 
