add support for shader wind effects

This commit is contained in:
octarine-noise
2021-04-27 18:13:57 +02:00
parent 594db19bfb
commit 8d9214c190
11 changed files with 100 additions and 85 deletions

View File

@@ -4,6 +4,7 @@ import me.zeroeightsix.fiber.JanksonSettings
import mods.betterfoliage.chunk.ChunkOverlayManager import mods.betterfoliage.chunk.ChunkOverlayManager
import mods.betterfoliage.config.BlockConfig import mods.betterfoliage.config.BlockConfig
import mods.betterfoliage.config.MainConfig import mods.betterfoliage.config.MainConfig
import mods.betterfoliage.render.ShadersModIntegration
import mods.betterfoliage.render.block.vanilla.* import mods.betterfoliage.render.block.vanilla.*
import mods.betterfoliage.render.particle.LeafParticleRegistry import mods.betterfoliage.render.particle.LeafParticleRegistry
import mods.betterfoliage.render.particle.RisingSoulParticle import mods.betterfoliage.render.particle.RisingSoulParticle
@@ -83,6 +84,7 @@ object BetterFoliage : ClientModInitializer {
MyceliumModel.Companion MyceliumModel.Companion
NetherrackModel.Companion NetherrackModel.Companion
RisingSoulParticle.Companion RisingSoulParticle.Companion
ShadersModIntegration
} }
} }

View File

@@ -1,29 +0,0 @@
package mods.betterfoliage
// Optifine
//val OptifineClassTransformer = ClassRefOld<Any>("optifine.OptiFineClassTransformer")
//val BlockPosM = ClassRefOld<Any>("net.optifine.BlockPosM")
//object ChunkCacheOF : ClassRefOld<Any>("net.optifine.override.ChunkCacheOF") {
// val chunkCache = FieldRefOld(this, "chunkCache", ChunkRendererRegion)
//}
//object RenderEnv : ClassRefOld<Any>("net.optifine.render.RenderEnv") {
// val reset = MethodRefOld(this, "reset", void, BlockState, BlockPos)
//}
// Optifine custom colors
//val IColorizer = ClassRefOld<Any>("net.optifine.CustomColors\$IColorizer")
//object CustomColors : ClassRefOld<Any>("net.optifine.CustomColors") {
// val getColorMultiplier = MethodRefOld(this, "getColorMultiplier", int, BakedQuad, BlockState, ExtendedBlockView, BlockPos, RenderEnv)
//}
// Optifine shaders
//object SVertexBuilder : ClassRefOld<Any>("net.optifine.shaders.SVertexBuilder") {
// val pushState = MethodRefOld(this, "pushEntity", void, BlockState, BlockPos, ExtendedBlockView, BufferBuilder)
// val pushNum = MethodRefOld(this, "pushEntity", void, long)
// val pop = MethodRefOld(this, "popEntity", void)
//}

View File

