Compare commits
11 Commits
1.12-2.3.0
...
kotlin-1.1
| Author | SHA1 | Date | |
|---|---|---|---|
| 5aa33d7c70 | |||
| 64146a0f98 | |||
|
|
47c134049c | ||
|
|
b1ad58c089 | ||
|
|
85e63b9161 | ||
|
|
59ddaa0335 | ||
|
|
ae84741622 | ||
|
|
7b739c172f | ||
|
|
8319d721c7 | ||
|
|
1b0e93b050 | ||
|
|
369348f6aa |
@@ -2,3 +2,6 @@ BetterFoliage
|
||||
=============
|
||||
|
||||
Minecraft mod that alters the appearance of leaves & grass
|
||||
|
||||
fixed by @CatmanGames for StateMC<br>
|
||||
(don't render certain textures when there is a block from another mod above)
|
||||
@@ -1,7 +1,7 @@
|
||||
group = com.github.octarine-noise
|
||||
jarName = BetterFoliage-MC1.12
|
||||
|
||||
version = 2.3.0
|
||||
version = 2.3.1
|
||||
|
||||
mc_version = 1.12.2
|
||||
forge_version = 14.23.5.2847
|
||||
@@ -9,3 +9,5 @@ mcp_mappings = stable_39
|
||||
|
||||
kotlin_version = 1.3.40
|
||||
forgelin_version = 1.8.4
|
||||
|
||||
org.gradle.java.home=C:\\Users\\catma\\.jdks\\corretto-1.8.0_482
|
||||
@@ -92,14 +92,16 @@ object ChunkOverlayManager : IBlockUpdateListener {
|
||||
}
|
||||
|
||||
class ChunkOverlayData(layers: List<ChunkOverlayLayer<*>>) {
|
||||
val BlockPos.isValid: Boolean get() = y in validYRange
|
||||
val rawData = layers.associateWith { emptyOverlay() }
|
||||
fun <T> get(layer: ChunkOverlayLayer<T>, pos: BlockPos): T? = rawData[layer]?.get(pos.x and 15)?.get(pos.z and 15)?.get(pos.y) as T?
|
||||
fun <T> set(layer: ChunkOverlayLayer<T>, pos: BlockPos, data: T) = rawData[layer]?.get(pos.x and 15)?.get(pos.z and 15)?.set(pos.y, data)
|
||||
fun <T> clear(layer: ChunkOverlayLayer<T>, pos: BlockPos) = rawData[layer]?.get(pos.x and 15)?.get(pos.z and 15)?.set(pos.y, UNCALCULATED)
|
||||
fun <T> get(layer: ChunkOverlayLayer<T>, pos: BlockPos): T? = if (pos.isValid) rawData[layer]?.get(pos.x and 15)?.get(pos.z and 15)?.get(pos.y) as T? else null
|
||||
fun <T> set(layer: ChunkOverlayLayer<T>, pos: BlockPos, data: T) = if (pos.isValid) rawData[layer]?.get(pos.x and 15)?.get(pos.z and 15)?.set(pos.y, data) else null
|
||||
fun <T> clear(layer: ChunkOverlayLayer<T>, pos: BlockPos) = if (pos.isValid) rawData[layer]?.get(pos.x and 15)?.get(pos.z and 15)?.set(pos.y, UNCALCULATED) else null
|
||||
|
||||
companion object {
|
||||
val UNCALCULATED = object {}
|
||||
fun emptyOverlay() = Array(16) { Array(16) { Array<Any?>(256) { UNCALCULATED }}}
|
||||
val validYRange = 0 until 256
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +118,7 @@ interface IBlockUpdateListener : IWorldEventListener {
|
||||
override fun notifyLightSet(pos: BlockPos) {}
|
||||
override fun spawnParticle(particleID: Int, ignoreRange: Boolean, xCoord: Double, yCoord: Double, zCoord: Double, xSpeed: Double, ySpeed: Double, zSpeed: Double, vararg parameters: Int) {}
|
||||
override fun spawnParticle(id: Int, ignoreRange: Boolean, minimiseParticleLevel: Boolean, x: Double, y: Double, z: Double, xSpeed: Double, ySpeed: Double, zSpeed: Double, vararg parameters: Int) {}
|
||||
override fun playRecord(soundIn: SoundEvent, pos: BlockPos) {}
|
||||
override fun playRecord(soundIn: SoundEvent?, pos: BlockPos) {}
|
||||
override fun sendBlockBreakProgress(breakerId: Int, pos: BlockPos, progress: Int) {}
|
||||
override fun markBlockRangeForRenderUpdate(x1: Int, y1: Int, z1: Int, x2: Int, y2: Int, z2: Int) {}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package mods.betterfoliage.client.config
|
||||
|
||||
import mods.betterfoliage.BetterFoliageMod
|
||||
import mods.betterfoliage.client.gui.BiomeListConfigEntry
|
||||
import mods.betterfoliage.client.integration.ShadersModIntegration
|
||||
import mods.octarinecore.common.config.*
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.world.biome.Biome
|
||||
@@ -32,6 +33,11 @@ object Config : DelegatingConfig(BetterFoliageMod.MOD_ID, BetterFoliageMod.DOMAI
|
||||
var enabled by boolean(true)
|
||||
var nVidia by boolean(GL11.glGetString(GL11.GL_VENDOR).toLowerCase().contains("nvidia"))
|
||||
|
||||
object shaders {
|
||||
val leavesId by long(min = 1, max = 65535, default = ShadersModIntegration.leavesDefaultBlockId.toInt())
|
||||
val grassId by long(min = 1, max = 65535, default = ShadersModIntegration.grassDefaultBlockId.toInt())
|
||||
}
|
||||
|
||||
object blocks {
|
||||
val leavesClasses = ConfigurableBlockMatcher(BetterFoliageMod.DOMAIN, "leaves_blocks_default.cfg")
|
||||
val leavesModels = ModelTextureListConfigOption(BetterFoliageMod.DOMAIN, "leaves_models_default.cfg", 1)
|
||||
|
||||
@@ -38,7 +38,7 @@ object OptifineCustomColors {
|
||||
|
||||
fun getBlockColor(ctx: BlockContext): Int {
|
||||
val ofColor = if (isColorAvailable && Minecraft.getMinecraft().gameSettings.reflectField<Boolean>("ofCustomColors") == true) {
|
||||
renderEnv.reset(ctx.world!!, ctx.blockState(Int3.zero), ctx.pos)
|
||||
renderEnv.reset(ctx.blockState(Int3.zero), ctx.pos)
|
||||
Refs.getColorMultiplier.invokeStatic(fakeQuad, ctx.blockState(Int3.zero), ctx.world!!, ctx.pos, renderEnv.wrapped) as? Int
|
||||
} else null
|
||||
return if (ofColor == null || ofColor == -1) ctx.blockData(Int3.zero).color else ofColor
|
||||
@@ -48,13 +48,13 @@ object OptifineCustomColors {
|
||||
@SideOnly(Side.CLIENT)
|
||||
class OptifineRenderEnv {
|
||||
val wrapped: Any = Refs.RenderEnv.element!!.getDeclaredConstructor(
|
||||
Refs.IBlockAccess.element, Refs.IBlockState.element, Refs.BlockPos.element
|
||||
Refs.IBlockState.element, Refs.BlockPos.element
|
||||
).let {
|
||||
it.isAccessible = true
|
||||
it.newInstance(null, null, null)
|
||||
it.newInstance(null, null)
|
||||
}
|
||||
|
||||
fun reset(blockAccess: IBlockAccess, state: IBlockState, pos: BlockPos) {
|
||||
Refs.RenderEnv_reset.invoke(wrapped, blockAccess, state, pos)
|
||||
fun reset(state: IBlockState, pos: BlockPos) {
|
||||
Refs.RenderEnv_reset.invoke(wrapped, state, pos)
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,8 @@ import net.minecraft.block.BlockTallGrass
|
||||
import net.minecraft.block.state.IBlockState
|
||||
import net.minecraft.client.renderer.BufferBuilder
|
||||
import net.minecraft.init.Blocks
|
||||
import net.minecraft.util.EnumBlockRenderType
|
||||
import net.minecraft.util.EnumBlockRenderType.MODEL
|
||||
import net.minecraftforge.fml.relauncher.Side
|
||||
import net.minecraftforge.fml.relauncher.SideOnly
|
||||
import org.apache.logging.log4j.Level.INFO
|
||||
@@ -19,23 +21,32 @@ import org.apache.logging.log4j.Level.INFO
|
||||
@SideOnly(Side.CLIENT)
|
||||
object ShadersModIntegration {
|
||||
|
||||
@JvmStatic var isAvailable = allAvailable(Refs.sVertexBuilder, Refs.pushEntity_state, Refs.pushEntity_num, Refs.popEntity)
|
||||
@JvmStatic val tallGrassEntityData = entityDataFor(Blocks.TALLGRASS.defaultState.withProperty(BlockTallGrass.TYPE, BlockTallGrass.EnumType.GRASS))
|
||||
@JvmStatic val leavesEntityData = entityDataFor(Blocks.LEAVES.defaultState)
|
||||
@JvmStatic val isAvailable = allAvailable(Refs.sVertexBuilder, Refs.pushEntity_state, Refs.pushEntity_num, Refs.popEntity)
|
||||
|
||||
fun entityDataFor(blockState: IBlockState) =
|
||||
(Block.REGISTRY.getIDForObject(blockState.block).toLong() and 65535) or
|
||||
((blockState.renderType.ordinal.toLong() and 65535) shl 16) or
|
||||
(blockState.block.getMetaFromState(blockState).toLong() shl 32)
|
||||
val grassDefaultBlockId = blockIdFor(Blocks.TALLGRASS.defaultState.withProperty(BlockTallGrass.TYPE, BlockTallGrass.EnumType.GRASS))
|
||||
val leavesDefaultBlockId = blockIdFor(Blocks.LEAVES.defaultState)
|
||||
fun blockIdFor(blockState: IBlockState) = Block.REGISTRY.getIDForObject(blockState.block).toLong() and 65535
|
||||
|
||||
// fun entityDataFor(blockState: IBlockState) =
|
||||
// (Block.REGISTRY.getIDForObject(blockState.block).toLong() and 65535) //or
|
||||
// ((blockState.renderType.ordinal.toLong() and 65535) shl 16) or
|
||||
// (blockState.block.getMetaFromState(blockState).toLong() shl 32)
|
||||
|
||||
fun logEntityData(name: String, blockState: IBlockState) {
|
||||
val blockId = Block.REGISTRY.getIDForObject(blockState.block).toLong() and 65535
|
||||
val meta = blockState.renderType.ordinal.toLong() and 65535
|
||||
val renderType = blockState.renderType.ordinal.toLong() and 65535
|
||||
Client.log(INFO, "ShadersMod integration for $name")
|
||||
Client.log(INFO, " blockState=$blockState")
|
||||
Client.log(INFO, " blockId=$blockId, meta=$meta, type=$renderType")
|
||||
}
|
||||
/**
|
||||
* Called from transformed ShadersMod code.
|
||||
* @see mods.betterfoliage.loader.BetterFoliageTransformer
|
||||
*/
|
||||
@JvmStatic fun getBlockIdOverride(original: Long, blockState: IBlockState): Long {
|
||||
if (Config.blocks.leavesClasses.matchesClass(blockState.block)) return leavesEntityData
|
||||
if (Config.blocks.crops.matchesClass(blockState.block)) return tallGrassEntityData
|
||||
if (Config.blocks.leavesClasses.matchesClass(blockState.block)) return Config.shaders.leavesId
|
||||
if (Config.blocks.crops.matchesClass(blockState.block)) return Config.shaders.grassId
|
||||
return original
|
||||
}
|
||||
|
||||
@@ -44,10 +55,11 @@ object ShadersModIntegration {
|
||||
}
|
||||
|
||||
/** Quads rendered inside this block will use the given block entity data in shader programs. */
|
||||
inline fun renderAs(blockEntityData: Long, renderer: BufferBuilder, enabled: Boolean = true, func: ()->Unit) {
|
||||
inline fun renderAs(blockId: Long, renderType: EnumBlockRenderType, renderer: BufferBuilder, enabled: Boolean = true, func: ()->Unit) {
|
||||
val blockData = blockId or (renderType.ordinal shl 16).toLong()
|
||||
if ((isAvailable && enabled)) {
|
||||
val vertexBuilder = Refs.sVertexBuilder.get(renderer)!!
|
||||
Refs.pushEntity_num.invoke(vertexBuilder, blockEntityData)
|
||||
Refs.pushEntity_num.invoke(vertexBuilder, blockId)
|
||||
func()
|
||||
Refs.popEntity.invoke(vertexBuilder)
|
||||
} else {
|
||||
@@ -56,14 +68,14 @@ object ShadersModIntegration {
|
||||
}
|
||||
|
||||
/** Quads rendered inside this block will use the given block entity data in shader programs. */
|
||||
inline fun renderAs(state: IBlockState, renderer: BufferBuilder, enabled: Boolean = true, func: ()->Unit) =
|
||||
renderAs(entityDataFor(state), renderer, enabled, func)
|
||||
inline fun renderAs(state: IBlockState, renderType: EnumBlockRenderType, renderer: BufferBuilder, enabled: Boolean = true, func: ()->Unit) =
|
||||
renderAs(blockIdFor(state), renderType, renderer, enabled, func)
|
||||
|
||||
/** Quads rendered inside this block will behave as tallgrass blocks in shader programs. */
|
||||
inline fun grass(renderer: BufferBuilder, enabled: Boolean = true, func: ()->Unit) =
|
||||
renderAs(tallGrassEntityData, renderer, enabled, func)
|
||||
renderAs(Config.shaders.grassId, MODEL, renderer, enabled, func)
|
||||
|
||||
/** Quads rendered inside this block will behave as leaf blocks in shader programs. */
|
||||
inline fun leaves(renderer: BufferBuilder, enabled: Boolean = true, func: ()->Unit) =
|
||||
renderAs(leavesEntityData, renderer, enabled, func)
|
||||
renderAs(Config.shaders.leavesId, MODEL, renderer, enabled, func)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ import mods.octarinecore.random
|
||||
import net.minecraft.client.renderer.BlockRendererDispatcher
|
||||
import net.minecraft.client.renderer.BufferBuilder
|
||||
import net.minecraft.util.BlockRenderLayer
|
||||
import net.minecraft.util.EnumBlockRenderType
|
||||
import net.minecraft.util.EnumBlockRenderType.MODEL
|
||||
import net.minecraft.util.EnumFacing.Axis
|
||||
import net.minecraft.util.EnumFacing.UP
|
||||
import net.minecraftforge.fml.relauncher.Side
|
||||
@@ -78,7 +80,7 @@ class RenderGrass : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
||||
val isHidden = forgeDirs.map { ctx.blockState(it.offset).isOpaqueCube }
|
||||
|
||||
// render full grass block
|
||||
ShadersModIntegration.renderAs(ctx.blockState(Int3.zero), renderer) {
|
||||
ShadersModIntegration.renderAs(ctx.blockState(Int3.zero), MODEL, renderer) {
|
||||
modelRenderer.render(
|
||||
renderer,
|
||||
fullCube,
|
||||
@@ -100,6 +102,10 @@ class RenderGrass : AbstractBlockRenderingHandler(BetterFoliageMod.MOD_ID) {
|
||||
}
|
||||
|
||||
if (!Config.shortGrass.grassEnabled) return true
|
||||
|
||||
val stateAbove = ctx.blockState(up1)
|
||||
if (!stateAbove.block.isAir(stateAbove, ctx.world!!, ctx.pos.up())) return true
|
||||
|
||||
if (isSnowed && !Config.shortGrass.snowEnabled) return true
|
||||
if (ctx.blockState(up1).isOpaqueCube) return true
|
||||
if (Config.shortGrass.population < 64 && noise[ctx.pos] >= Config.shortGrass.population) return true
|
||||
|
||||
@@ -17,6 +17,8 @@ import net.minecraft.client.renderer.BlockRendererDispatcher
|
||||
import net.minecraft.client.renderer.BufferBuilder
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||
import net.minecraft.util.BlockRenderLayer
|
||||
import net.minecraft.util.EnumBlockRenderType
|
||||
import net.minecraft.util.EnumBlockRenderType.MODEL
|
||||
import net.minecraft.util.EnumFacing.*
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.IBlockAccess
|
||||
@@ -117,7 +119,7 @@ abstract class AbstractRenderColumn(modId: String) : AbstractBlockRenderingHandl
|
||||
modelRenderer.updateShading(Int3.zero, allFaces)
|
||||
|
||||
val baseRotation = rotationFromUp[((roundLog.column.axis ?: Axis.Y) to AxisDirection.POSITIVE).face.ordinal]
|
||||
renderAs(ctx.blockState(Int3.zero), renderer) {
|
||||
renderAs(ctx.blockState(Int3.zero), MODEL, renderer) {
|
||||
quadrantRotations.forEachIndexed { idx, quadrantRotation ->
|
||||
// set rotation for the current quadrant
|
||||
val rotation = baseRotation + quadrantRotation
|
||||
|
||||
@@ -101,7 +101,7 @@ class BetterFoliageTransformer : Transformer() {
|
||||
}
|
||||
if (isOptifinePresent) {
|
||||
find(varinsn(ISTORE, 23))?.insertAfter {
|
||||
log.info("[BetterFoliageLoader] Applying RenderChunk block layer override")
|
||||
log.info("[BetterFoliageLoader] Applying RenderChunk block layer override (Optifine)")
|
||||
varinsn(ALOAD, 19)
|
||||
varinsn(ALOAD, 18)
|
||||
varinsn(ALOAD, 22)
|
||||
@@ -110,7 +110,7 @@ class BetterFoliageTransformer : Transformer() {
|
||||
}
|
||||
} else {
|
||||
find(invokeRef(Refs.canRenderInLayer))?.replace {
|
||||
log.info("[BetterFoliageLoader] Applying RenderChunk block layer override")
|
||||
log.info("[BetterFoliageLoader] Applying RenderChunk block layer override (non-Optifine)")
|
||||
invokeStatic(Refs.canRenderBlockInLayer)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ object Refs {
|
||||
|
||||
// Optifine
|
||||
val RenderEnv = ClassRef("net.optifine.render.RenderEnv")
|
||||
val RenderEnv_reset = MethodRef(RenderEnv, "reset", ClassRef.void, IBlockAccess, IBlockState, BlockPos)
|
||||
val RenderEnv_reset = MethodRef(RenderEnv, "reset", ClassRef.void, IBlockState, BlockPos)
|
||||
val quadSprite = FieldRef(BufferBuilder, "quadSprite", TextureAtlasSprite)
|
||||
|
||||
// Optifine: custom colors
|
||||
|
||||
@@ -47,11 +47,10 @@ class AoData() {
|
||||
}
|
||||
|
||||
class AoFaceData(val face: EnumFacing) {
|
||||
val ao = Refs.AmbientOcclusionFace.element!!.let {
|
||||
if (allAvailable(Refs.OptifineClassTransformer)) it.getDeclaredConstructor().newInstance()
|
||||
else it.getDeclaredConstructor(Refs.BlockModelRenderer.element!!)
|
||||
val ao = Refs.AmbientOcclusionFace.element!!.getDeclaredConstructor(Refs.BlockModelRenderer.element!!)
|
||||
.newInstance(BlockModelRenderer(Minecraft.getMinecraft().blockColors))
|
||||
} as BlockModelRenderer.AmbientOcclusionFace
|
||||
as BlockModelRenderer.AmbientOcclusionFace
|
||||
|
||||
val top = faceCorners[face.ordinal].topLeft.first
|
||||
val left = faceCorners[face.ordinal].topLeft.second
|
||||
|
||||
|
||||
@@ -228,6 +228,15 @@ class ConfigPropertyInt(val min: Int, val max: Int, val default: Int) :
|
||||
override fun Property.write(value: Int) = property!!.set(value)
|
||||
}
|
||||
|
||||
/** [Long]-typed property delegate. Still uses [Int] internally */
|
||||
class ConfigPropertyLong(val min: Int, val max: Int, val default: Int) :
|
||||
ConfigPropertyDelegate<Long>() {
|
||||
override fun resolve(target: Configuration, category: String, name: String) =
|
||||
target.get(category, name, default, null).apply { setMinValue(min); setMaxValue(max) }
|
||||
override fun Property.read() = property!!.long
|
||||
override fun Property.write(value: Long) = property!!.set(value)
|
||||
}
|
||||
|
||||
/** [Boolean]-typed property delegate. */
|
||||
class ConfigPropertyBoolean(val default: Boolean) :
|
||||
ConfigPropertyDelegate<Boolean>() {
|
||||
@@ -252,5 +261,6 @@ class ConfigPropertyIntList(val defaults: ()->Array<Int>) :
|
||||
fun double(min: Double = 0.0, max: Double = 1.0, default: Double) = ConfigPropertyDouble(min, max, default)
|
||||
fun float(min: Double = 0.0, max: Double = 1.0, default: Double) = ConfigPropertyFloat(min, max, default)
|
||||
fun int(min: Int = 0, max: Int, default: Int) = ConfigPropertyInt(min, max, default)
|
||||
fun long(min: Int = 0, max: Int, default: Int) = ConfigPropertyLong(min, max, default)
|
||||
fun intList(defaults: ()->Array<Int>) = ConfigPropertyIntList(defaults)
|
||||
fun boolean(default: Boolean) = ConfigPropertyBoolean(default)
|
||||
@@ -34,3 +34,10 @@ com.pam.harvestcraft.BlockPamCrop
|
||||
com.pam.harvestcraft.BlockPamDesertGarden
|
||||
com.pam.harvestcraft.BlockPamNormalGarden
|
||||
com.pam.harvestcraft.BlockPamWaterGarden
|
||||
|
||||
// Plants
|
||||
shadows.plants2.block.BlockEnumCrop
|
||||
|
||||
// Cuisine
|
||||
snownee.cuisine.blocks.BlockCuisineCrops
|
||||
-snownee.cuisine.blocks.BlockDoubleCrops
|
||||
|
||||
@@ -108,6 +108,13 @@ betterfoliage.blocks.netherrackBlacklist.arrayEntry=%d entries
|
||||
betterfoliage.blocks.netherrackWhitelist.tooltip=Blocks recognized as Netherrack. Has an impact on Netherrack Vines
|
||||
betterfoliage.blocks.netherrackBlacklist.tooltip=Blocks never accepted Netherrack. Has an impact on Netherrack Vines
|
||||
|
||||
betterfoliage.shaders=Shader configuration
|
||||
betterfoliage.shaders.tooltip=Configure integration with shaders
|
||||
betterfoliage.shaders.leavesId=Leaves ID
|
||||
betterfoliage.shaders.leavesId.tooltip=Block ID reported to shader programs for all kinds of leaves. If your shader uses a §6block.properties§e file, you'll probably need to change this to match the shader's mappings.
|
||||
betterfoliage.shaders.grassId=Grass ID
|
||||
betterfoliage.shaders.grassId.tooltip=Block ID reported to shader programs for all grasses and crops. If your shader uses a §6block.properties§e file, you'll probably need to change this to match the shader's mappings.
|
||||
|
||||
betterfoliage.leaves=Extra Leaves
|
||||
betterfoliage.leaves.tooltip=Extra round leaves on leaf blocks
|
||||
betterfoliage.leaves.dense=Dense mode
|
||||
|
||||
@@ -6,3 +6,11 @@ biomesoplenty.common.block.BlockBOPLeaves
|
||||
|
||||
// Aether II
|
||||
com.gildedgames.aether.common.blocks.natural.BlockAetherLeaves
|
||||
|
||||
// Plants
|
||||
shadows.plants2.block.BlockEnumLeaves
|
||||
shadows.plants2.block.BlockEnumNetherLeaves
|
||||
|
||||
// Cuisine
|
||||
snownee.cuisine.blocks.BlockModLeaves
|
||||
snownee.cuisine.blocks.BlockShearedLeaves
|
||||
|
||||
@@ -33,3 +33,7 @@ techreborn.blocks.BlockRubberLog
|
||||
|
||||
// Better With Mods
|
||||
betterwithmods.blocks.BlockStump
|
||||
|
||||
// Plants
|
||||
shadows.plants2.block.BlockEnumLog
|
||||
shadows.plants2.block.BlockEnumNetherLog
|
||||
|
||||
Reference in New Issue
Block a user