feat: Add block breaking indicators to jade

This commit is contained in:
Linnea Gräf
2025-03-07 00:21:38 +01:00
parent 6ad259ca40
commit b4a93bd751
10 changed files with 101 additions and 39 deletions

View File

@@ -4,8 +4,11 @@ import snownee.jade.api.BlockAccessor
import snownee.jade.api.IBlockComponentProvider import snownee.jade.api.IBlockComponentProvider
import snownee.jade.api.ITooltip import snownee.jade.api.ITooltip
import snownee.jade.api.config.IPluginConfig import snownee.jade.api.config.IPluginConfig
import net.minecraft.block.BlockState
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos
import moe.nea.firmament.Firmament import moe.nea.firmament.Firmament
import moe.nea.firmament.util.MC
import moe.nea.firmament.util.tr import moe.nea.firmament.util.tr
object CustomMiningHardnessProvider : IBlockComponentProvider { object CustomMiningHardnessProvider : IBlockComponentProvider {
@@ -22,4 +25,30 @@ object CustomMiningHardnessProvider : IBlockComponentProvider {
override fun getUid(): Identifier = override fun getUid(): Identifier =
Firmament.identifier("custom_mining_hardness") Firmament.identifier("custom_mining_hardness")
data class BreakingInfo(
val blockPos: BlockPos, val stage: Int,
val state: BlockState?,
)
var currentBreakingInfo: BreakingInfo? = null
@JvmStatic
fun setBreakingInfo(blockPos: BlockPos, stage: Int) {
currentBreakingInfo = BreakingInfo(blockPos.toImmutable(), stage, MC.world?.getBlockState(blockPos))
}
@JvmStatic
fun replaceBreakProgress(original: Float): Float {
if (!isOnMiningIsland()) return original
val pos = MC.interactionManager?.currentBreakingPos ?: return original
val info = currentBreakingInfo
if (info?.blockPos != pos || info.state != MC.world?.getBlockState(pos)) {
currentBreakingInfo = null
return 0F
}
// TODO: use linear extrapolation to guess how quickly this progresses between stages.
// This would only be possible after one stage, but should make the remaining stages a bit smoother
return info.stage / 10F
}
} }

View File

@@ -44,7 +44,7 @@ class DrillToolProvider : IBlockComponentProvider {
if (it.isEmpty()) Optional.empty() else Optional.of(true) if (it.isEmpty()) Optional.empty() else Optional.of(true)
}.getOrDefault(false) }.getOrDefault(false)
} }
val canHarvest = (SBItemStack(MC.stackInHand).neuItem?.breakingPower ?: 0) >= customBlock.breakingPower val canHarvest = SBItemStack(MC.stackInHand).breakingPower >= customBlock.breakingPower
val lastItem = innerMut[lastItemIndex] as ItemStackElement val lastItem = innerMut[lastItemIndex] as ItemStackElement
if (harvestIndicator < 0) { if (harvestIndicator < 0) {
innerMut.add(lastItemIndex + 1, canHarvestIndicator(canHarvest, lastItem.alignment)) innerMut.add(lastItemIndex + 1, canHarvestIndicator(canHarvest, lastItem.alignment))

View File

@@ -18,7 +18,6 @@ class FirmamentJadePlugin : IWailaPlugin {
override fun registerClient(registration: IWailaClientRegistration) { override fun registerClient(registration: IWailaClientRegistration) {
registration.registerBlockComponent(CustomMiningHardnessProvider, Block::class.java) registration.registerBlockComponent(CustomMiningHardnessProvider, Block::class.java)
registration.registerProgressClient(SkyblockProgressProvider())
registration.registerBlockComponent(DrillToolProvider(), Block::class.java) registration.registerBlockComponent(DrillToolProvider(), Block::class.java)
registration.addRayTraceCallback(CustomFakeBlockProvider(registration)) registration.addRayTraceCallback(CustomFakeBlockProvider(registration))
} }

View File

@@ -1,34 +0,0 @@
package moe.nea.firmament.compat.jade
import java.util.function.BiConsumer
import snownee.jade.api.Accessor
import snownee.jade.api.view.ClientViewGroup
import snownee.jade.api.view.IClientExtensionProvider
import snownee.jade.api.view.ProgressView
import snownee.jade.api.view.ViewGroup
import net.minecraft.text.Text
import net.minecraft.util.Identifier
class SkyblockProgressProvider : IClientExtensionProvider<ProgressView.Data, ProgressView> {
// wtf does this do i think its for the little progress bar which breaks in mining fatigue mining system
// but like this is just copied from the example plugin soo
// this is different from the toolhandler/toolprovider, this one adjusts the mining progress bar to
// adjust with skyblock's nms packet fuckery (see pr for references on how those packets probably work)
// so yeah we need to fix that.
// TODO :3 lol
override fun getClientGroups(accessor: Accessor<*>, groups: List<ViewGroup<ProgressView.Data>>): List<ClientViewGroup<ProgressView>> {
return ClientViewGroup.map(groups, ProgressView::read,
BiConsumer { group: ViewGroup<ProgressView.Data>, clientGroup: ClientViewGroup<ProgressView> ->
var view = clientGroup.views.first()
view.style.color(-0x340000)
view.text = Text.literal("e")
view = clientGroup.views[1]
view.style.color(-0xff3400)
view.text = Text.literal("!!")
})
}
override fun getUid(): Identifier {
return "progress".jadeId()
}
}

View File

@@ -0,0 +1,22 @@
package moe.nea.firmament.mixins.compat.jade;
import moe.nea.firmament.compat.jade.CustomMiningHardnessProvider;
import moe.nea.firmament.util.MC;
import net.minecraft.client.render.WorldRenderer;
import net.minecraft.util.math.BlockPos;
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.Objects;
@Mixin(WorldRenderer.class)
public class OnUpdateBreakProgress {
@Inject(method = "setBlockBreakingInfo", at = @At("HEAD"))
private void replaceBreakProgress(int entityId, BlockPos pos, int stage, CallbackInfo ci) {
if (entityId == 0 && null != MC.INSTANCE.getInteractionManager() && Objects.equals(MC.INSTANCE.getInteractionManager().currentBreakingPos, pos)) {
CustomMiningHardnessProvider.setBreakingInfo(pos, stage);
}
}
}

View File

@@ -0,0 +1,20 @@
package moe.nea.firmament.mixins.compat.jade;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import moe.nea.firmament.compat.jade.CustomMiningHardnessProvider;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import snownee.jade.JadeClient;
@Mixin(JadeClient.class)
public class PatchBreakingBarSpeedJade {
@ModifyExpressionValue(
method = "drawBreakingProgress",
at = @At(value = "FIELD", target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;currentBreakingProgress:F", opcode = Opcodes.GETFIELD)
)
private static float replaceBlockBreakingProgress(float original) {
return CustomMiningHardnessProvider.replaceBreakProgress(original);
}
// TODO: given the inherent roughness of the server provided stages, i don't feel the need to also patch the accesses to the delta provided by the block state. if i ever get around to adding the linear extrapolation, i should also patch that extrapolation to use the better one provided by the server stages.
}

View File

@@ -0,0 +1,17 @@
package moe.nea.firmament.mixins.accessor;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import net.minecraft.client.render.WorldRenderer;
import net.minecraft.entity.player.BlockBreakingInfo;
import org.jetbrains.annotations.NotNull;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.SortedSet;
@Mixin(WorldRenderer.class)
public interface AccessorWorldRenderer {
@Accessor("blockBreakingProgressions")
@NotNull
Long2ObjectMap<SortedSet<BlockBreakingInfo>> getBlockBreakingProgressions_firmament();
}

View File

@@ -81,6 +81,7 @@ data class SBItemStack constructor(
} }
val EMPTY = SBItemStack(SkyblockId.NULL, 0) val EMPTY = SBItemStack(SkyblockId.NULL, 0)
private val BREAKING_POWER_REGEX = "Breaking Power (?<power>[0-9]+)".toPattern()
operator fun invoke(itemStack: ItemStack): SBItemStack { operator fun invoke(itemStack: ItemStack): SBItemStack {
val skyblockId = itemStack.skyBlockId ?: SkyblockId.NULL val skyblockId = itemStack.skyBlockId ?: SkyblockId.NULL
return SBItemStack( return SBItemStack(
@@ -349,6 +350,12 @@ data class SBItemStack constructor(
private var itemStack_: ItemStack? = null private var itemStack_: ItemStack? = null
val breakingPower: Int
get() =
BREAKING_POWER_REGEX.useMatch(asImmutableItemStack().loreAccordingToNbt.firstOrNull()?.string) {
group("power").toInt()
} ?: 0
private val itemStack: ItemStack private val itemStack: ItemStack
get() { get() {
val itemStack = itemStack_ ?: run { val itemStack = itemStack_ ?: run {

View File

@@ -16,12 +16,13 @@ import kotlin.time.Duration.Companion.seconds
inline fun <T> String.ifMatches(regex: Regex, block: (MatchResult) -> T): T? = inline fun <T> String.ifMatches(regex: Regex, block: (MatchResult) -> T): T? =
regex.matchEntire(this)?.let(block) regex.matchEntire(this)?.let(block)
inline fun <T> Pattern.useMatch(string: String, block: Matcher.() -> T): T? { inline fun <T> Pattern.useMatch(string: String?, block: Matcher.() -> T): T? {
contract { contract {
callsInPlace(block, InvocationKind.AT_MOST_ONCE) callsInPlace(block, InvocationKind.AT_MOST_ONCE)
} }
return matcher(string) return string
.takeIf(Matcher::matches) ?.let(this::matcher)
?.takeIf(Matcher::matches)
?.let(block) ?.let(block)
} }

View File

@@ -25,3 +25,4 @@ accessible field net/minecraft/client/render/OverlayTexture texture Lnet/minecra
accessible method net/minecraft/client/render/RenderPhase$Texture getId ()Ljava/util/Optional; accessible method net/minecraft/client/render/RenderPhase$Texture getId ()Ljava/util/Optional;
accessible field net/minecraft/client/render/RenderLayer$MultiPhase phases Lnet/minecraft/client/render/RenderLayer$MultiPhaseParameters; accessible field net/minecraft/client/render/RenderLayer$MultiPhase phases Lnet/minecraft/client/render/RenderLayer$MultiPhaseParameters;
accessible field net/minecraft/client/render/RenderLayer$MultiPhaseParameters texture Lnet/minecraft/client/render/RenderPhase$TextureBase; accessible field net/minecraft/client/render/RenderLayer$MultiPhaseParameters texture Lnet/minecraft/client/render/RenderPhase$TextureBase;
accessible field net/minecraft/client/network/ClientPlayerInteractionManager currentBreakingPos Lnet/minecraft/util/math/BlockPos;