@@ -39,6 +39,7 @@ class LeavesConfig(node: ConfigNode) : DelegatingConfigGroup(node) {
val hOffset by double(0.2, min = 0.0, max = 0.4, langKey = recurring) val hOffset by double(0.2, min = 0.0, max = 0.4, langKey = recurring)
val vOffset by double(0.1, min = 0.0, max = 0.4, langKey = recurring) val vOffset by double(0.1, min = 0.0, max = 0.4, langKey = recurring)
val size by double(1.4, min = 0.75, max = 2.5, langKey = recurring) val size by double(1.4, min = 0.75, max = 2.5, langKey = recurring)
val shaderWind by boolean(true, langKey = recurring)
} }
class ShortGrassConfig(node: ConfigNode) : DelegatingConfigGroup(node), PopulationConfigData { class ShortGrassConfig(node: ConfigNode) : DelegatingConfigGroup(node), PopulationConfigData {
@@ -86,6 +87,7 @@ class LilypadConfig(node: ConfigNode) : DelegatingConfigGroup(node), PopulationC
override val enabled by boolean(true, langKey = recurring) override val enabled by boolean(true, langKey = recurring)
val hOffset by double(0.1, min = 0.0, max = 0.25, langKey = recurring) val hOffset by double(0.1, min = 0.0, max = 0.25, langKey = recurring)
override val population by population(16) override val population by population(16)
val shaderWind by boolean(true, langKey = recurring)
} }
class ReedConfig(node: ConfigNode) : DelegatingConfigGroup(node), PopulationConfigData { class ReedConfig(node: ConfigNode) : DelegatingConfigGroup(node), PopulationConfigData {

View File

@@ -1,64 +1,54 @@
package mods.betterfoliage.render package mods.betterfoliage.render
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.util.get import mods.betterfoliage.render.lighting.getBufferBuilder
import net.minecraft.block.BlockRenderType import mods.betterfoliage.util.getAllMethods
import net.minecraft.block.BlockRenderType.MODEL import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
import net.minecraft.block.BlockRenderLayer
import net.minecraft.block.BlockRenderLayer.CUTOUT_MIPPED
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.block.Blocks import net.minecraft.block.Blocks
import net.minecraft.util.math.BlockPos import net.minecraft.client.render.BufferBuilder
import net.minecraft.world.ExtendedBlockView
import org.apache.logging.log4j.Level.INFO
/** /**
* Integration for ShadersMod. * Integration for ShadersMod.
*/ */
/*
object ShadersModIntegration { object ShadersModIntegration {
@JvmStatic val isAvailable = allAvailable(SVertexBuilder, SVertexBuilder.pushState, SVertexBuilder.pushNum, SVertexBuilder.pop) val BufferBuilder_SVertexBuilder = BufferBuilder::class.java.fields.find { it.name == "sVertexBuilder" }
val SVertexBuilder_pushState = getAllMethods("net.optifine.shaders.SVertexBuilder", "pushEntity").find { it.parameterCount == 1 }
val SVertexBuilder_popState = getAllMethods("net.optifine.shaders.SVertexBuilder", "popEntity").find { it.parameterCount == 0 }
val BlockAliases_getAliasBlockId = getAllMethods("net.optifine.shaders.BlockAliases", "getAliasBlockId").firstOrNull()
@JvmStatic val isAvailable =
listOf(BufferBuilder_SVertexBuilder).all { it != null } &&
listOf(SVertexBuilder_pushState, SVertexBuilder_popState, BlockAliases_getAliasBlockId).all { it != null }
val defaultLeaves = Blocks.OAK_LEAVES.defaultState val defaultLeaves = Blocks.OAK_LEAVES.defaultState
val defaultGrass = Blocks.TALL_GRASS.defaultState val defaultGrass = Blocks.TALL_GRASS.defaultState
/**
* Called from transformed ShadersMod code.
* @see mods.betterfoliage.loader.BetterFoliageTransformer
*/
@JvmStatic fun getBlockStateOverride(state: BlockState, world: ExtendedBlockView, pos: BlockPos): BlockState {
// if (LeafRegistry[state, world, pos] != null) return defaultLeaves
if (BetterFoliage.blockConfig.crops.matchesClass(state.block)) return defaultGrass
return state
}
init { init {
BetterFoliage.log(INFO, "ShadersMod integration is ${if (isAvailable) "enabled" else "disabled" }") BetterFoliage.logger.info("[BetterFoliage] ShadersMod integration is ${if (isAvailable) "enabled" else "disabled" }")
} }
inline fun renderAs(ctx: CombinedContext, renderType: BlockRenderType, enabled: Boolean = true, func: ()->Unit) =
renderAs(ctx, ctx.state, renderType, enabled, func)
/** Quads rendered inside this block will use the given block entity data in shader programs. */ /** Quads rendered inside this block will use the given block entity data in shader programs. */
inline fun renderAs(ctx: CombinedContext, state: BlockState, renderType: BlockRenderType, enabled: Boolean = true, func: ()->Unit) { inline fun renderAs(ctx: RenderContext, state: BlockState, layer: BlockRenderLayer, enabled: Boolean = true, func: ()->Unit) {
if (isAvailable && enabled) { if (isAvailable && enabled) {
val buffer = ctx.renderCtx.renderBuffer val sVertexBuilder = BufferBuilder_SVertexBuilder!!.get(ctx.getBufferBuilder(layer))
val sVertexBuilder = buffer[BufferBuilder_sVertexBuilder] val aliasBlockId = BlockAliases_getAliasBlockId!!.invoke(null, state)
SVertexBuilder.pushState.invoke(sVertexBuilder!!, ctx.state, ctx.pos, ctx.world, buffer) SVertexBuilder_pushState!!.invoke(sVertexBuilder, aliasBlockId)
func() func()
SVertexBuilder.pop.invoke(sVertexBuilder) SVertexBuilder_popState!!.invoke(sVertexBuilder)
} else { } else {
func() func()
} }
} }
/** Quads rendered inside this block will behave as tallgrass blocks in shader programs. */ /** Quads rendered inside this block will behave as tallgrass blocks in shader programs. */
inline fun grass(ctx: CombinedContext, enabled: Boolean = true, func: ()->Unit) = inline fun grass(ctx: RenderContext, enabled: Boolean = true, func: ()->Unit) =
renderAs(ctx, defaultGrass, MODEL, enabled, func) renderAs(ctx, defaultGrass, CUTOUT_MIPPED, enabled, func)
/** Quads rendered inside this block will behave as leaf blocks in shader programs. */ /** Quads rendered inside this block will behave as leaf blocks in shader programs. */
inline fun leaves(ctx: CombinedContext, enabled: Boolean = true, func: ()->Unit) = inline fun leaves(ctx: RenderContext, enabled: Boolean = true, func: ()->Unit) =
renderAs(ctx, defaultLeaves, MODEL, enabled, func) renderAs(ctx, defaultLeaves, CUTOUT_MIPPED, enabled, func)
} }
*/

View File

@@ -4,6 +4,7 @@ import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.chunk.BasicBlockCtx import mods.betterfoliage.chunk.BasicBlockCtx
import mods.betterfoliage.render.DIRT_BLOCKS import mods.betterfoliage.render.DIRT_BLOCKS
import mods.betterfoliage.render.SALTWATER_BIOMES import mods.betterfoliage.render.SALTWATER_BIOMES
import mods.betterfoliage.render.ShadersModIntegration
import mods.betterfoliage.render.lighting.withLighting import mods.betterfoliage.render.lighting.withLighting
import mods.betterfoliage.render.lighting.grassTuftLighting import mods.betterfoliage.render.lighting.grassTuftLighting
import mods.betterfoliage.render.lighting.reedLighting import mods.betterfoliage.render.lighting.reedLighting
@@ -66,12 +67,16 @@ class DirtModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
val random = randomSupplier.get() val random = randomSupplier.get()
if (BetterFoliage.config.algae.enabled(random) && isDeepWater) { if (BetterFoliage.config.algae.enabled(random) && isDeepWater) {
context.withLighting(algaeLighting) { ShadersModIntegration.grass(context, BetterFoliage.config.algae.shaderWind) {
it.accept(algaeModels[random]) context.withLighting(algaeLighting) {
it.accept(algaeModels[random])
}
} }
} else if (BetterFoliage.config.reed.enabled(random) && isShallowWater && !isSaltWater) { } else if (BetterFoliage.config.reed.enabled(random) && isShallowWater && !isSaltWater) {
context.withLighting(reedLighting) { ShadersModIntegration.grass(context, BetterFoliage.config.reed.shaderWind) {
it.accept(reedModels[random]) context.withLighting(reedLighting) {
it.accept(reedModels[random])
}
} }
} }
} }

View File

@@ -3,6 +3,7 @@ package mods.betterfoliage.render.block.vanilla
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.chunk.BasicBlockCtx import mods.betterfoliage.chunk.BasicBlockCtx
import mods.betterfoliage.render.SNOW_MATERIALS import mods.betterfoliage.render.SNOW_MATERIALS
import mods.betterfoliage.render.ShadersModIntegration
import mods.betterfoliage.render.lighting.withLighting import mods.betterfoliage.render.lighting.withLighting
import mods.betterfoliage.render.lighting.grassTuftLighting import mods.betterfoliage.render.lighting.grassTuftLighting
import mods.betterfoliage.util.Atlas import mods.betterfoliage.util.Atlas
@@ -78,8 +79,10 @@ class GrassBlockModel(val key: Key, wrapped: BakedModel) : WrappedBakedModel(wra
} }
if (BetterFoliage.config.shortGrass.enabled(random) && !ctx.isNeighborSolid(UP)) { if (BetterFoliage.config.shortGrass.enabled(random) && !ctx.isNeighborSolid(UP)) {
context.withLighting(tuftLighting) { ShadersModIntegration.grass(context, BetterFoliage.config.shortGrass.shaderWind) {
it.accept(if (isSnowed) tuftSnowed[random] else tuftNormal[random]) context.withLighting(tuftLighting) {
it.accept(if (isSnowed) tuftSnowed[random] else tuftNormal[random])
}
} }
} }
} }

View File

@@ -1,8 +1,11 @@
package mods.betterfoliage.render.block.vanilla package mods.betterfoliage.render.block.vanilla
import it.unimi.dsi.fastutil.ints.Int2ObjectFunction
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.chunk.BasicBlockCtx import mods.betterfoliage.chunk.BasicBlockCtx
import mods.betterfoliage.render.SNOW_MATERIALS import mods.betterfoliage.render.SNOW_MATERIALS
import mods.betterfoliage.render.ShadersModIntegration
import mods.betterfoliage.render.lighting.getBufferBuilder
import mods.betterfoliage.render.lighting.withLighting import mods.betterfoliage.render.lighting.withLighting
import mods.betterfoliage.render.lighting.roundLeafLighting import mods.betterfoliage.render.lighting.roundLeafLighting
import mods.betterfoliage.render.particle.LeafParticleRegistry import mods.betterfoliage.render.particle.LeafParticleRegistry
@@ -13,6 +16,11 @@ import mods.betterfoliage.resource.model.*
import mods.betterfoliage.util.* import mods.betterfoliage.util.*
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
import net.fabricmc.fabric.impl.client.indigo.renderer.accessor.AccessBufferBuilder
import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderInfo
import net.fabricmc.fabric.impl.client.indigo.renderer.render.TerrainMeshConsumer
import net.fabricmc.fabric.impl.client.indigo.renderer.render.TerrainRenderContext
import net.minecraft.block.BlockRenderLayer
import net.minecraft.block.BlockRenderLayer.CUTOUT_MIPPED import net.minecraft.block.BlockRenderLayer.CUTOUT_MIPPED
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.client.render.model.BakedModel import net.minecraft.client.render.model.BakedModel
@@ -77,17 +85,19 @@ class NormalLeavesModel(val key: Key, wrapped: BakedModel) : WrappedBakedModel(w
val leafLighting = roundLeafLighting() val leafLighting = roundLeafLighting()
override fun emitBlockQuads(blockView: ExtendedBlockView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) { override fun emitBlockQuads(blockView: ExtendedBlockView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
super.emitBlockQuads(blockView, state, pos, randomSupplier, context) ShadersModIntegration.leaves(context, BetterFoliage.config.leaves.shaderWind) {
if (!BetterFoliage.config.enabled || !BetterFoliage.config.leaves.enabled) return super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
if (!BetterFoliage.config.enabled || !BetterFoliage.config.leaves.enabled) return
val ctx = BasicBlockCtx(blockView, pos) val ctx = BasicBlockCtx(blockView, pos)
val stateAbove = ctx.state(UP) val stateAbove = ctx.state(UP)
val isSnowed = stateAbove.material in SNOW_MATERIALS val isSnowed = stateAbove.material in SNOW_MATERIALS
val random = randomSupplier.get() val random = randomSupplier.get()
context.withLighting(leafLighting) { context.withLighting(leafLighting) {
it.accept(leafNormal[random]) it.accept(leafNormal[random])
if (isSnowed) it.accept(leafSnowed[random]) if (isSnowed) it.accept(leafSnowed[random])
}
} }
} }

View File

@@ -1,6 +1,7 @@
package mods.betterfoliage.render.block.vanilla package mods.betterfoliage.render.block.vanilla
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.render.ShadersModIntegration
import mods.betterfoliage.util.Atlas import mods.betterfoliage.util.Atlas
import mods.betterfoliage.resource.discovery.BlockRenderKey import mods.betterfoliage.resource.discovery.BlockRenderKey
import mods.betterfoliage.resource.discovery.ModelDiscoveryBase import mods.betterfoliage.resource.discovery.ModelDiscoveryBase
@@ -39,7 +40,9 @@ class LilypadModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
if (!BetterFoliage.config.enabled || !BetterFoliage.config.lilypad.enabled) return if (!BetterFoliage.config.enabled || !BetterFoliage.config.lilypad.enabled) return
val random = randomSupplier.get() val random = randomSupplier.get()
context.meshConsumer().accept(lilypadRootModels[random]) ShadersModIntegration.grass(context, BetterFoliage.config.lilypad.shaderWind) {
context.meshConsumer().accept(lilypadRootModels[random])
}
if (random.nextInt(64) < BetterFoliage.config.lilypad.population) { if (random.nextInt(64) < BetterFoliage.config.lilypad.population) {
context.meshConsumer().accept(lilypadFlowerModels[random]) context.meshConsumer().accept(lilypadFlowerModels[random])
} }

View File

@@ -1,6 +1,7 @@
package mods.betterfoliage.render.block.vanilla package mods.betterfoliage.render.block.vanilla
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.render.ShadersModIntegration
import mods.betterfoliage.render.lighting.withLighting import mods.betterfoliage.render.lighting.withLighting
import mods.betterfoliage.render.lighting.grassTuftLighting import mods.betterfoliage.render.lighting.grassTuftLighting
import mods.betterfoliage.util.Atlas import mods.betterfoliage.util.Atlas
@@ -45,8 +46,10 @@ class MyceliumModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
BetterFoliage.config.shortGrass.let { it.myceliumEnabled && random.nextInt(64) < it.population } && BetterFoliage.config.shortGrass.let { it.myceliumEnabled && random.nextInt(64) < it.population } &&
blockView.getBlockState(pos + UP.offset).isAir blockView.getBlockState(pos + UP.offset).isAir
) { ) {
context.withLighting(tuftLighting) { ShadersModIntegration.grass(context, BetterFoliage.config.shortGrass.shaderWind) {
it.accept(myceliumTuftModels[random]) context.withLighting(tuftLighting) {
it.accept(myceliumTuftModels[random])
}
} }
} }
} }

View File

@@ -1,13 +1,15 @@
package mods.betterfoliage.render.lighting package mods.betterfoliage.render.lighting
import it.unimi.dsi.fastutil.ints.Int2ObjectFunction
import mods.betterfoliage.util.reflectField import mods.betterfoliage.util.reflectField
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
import net.fabricmc.fabric.impl.client.indigo.renderer.accessor.AccessBufferBuilder
import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator
import net.fabricmc.fabric.impl.client.indigo.renderer.render.* import net.fabricmc.fabric.impl.client.indigo.renderer.render.*
import net.minecraft.block.BlockRenderLayer
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.client.MinecraftClient
import net.minecraft.client.render.model.BakedModel import net.minecraft.client.render.model.BakedModel
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
import net.minecraft.world.ExtendedBlockView import net.minecraft.world.ExtendedBlockView
@@ -25,13 +27,17 @@ fun TerrainMeshConsumer.modified() = MODIFIED_CONSUMER_POOL.get() ?: let {
ModifiedTerrainMeshConsumer(blockInfo, chunkInfo, aoCalc, transform) ModifiedTerrainMeshConsumer(blockInfo, chunkInfo, aoCalc, transform)
}.apply { MODIFIED_CONSUMER_POOL.set(this) } }.apply { MODIFIED_CONSUMER_POOL.set(this) }
val TerrainRenderContext_blockInfo = TerrainRenderContext::class.java.declaredFields.find { it.name == "blockInfo" }?.apply { isAccessible = true }
val BlockRenderInfo_layerIndexOrDefault = BlockRenderInfo::class.java.declaredMethods.find { it.name == "layerIndexOrDefault" }?.apply { isAccessible = true }
val AbstractQuadRenderer_bufferFunc = AbstractQuadRenderer::class.java.declaredFields.find { it.name == "bufferFunc" }?.apply { isAccessible = true }
/** /**
* Render the given model at the given position. * Render the given model at the given position.
* Mutates the state of the [RenderContext]!! * Mutates the state of the [RenderContext]!!
*/ */
fun RenderContext.renderMasquerade(model: BakedModel, blockView: ExtendedBlockView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) = when(this) { fun RenderContext.renderMasquerade(model: BakedModel, blockView: ExtendedBlockView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) = when(this) {
is TerrainRenderContext -> { is TerrainRenderContext -> {
val blockInfo = reflectField<TerrainBlockRenderInfo>("blockInfo") val blockInfo = TerrainRenderContext_blockInfo!!.get(this) as BlockRenderInfo
blockInfo.prepareForBlock(state, pos, model.useAmbientOcclusion()) blockInfo.prepareForBlock(state, pos, model.useAmbientOcclusion())
(model as FabricBakedModel).emitBlockQuads(blockView, state, pos, randomSupplier, context) (model as FabricBakedModel).emitBlockQuads(blockView, state, pos, randomSupplier, context)
} }
@@ -51,3 +57,14 @@ fun RenderContext.withLighting(lighter: CustomLighting, func: (Consumer<Mesh>)->
} }
else -> func(meshConsumer()) else -> func(meshConsumer())
} }
/** Get the [BufferBuilder] responsible for a given [BlockRenderLayer] */
fun RenderContext.getBufferBuilder(layer: BlockRenderLayer) = when(this) {
is TerrainRenderContext -> {
val blockInfo = TerrainRenderContext_blockInfo!!.get(this) as BlockRenderInfo
val layerIdx = BlockRenderInfo_layerIndexOrDefault!!.invoke(blockInfo, layer) as Int
val bufferFunc = AbstractQuadRenderer_bufferFunc!!.get(meshConsumer()) as Int2ObjectFunction<AccessBufferBuilder>
bufferFunc[layerIdx]
}
else -> null
}

View File

@@ -16,18 +16,27 @@ fun <T> Any.reflectField(name: String) = getFieldRecursive(this::class.java, nam
it.get(this) as T it.get(this) as T
} }
/** Get the field on the class with the given name.
* Does not handle overloads, suitable only for unique field names (like Yarn intermediate names)
* */
fun getFieldRecursive(cls: Class<*>, name: String): Field = try { fun getFieldRecursive(cls: Class<*>, name: String): Field = try {
cls.getDeclaredField(name) cls.getDeclaredField(name)
} catch (e: NoSuchFieldException) { } catch (e: NoSuchFieldException) {
cls.superclass?.let { getFieldRecursive(it, name) } ?: throw e cls.superclass?.let { getFieldRecursive(it, name) } ?: throw e
} }
/** Get the method on the class with the given name.
* Does not handle overloads, suitable only for unique field names (like Yarn intermediate names)
* */
fun getMethodRecursive(cls: Class<*>, name: String): Method = try { fun getMethodRecursive(cls: Class<*>, name: String): Method = try {
cls.declaredMethods.find { it.name == name } ?: throw NoSuchMethodException() cls.declaredMethods.find { it.name == name } ?: throw NoSuchMethodException()
} catch (e: NoSuchMethodException) { } catch (e: NoSuchMethodException) {
cls.superclass?.let { getMethodRecursive(it, name) } ?: throw e cls.superclass?.let { getMethodRecursive(it, name) } ?: throw e
} }
fun getAllMethods(className: String, methodName: String): List<Method> =
tryDefault(null) { Class.forName(className) }?.declaredMethods?.filter { it.name == methodName }
?: emptyList()
interface FieldRef<T> { interface FieldRef<T> {
val field: Field? val field: Field?
@@ -66,7 +75,7 @@ object YarnHelper {
try { try {
val classMapped = resolver.mapClassName(INTERMEDIARY, className) val classMapped = resolver.mapClassName(INTERMEDIARY, className)
val fieldMapped = resolver.mapFieldName(INTERMEDIARY, className, fieldName, descriptor) val fieldMapped = resolver.mapFieldName(INTERMEDIARY, className, fieldName, descriptor)
Class.forName(classMapped)?.let { cls -> getFieldRecursive(cls, fieldMapped).apply { isAccessible = true } } Class.forName(classMapped).let { cls -> getFieldRecursive(cls, fieldMapped).apply { isAccessible = true } }
} catch (e: Exception) { } catch (e: Exception) {
logger.log( logger.log(
if (optional) Level.DEBUG else Level.ERROR, if (optional) Level.DEBUG else Level.ERROR,
@@ -82,7 +91,7 @@ object YarnHelper {
try { try {
val classMapped = resolver.mapClassName(INTERMEDIARY, className) val classMapped = resolver.mapClassName(INTERMEDIARY, className)
val methodMapped = resolver.mapMethodName(INTERMEDIARY, className, methodName, descriptor) val methodMapped = resolver.mapMethodName(INTERMEDIARY, className, methodName, descriptor)
Class.forName(classMapped)?.let { cls -> getMethodRecursive(cls, methodMapped).apply { isAccessible = true } } Class.forName(classMapped).let { cls -> getMethodRecursive(cls, methodMapped).apply { isAccessible = true } }
} catch (e: Exception) { } catch (e: Exception) {
logger.log( logger.log(
if (optional) Level.DEBUG else Level.ERROR, if (optional) Level.DEBUG else Level.ERROR,