[enhancement] Threaded HoleESP, VoidESP, StorageESP, StashLogger, Search (#1816)

This commit is contained in:
Xiaro 2021-01-07 11:02:44 -05:00 committed by GitHub
parent b4467fa0e1
commit 385fb1660d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 485 additions and 438 deletions

View File

@ -86,7 +86,7 @@ object SearchCommand : ClientCommand(
literal("override") { literal("override") {
execute("Override the Intel Integrated GPU check") { execute("Override the Intel Integrated GPU check") {
Search.overrideWarning.value = true Search.overrideWarning = true
MessageSendHelper.sendWarningMessage("Override for Intel Integrated GPUs enabled!") MessageSendHelper.sendWarningMessage("Override for Intel Integrated GPUs enabled!")
} }
} }

View File

@ -13,7 +13,7 @@ import me.zeroeightsix.kami.util.graphics.font.FontRenderAdapter
import me.zeroeightsix.kami.util.graphics.font.HAlign import me.zeroeightsix.kami.util.graphics.font.HAlign
import me.zeroeightsix.kami.util.graphics.font.TextComponent import me.zeroeightsix.kami.util.graphics.font.TextComponent
import me.zeroeightsix.kami.util.graphics.font.VAlign import me.zeroeightsix.kami.util.graphics.font.VAlign
import me.zeroeightsix.kami.util.threads.safeAsyncListener import me.zeroeightsix.kami.util.threads.safeListener
import net.minecraft.client.renderer.GlStateManager import net.minecraft.client.renderer.GlStateManager
import net.minecraftforge.fml.common.gameevent.TickEvent import net.minecraftforge.fml.common.gameevent.TickEvent
import org.kamiblue.commons.extension.sumByFloat import org.kamiblue.commons.extension.sumByFloat
@ -67,8 +67,8 @@ object ModuleList : HudElement(
.toMutableMap() .toMutableMap()
init { init {
safeAsyncListener<TickEvent.ClientTickEvent> { event -> safeListener<TickEvent.ClientTickEvent> { event ->
if (event.phase != TickEvent.Phase.END) return@safeAsyncListener if (event.phase != TickEvent.Phase.END) return@safeListener
val moduleSet = ModuleManager.modules.toSet() val moduleSet = ModuleManager.modules.toSet()

View File

@ -1,22 +1,27 @@
package me.zeroeightsix.kami.module.modules.combat package me.zeroeightsix.kami.module.modules.combat
import kotlinx.coroutines.launch
import me.zeroeightsix.kami.event.SafeClientEvent
import me.zeroeightsix.kami.event.events.RenderWorldEvent import me.zeroeightsix.kami.event.events.RenderWorldEvent
import me.zeroeightsix.kami.module.Module import me.zeroeightsix.kami.module.Module
import me.zeroeightsix.kami.setting.ModuleConfig.setting import me.zeroeightsix.kami.setting.ModuleConfig.setting
import me.zeroeightsix.kami.util.TickTimer import me.zeroeightsix.kami.util.TickTimer
import me.zeroeightsix.kami.util.color.ColorHolder import me.zeroeightsix.kami.util.color.ColorHolder
import me.zeroeightsix.kami.util.combat.SurroundUtils import me.zeroeightsix.kami.util.combat.SurroundUtils
import me.zeroeightsix.kami.util.combat.SurroundUtils.checkHole
import me.zeroeightsix.kami.util.graphics.ESPRenderer import me.zeroeightsix.kami.util.graphics.ESPRenderer
import me.zeroeightsix.kami.util.graphics.GeometryMasks import me.zeroeightsix.kami.util.graphics.GeometryMasks
import me.zeroeightsix.kami.util.math.VectorUtils.toBlockPos import me.zeroeightsix.kami.util.math.VectorUtils.toBlockPos
import org.kamiblue.event.listener.listener import me.zeroeightsix.kami.util.threads.defaultScope
import me.zeroeightsix.kami.util.threads.safeListener
import net.minecraft.util.math.AxisAlignedBB
object HoleESP : Module( object HoleESP : Module(
name = "HoleESP", name = "HoleESP",
category = Category.COMBAT, category = Category.COMBAT,
description = "Show safe holes for crystal pvp" description = "Show safe holes for crystal pvp"
) { ) {
private val range = setting("RenderDistance", 8, 4..16, 1) private val range = setting("RenderDistance", 8, 4..32, 1)
private val filled = setting("Filled", true) private val filled = setting("Filled", true)
private val outline = setting("Outline", true) private val outline = setting("Outline", true)
private val hideOwn = setting("HideOwn", true) private val hideOwn = setting("HideOwn", true)
@ -43,38 +48,46 @@ object HoleESP : Module(
private val timer = TickTimer() private val timer = TickTimer()
init { init {
listener<RenderWorldEvent> { safeListener<RenderWorldEvent> {
if (mc.world == null || mc.player == null) return@listener if (timer.tick(133L)) { // Avoid running this on a tick
if (timer.tick(133L)) updateRenderer() // Avoid running this on a tick updateRenderer()
}
renderer.render(false) renderer.render(false)
} }
} }
private fun updateRenderer() { private fun SafeClientEvent.updateRenderer() {
renderer.clear()
renderer.aFilled = if (filled.value) aFilled.value else 0 renderer.aFilled = if (filled.value) aFilled.value else 0
renderer.aOutline = if (outline.value) aOutline.value else 0 renderer.aOutline = if (outline.value) aOutline.value else 0
val colorObsidian = ColorHolder(r1.value, g1.value, b1.value) val colorObsidian = ColorHolder(r1.value, g1.value, b1.value)
val colorBedrock = ColorHolder(r2.value, g2.value, b2.value) val colorBedrock = ColorHolder(r2.value, g2.value, b2.value)
val playerPos = mc.player.positionVector.toBlockPos() val playerPos = player.positionVector.toBlockPos()
val side = if (renderMode.value != Mode.FLAT) GeometryMasks.Quad.ALL val side = if (renderMode.value != Mode.FLAT) GeometryMasks.Quad.ALL
else GeometryMasks.Quad.DOWN else GeometryMasks.Quad.DOWN
for (x in -range.value..range.value) for (y in -range.value..range.value) for (z in -range.value..range.value) { defaultScope.launch {
if (hideOwn.value && x == 0 && y == 0 && z == 0) continue val cached = ArrayList<Triple<AxisAlignedBB, ColorHolder, Int>>()
val pos = playerPos.add(x, y, z)
val holeType = SurroundUtils.checkHole(pos)
if (holeType == SurroundUtils.HoleType.NONE) continue
val renderPos = if (renderMode.value == Mode.BLOCK_FLOOR) pos.down() else pos
if (holeType == SurroundUtils.HoleType.OBBY && shouldAddObsidian()) { for (x in -range.value..range.value) for (y in -range.value..range.value) for (z in -range.value..range.value) {
renderer.add(renderPos, colorObsidian, side) if (hideOwn.value && x == 0 && y == 0 && z == 0) continue
val pos = playerPos.add(x, y, z)
val holeType = checkHole(pos)
if (holeType == SurroundUtils.HoleType.NONE) continue
val bb = AxisAlignedBB(if (renderMode.value == Mode.BLOCK_FLOOR) pos.down() else pos)
if (holeType == SurroundUtils.HoleType.OBBY && shouldAddObsidian()) {
cached.add(Triple(bb, colorObsidian, side))
}
if (holeType == SurroundUtils.HoleType.BEDROCK && shouldAddBedrock()) {
cached.add(Triple(bb, colorBedrock, side))
}
} }
if (holeType == SurroundUtils.HoleType.BEDROCK && shouldAddBedrock()) { renderer.replaceAll(cached)
renderer.add(renderPos, colorBedrock, side)
}
} }
} }

View File

@ -1,122 +0,0 @@
package me.zeroeightsix.kami.module.modules.misc
import me.zeroeightsix.kami.manager.managers.WaypointManager
import me.zeroeightsix.kami.module.Module
import me.zeroeightsix.kami.setting.ModuleConfig.setting
import me.zeroeightsix.kami.util.math.CoordinateConverter.asString
import me.zeroeightsix.kami.util.text.MessageSendHelper
import me.zeroeightsix.kami.util.threads.safeListener
import net.minecraft.client.audio.PositionedSoundRecord
import net.minecraft.init.SoundEvents
import net.minecraft.tileentity.*
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.ChunkPos
import net.minecraftforge.fml.common.gameevent.TickEvent
import kotlin.math.roundToInt
object StashFinder : Module(
name = "StashFinder",
category = Category.MISC,
description = "Logs storage units in render distance."
) {
private val saveToFile = setting("SaveToFile", true)
private val logToChat = setting("LogToChat", true)
private val playSound = setting("PlaySound", true)
private val logChests = setting("Chests", true)
private val chestDensity = setting("MinChests", 5, 1..20, 1, { logChests.value })
private val logShulkers = setting("Shulkers", true)
private val shulkerDensity = setting("MinShulkers", 1, 1..20, 1, { logShulkers.value })
private val logDroppers = setting("Droppers", true)
private val dropperDensity = setting("MinDroppers", 5, 1..20, 1, { logDroppers.value })
private val logDispensers = setting("Dispensers", true)
private val dispenserDensity = setting("MinDispensers", 5, 1..20, 1, { logDispensers.value })
private val logHoppers = setting("Hoppers", true)
private val hopperDensity = setting("MinHoppers", 5, 1..20, 1, { logHoppers.value })
private val chunkData = LinkedHashMap<Long, ChunkStats>()
private val knownPositions = LinkedHashSet<BlockPos>()
override fun onEnable() {
chunkData.clear()
knownPositions.clear()
}
init {
safeListener<TickEvent.ClientTickEvent> {
world.loadedTileEntityList
.filter {
logChests.value && it is TileEntityChest
|| logShulkers.value && it is TileEntityShulkerBox
|| logDroppers.value && it is TileEntityDropper
|| logDispensers.value && it is TileEntityDispenser
|| logHoppers.value && it is TileEntityHopper
}
.forEach { logTileEntity(it) }
chunkData.values.filter { it.hot }.forEach { chunkStats ->
chunkStats.hot = false
// mfw int array instead of Vec3i
if (saveToFile.value) {
WaypointManager.add(chunkStats.getBlockPos(), chunkStats.toString())
}
if (playSound.value) {
mc.soundHandler.playSound(PositionedSoundRecord.getRecord(SoundEvents.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0f, 1.0f))
}
if (logToChat.value) {
val positionString = chunkStats.getBlockPos().asString()
MessageSendHelper.sendChatMessage("$chatName $positionString $chunkStats")
}
}
}
}
private fun logTileEntity(tileEntity: TileEntity) {
if (knownPositions.contains(tileEntity.pos)) return
knownPositions.add(tileEntity.pos)
val chunk = ChunkPos.asLong(tileEntity.pos.x / 16, tileEntity.pos.z / 16)
val chunkStats = chunkData.getOrPut(chunk, { ChunkStats() })
chunkStats.add(tileEntity)
if (chunkStats.chests >= chestDensity.value || chunkStats.shulkers >= shulkerDensity.value || chunkStats.droppers >= dropperDensity.value || chunkStats.dispensers >= dispenserDensity.value || chunkStats.hoppers >= hopperDensity.value) {
chunkStats.hot = true
}
}
private data class ChunkStats(var chests: Int = 0, var shulkers: Int = 0, var droppers: Int = 0, var dispensers: Int = 0, var hoppers: Int = 0, var hot: Boolean = false) {
private val tileEntities = ArrayList<TileEntity>()
fun add(tileEntity: TileEntity) {
when (tileEntity) {
is TileEntityChest -> chests++
is TileEntityShulkerBox -> shulkers++
is TileEntityDropper -> droppers++
is TileEntityDispenser -> dispensers++
}
tileEntities.add(tileEntity)
}
// Averages the positions of all the tile entities
fun getBlockPos(): BlockPos {
val x = tileEntities.map { it.pos.x }.average().roundToInt()
val y = tileEntities.map { it.pos.y }.average().roundToInt()
val z = tileEntities.map { it.pos.z }.average().roundToInt()
return BlockPos(x, y, z)
}
override fun toString(): String {
return "($chests chests, $shulkers shulkers, $droppers droppers, $dispensers dispensers, $hoppers hoppers)"
}
}
}

View File

@ -0,0 +1,164 @@
package me.zeroeightsix.kami.module.modules.misc
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import me.zeroeightsix.kami.manager.managers.WaypointManager
import me.zeroeightsix.kami.module.Module
import me.zeroeightsix.kami.setting.ModuleConfig.setting
import me.zeroeightsix.kami.util.TickTimer
import me.zeroeightsix.kami.util.TimeUnit
import me.zeroeightsix.kami.util.math.CoordinateConverter.asString
import me.zeroeightsix.kami.util.text.MessageSendHelper
import me.zeroeightsix.kami.util.threads.defaultScope
import me.zeroeightsix.kami.util.threads.onMainThread
import me.zeroeightsix.kami.util.threads.safeListener
import net.minecraft.client.audio.PositionedSoundRecord
import net.minecraft.init.SoundEvents
import net.minecraft.tileentity.*
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.ChunkPos
import net.minecraftforge.fml.common.gameevent.TickEvent
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashSet
import kotlin.collections.LinkedHashMap
import kotlin.math.roundToInt
object StashLogger : Module(
name = "StashLogger",
category = Category.MISC,
description = "Logs storage units in render distance."
) {
private val saveToFile by setting("SaveToFile", true)
private val logToChat by setting("LogToChat", true)
private val playSound by setting("PlaySound", true)
private val logChests by setting("Chests", true)
private val chestDensity by setting("MinChests", 5, 1..20, 1, { logChests })
private val logShulkers by setting("Shulkers", true)
private val shulkerDensity by setting("MinShulkers", 1, 1..20, 1, { logShulkers })
private val logDroppers by setting("Droppers", true)
private val dropperDensity by setting("MinDroppers", 5, 1..20, 1, { logDroppers })
private val logDispensers by setting("Dispensers", true)
private val dispenserDensity by setting("MinDispensers", 5, 1..20, 1, { logDispensers })
private val logHoppers by setting("Hoppers", true)
private val hopperDensity by setting("MinHoppers", 5, 1..20, 1, { logHoppers })
private val chunkData = Collections.synchronizedMap(LinkedHashMap<Long, ChunkStats>())
private val knownPositions = HashSet<BlockPos>()
private val timer = TickTimer(TimeUnit.SECONDS)
override fun onEnable() {
chunkData.clear()
knownPositions.clear()
}
init {
safeListener<TickEvent.ClientTickEvent> {
if (it.phase != TickEvent.Phase.END || !timer.tick(3L)) return@safeListener
defaultScope.launch {
coroutineScope {
launch {
world.loadedTileEntityList.toList().forEach(::logTileEntity)
}
launch {
for (chunkStats in chunkData.values) {
if (!chunkStats.hot) continue
chunkStats.hot = false
val center = chunkStats.center()
val string = chunkStats.toString()
if (saveToFile) {
WaypointManager.add(center, string)
}
if (playSound) {
onMainThread {
mc.soundHandler.playSound(PositionedSoundRecord.getRecord(SoundEvents.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0f, 1.0f))
}
}
if (logToChat) {
val positionString = center.asString()
MessageSendHelper.sendChatMessage("$chatName $positionString $string")
}
}
}
}
}
}
}
private fun logTileEntity(tileEntity: TileEntity) {
if (!checkTileEntityType(tileEntity)) return
if (!knownPositions.add(tileEntity.pos)) return
val chunk = ChunkPos.asLong(tileEntity.pos.x shl 4, tileEntity.pos.z shl 4)
val chunkStats = chunkData.getOrPut(chunk, ::ChunkStats)
chunkStats.add(tileEntity)
}
private fun checkTileEntityType(tileEntity: TileEntity) =
logChests && tileEntity is TileEntityChest
|| logShulkers && tileEntity is TileEntityShulkerBox
|| logDroppers && tileEntity is TileEntityDropper
|| logDispensers && tileEntity is TileEntityDispenser
|| logHoppers && tileEntity is TileEntityHopper
private class ChunkStats {
var chests: Int = 0; private set
var shulkers: Int = 0; private set
var droppers: Int = 0; private set
var dispensers: Int = 0; private set
var hoppers: Int = 0; private set
var hot: Boolean = false
private val tileEntities = Collections.synchronizedList(ArrayList<TileEntity>())
fun add(tileEntity: TileEntity) {
when (tileEntity) {
is TileEntityChest -> chests++
is TileEntityShulkerBox -> shulkers++
is TileEntityDropper -> droppers++
is TileEntityDispenser -> dispensers++
is TileEntityHopper -> hoppers++
}
tileEntities.add(tileEntity)
if (chests >= chestDensity
|| shulkers >= shulkerDensity
|| droppers >= dropperDensity
|| dispensers >= dispenserDensity
|| hoppers >= hopperDensity) {
hot = true
}
}
fun center(): BlockPos {
var x = 0.0
var y = 0.0
var z = 0.0
val size = tileEntities.size
for (tileEntity in tileEntities) {
x += tileEntity.pos.x
y += tileEntity.pos.y
z += tileEntity.pos.z
}
x /= size
y /= size
z /= size
return BlockPos(x.roundToInt(), y.roundToInt(), z.roundToInt())
}
override fun toString(): String {
return "($chests chests, $shulkers shulkers, $droppers droppers, $dispensers dispensers, $hoppers hoppers)"
}
}
}

View File

@ -139,13 +139,13 @@ object ESP : Module(
if (mc.renderManager.options == null) return@listener if (mc.renderManager.options == null) return@listener
when (mode.value) { when (mode.value) {
ESPMode.BOX -> { ESPMode.BOX -> {
val colour = ColorHolder(r.value, g.value, b.value) val color = ColorHolder(r.value, g.value, b.value)
val renderer = ESPRenderer() val renderer = ESPRenderer()
renderer.aFilled = if (filled.value) aFilled.value else 0 renderer.aFilled = if (filled.value) aFilled.value else 0
renderer.aOutline = if (outline.value) aOutline.value else 0 renderer.aOutline = if (outline.value) aOutline.value else 0
renderer.thickness = width.value renderer.thickness = width.value
for (entity in entityList) { for (entity in entityList) {
renderer.add(entity, colour) renderer.add(entity, color)
} }
renderer.render(true) renderer.render(true)
} }

View File

@ -1,30 +1,34 @@
package me.zeroeightsix.kami.module.modules.render package me.zeroeightsix.kami.module.modules.render
import io.netty.util.internal.ConcurrentSet import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import me.zeroeightsix.kami.command.CommandManager import me.zeroeightsix.kami.command.CommandManager
import me.zeroeightsix.kami.event.SafeClientEvent import me.zeroeightsix.kami.event.SafeClientEvent
import me.zeroeightsix.kami.event.events.RenderWorldEvent import me.zeroeightsix.kami.event.events.RenderWorldEvent
import me.zeroeightsix.kami.module.Module import me.zeroeightsix.kami.module.Module
import me.zeroeightsix.kami.setting.ModuleConfig.setting import me.zeroeightsix.kami.setting.ModuleConfig.setting
import me.zeroeightsix.kami.setting.settings.impl.collection.CollectionSetting import me.zeroeightsix.kami.setting.settings.impl.collection.CollectionSetting
import me.zeroeightsix.kami.util.TickTimer
import me.zeroeightsix.kami.util.color.ColorHolder import me.zeroeightsix.kami.util.color.ColorHolder
import me.zeroeightsix.kami.util.graphics.ESPRenderer import me.zeroeightsix.kami.util.graphics.ESPRenderer
import me.zeroeightsix.kami.util.graphics.GeometryMasks
import me.zeroeightsix.kami.util.graphics.ShaderHelper import me.zeroeightsix.kami.util.graphics.ShaderHelper
import me.zeroeightsix.kami.util.math.VectorUtils.distanceTo import me.zeroeightsix.kami.util.math.VectorUtils.distanceTo
import me.zeroeightsix.kami.util.text.MessageSendHelper import me.zeroeightsix.kami.util.text.MessageSendHelper
import me.zeroeightsix.kami.util.text.formatValue import me.zeroeightsix.kami.util.text.formatValue
import me.zeroeightsix.kami.util.threads.defaultScope
import me.zeroeightsix.kami.util.threads.safeListener import me.zeroeightsix.kami.util.threads.safeListener
import net.minecraft.block.state.IBlockState
import net.minecraft.init.Blocks import net.minecraft.init.Blocks
import net.minecraft.util.math.AxisAlignedBB
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.ChunkPos import net.minecraft.util.math.ChunkPos
import net.minecraftforge.fml.common.gameevent.TickEvent import net.minecraft.util.math.Vec3d
import org.kamiblue.event.listener.listener import net.minecraft.world.chunk.Chunk
import java.util.* import java.util.*
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executors
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.collections.set import kotlin.collections.set
import kotlin.math.max
object Search : Module( object Search : Module(
name = "Search", name = "Search",
@ -33,189 +37,130 @@ object Search : Module(
) { ) {
private val defaultSearchList = linkedSetOf("minecraft:portal", "minecraft:end_portal_frame", "minecraft:bed") private val defaultSearchList = linkedSetOf("minecraft:portal", "minecraft:end_portal_frame", "minecraft:bed")
private val renderUpdate = setting("RenderUpdate", 1500, 500..3000, 100) private val updateDelay by setting("UpdateDelay", 1000, 500..3000, 50)
val overrideWarning = setting("OverrideWarning", false, { false }) private val range by setting("SearchRange", 128, 0..256, 8)
private val range = setting("SearchRange", 128, 0..256, 8) private val maximumBlocks by setting("MaximumBlocks", 256, 16..4096, 128)
private val maximumBlocks = setting("MaximumBlocks", 256, 16..4096, 128) private val filled by setting("Filled", true)
private val filled = setting("Filled", true) private val outline by setting("Outline", true)
private val outline = setting("Outline", true) private val tracer by setting("Tracer", true)
private val tracer = setting("Tracer", true) private val customColors by setting("CustomColors", false)
private val customColours = setting("CustomColours", false) private val r by setting("Red", 155, 0..255, 1, { customColors })
private val r = setting("Red", 155, 0..255, 1, { customColours.value }) private val g by setting("Green", 144, 0..255, 1, { customColors })
private val g = setting("Green", 144, 0..255, 1, { customColours.value }) private val b by setting("Blue", 255, 0..255, 1, { customColors })
private val b = setting("Blue", 255, 0..255, 1, { customColours.value }) private val aFilled by setting("FilledAlpha", 31, 0..255, 1, { filled })
private val aFilled = setting("FilledAlpha", 31, 0..255, 1, { filled.value }) private val aOutline by setting("OutlineAlpha", 127, 0..255, 1, { outline })
private val aOutline = setting("OutlineAlpha", 127, 0..255, 1, { outline.value }) private val aTracer by setting("TracerAlpha", 200, 0..255, 1, { tracer })
private val aTracer = setting("TracerAlpha", 200, 0..255, 1, { tracer.value }) private val thickness by setting("LineThickness", 2.0f, 0.25f..5.0f, 0.25f)
private val thickness = setting("LineThickness", 2.0f, 0.25f..5.0f, 0.25f)
var overrideWarning by setting("OverrideWarning", false, { false })
val searchList = setting(CollectionSetting("SearchList", defaultSearchList, { false })) val searchList = setting(CollectionSetting("SearchList", defaultSearchList, { false }))
private val chunkThreads = ConcurrentHashMap<ChunkPos, Thread>()
private val chunkThreadPool = Executors.newCachedThreadPool()
private val loadedChunks = ConcurrentSet<ChunkPos>()
private val mainList = ConcurrentHashMap<ChunkPos, List<BlockPos>>()
private val renderList = ConcurrentHashMap<BlockPos, ColorHolder>()
private val renderer = ESPRenderer() private val renderer = ESPRenderer()
private var dirty = 0 private val updateTimer = TickTimer()
private var startTimeChunk = 0L
private var startTimeRender = 0L
override fun getHudInfo(): String { override fun getHudInfo(): String {
return if (renderList.isNotEmpty()) renderList.size.toString() else "0" return renderer.getSize().toString()
} }
override fun onEnable() { override fun onEnable() {
if (!overrideWarning.value && ShaderHelper.isIntegratedGraphics) { if (!overrideWarning && ShaderHelper.isIntegratedGraphics) {
MessageSendHelper.sendErrorMessage("$chatName Warning: Running Search with an Intel Integrated GPU is not recommended, as it has a &llarge&r impact on performance.") MessageSendHelper.sendErrorMessage("$chatName Warning: Running Search with an Intel Integrated GPU is not recommended, as it has a &llarge&r impact on performance.")
MessageSendHelper.sendWarningMessage("$chatName If you're sure you want to try, run the ${formatValue("${CommandManager.prefix}search override")} command") MessageSendHelper.sendWarningMessage("$chatName If you're sure you want to try, run the ${formatValue("${CommandManager.prefix}search override")} command")
disable() disable()
return return
} }
startTimeChunk = 0L
startTimeRender = 0L
} }
init { init {
safeListener<TickEvent.ClientTickEvent> { safeListener<RenderWorldEvent> {
if (shouldUpdateChunk()) {
updateLoadedChunkList()
updateMainList()
}
if (shouldUpdateRender()) {
updateRenderList()
}
}
listener<RenderWorldEvent> {
if (dirty > 1) {
dirty = 0
renderer.clear()
for ((pos, colour) in renderList) renderer.add(pos, colour)
}
renderer.render(false) renderer.render(false)
if (updateTimer.tick(updateDelay.toLong())) {
updateRenderer()
}
} }
} }
/* Main list updating */ private fun SafeClientEvent.updateRenderer() {
private fun shouldUpdateChunk(): Boolean { defaultScope.launch {
return if (System.currentTimeMillis() - startTimeChunk < max(renderUpdate.value * 2, 500)) { val posMap = TreeMap<Double, Pair<BlockPos, IBlockState>>()
false
} else {
startTimeChunk = System.currentTimeMillis()
true
}
}
private fun SafeClientEvent.updateLoadedChunkList() { coroutineScope {
/* Removes unloaded chunks from the list */ launch {
Thread { updateAlpha()
for (chunkPos in loadedChunks) { }
if (isChunkLoaded(chunkPos)) continue launch {
chunkThreads.remove(chunkPos) val eyePos = player.getPositionEyes(1f)
loadedChunks.remove(chunkPos) getBlockPosList(eyePos, posMap)
mainList.remove(chunkPos) }
} }
/* Adds new loaded chunks to the list */ val renderList = ArrayList<Triple<AxisAlignedBB, ColorHolder, Int>>()
val renderDist = mc.gameSettings.renderDistanceChunks val sides = GeometryMasks.Quad.ALL
val playerChunkPos = ChunkPos(player.position)
val chunkPos1 = ChunkPos(playerChunkPos.x - renderDist, playerChunkPos.z - renderDist) for ((index, pair) in posMap.values.withIndex()) {
val chunkPos2 = ChunkPos(playerChunkPos.x + renderDist, playerChunkPos.z + renderDist) if (index >= maximumBlocks) break
val bb = pair.second.getSelectedBoundingBox(world, pair.first)
val color = getBlockColor(pair.first, pair.second)
renderList.add(Triple(bb, color, sides))
}
renderer.replaceAll(renderList)
}
}
private fun updateAlpha() {
renderer.aFilled = if (filled) aFilled else 0
renderer.aOutline = if (outline) aOutline else 0
renderer.aTracer = if (tracer) aTracer else 0
renderer.thickness = thickness
}
private suspend fun SafeClientEvent.getBlockPosList(eyePos: Vec3d, map: MutableMap<Double, Pair<BlockPos, IBlockState>>) {
val renderDist = mc.gameSettings.renderDistanceChunks
val playerChunkPos = ChunkPos(player.position)
val chunkPos1 = ChunkPos(playerChunkPos.x - renderDist, playerChunkPos.z - renderDist)
val chunkPos2 = ChunkPos(playerChunkPos.x + renderDist, playerChunkPos.z + renderDist)
coroutineScope {
for (x in chunkPos1.x..chunkPos2.x) for (z in chunkPos1.z..chunkPos2.z) { for (x in chunkPos1.x..chunkPos2.x) for (z in chunkPos1.z..chunkPos2.z) {
val chunk = world.getChunk(x, z) val chunk = world.getChunk(x, z)
if (!chunk.isLoaded) continue if (!chunk.isLoaded) continue
loadedChunks.add(chunk.pos) if (player.distanceTo(chunk.pos) > range + 16) continue
}
}.start()
}
private fun updateMainList() { launch {
Thread { findBlocksInChunk(chunk, eyePos, map)
for (chunkPos in loadedChunks) {
val thread = Thread {
findBlocksInChunk(chunkPos, searchList.toHashSet())
} }
thread.priority = 1 delay(1L)
chunkThreads.putIfAbsent(chunkPos, thread)
} }
for (thread in chunkThreads.values) { }
chunkThreadPool.execute(thread)
Thread.sleep(5L)
}
}.start()
} }
private fun findBlocksInChunk(chunkPos: ChunkPos, blocksToFind: HashSet<String>) { private fun findBlocksInChunk(chunk: Chunk, eyePos: Vec3d, map: MutableMap<Double, Pair<BlockPos, IBlockState>>) {
val yRange = IntRange(0, 256) val yRange = 0..256
val xRange = IntRange(chunkPos.xStart, chunkPos.xEnd) val xRange = (chunk.x shl 4)..(chunk.x shl 4) + 15
val zRange = IntRange(chunkPos.zStart, chunkPos.zEnd) val zRange = (chunk.z shl 4)..(chunk.z shl 4) + 15
val foundBlocks = ArrayList<BlockPos>()
for (y in yRange) for (x in xRange) for (z in zRange) { for (y in yRange) for (x in xRange) for (z in zRange) {
val blockPos = BlockPos(x, y, z) val pos = BlockPos(x, y, z)
val block = mc.world.getBlockState(blockPos).block val blockState = chunk.getBlockState(pos)
val block = blockState.block
if (block == Blocks.AIR) continue if (block == Blocks.AIR) continue
if (!blocksToFind.contains(block.registryName.toString())) continue if (!searchList.contains(block.registryName.toString())) continue
foundBlocks.add(BlockPos(blockPos))
}
mainList[chunkPos] = foundBlocks
}
/* End of main list updating */
/* Rendering */ val dist = eyePos.distanceTo(pos)
private fun shouldUpdateRender(): Boolean { if (dist > range) continue
return if (System.currentTimeMillis() - startTimeRender < renderUpdate.value) {
false map[dist] = (pos to blockState)
} else {
startTimeRender = System.currentTimeMillis()
true
} }
} }
private fun SafeClientEvent.updateRenderList() { private fun SafeClientEvent.getBlockColor(pos: BlockPos, blockState: IBlockState): ColorHolder {
Thread {
val cacheDistMap = TreeMap<Double, BlockPos>(Comparator.naturalOrder())
/* Calculates distance for all BlockPos, ignores the ones out of the setting range, and puts them into the cacheMap to sort them */
for (posList in mainList.values) {
for (i in posList.indices) {
val pos = posList[i]
val distance = player.distanceTo(pos)
if (distance > range.value) continue
cacheDistMap[distance] = pos
}
}
/* Removes the furthest blocks to keep it in the maximum block limit */
while (cacheDistMap.size > maximumBlocks.value) {
cacheDistMap.pollLastEntry()
}
renderList.keys.removeIf { pos ->
!cacheDistMap.containsValue(pos)
}
for (pos in cacheDistMap.values) {
renderList[pos] = getPosColor(pos)
}
/* Updates renderer */
renderer.aFilled = if (filled.value) aFilled.value else 0
renderer.aOutline = if (outline.value) aOutline.value else 0
renderer.aTracer = if (tracer.value) aTracer.value else 0
renderer.thickness = thickness.value
if (renderList.size != renderer.getSize()) {
dirty = 2
} else {
dirty++
}
}.start()
}
private fun SafeClientEvent.getPosColor(pos: BlockPos): ColorHolder {
val blockState = world.getBlockState(pos)
val block = blockState.block val block = blockState.block
return if (!customColours.value) {
return if (!customColors) {
if (block == Blocks.PORTAL) { if (block == Blocks.PORTAL) {
ColorHolder(82, 49, 153) ColorHolder(82, 49, 153)
} else { } else {
@ -223,12 +168,8 @@ object Search : Module(
ColorHolder((colorInt shr 16), (colorInt shr 8 and 255), (colorInt and 255)) ColorHolder((colorInt shr 16), (colorInt shr 8 and 255), (colorInt and 255))
} }
} else { } else {
ColorHolder(r.value, g.value, b.value) ColorHolder(r, g, b)
} }
} }
/* End of rendering */
private fun SafeClientEvent.isChunkLoaded(chunkPos: ChunkPos): Boolean {
return world.getChunk(chunkPos.x, chunkPos.z).isLoaded
}
} }

View File

@ -1,5 +1,9 @@
package me.zeroeightsix.kami.module.modules.render package me.zeroeightsix.kami.module.modules.render
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import me.zeroeightsix.kami.event.SafeClientEvent
import me.zeroeightsix.kami.event.events.RenderWorldEvent import me.zeroeightsix.kami.event.events.RenderWorldEvent
import me.zeroeightsix.kami.module.Module import me.zeroeightsix.kami.module.Module
import me.zeroeightsix.kami.setting.ModuleConfig.setting import me.zeroeightsix.kami.setting.ModuleConfig.setting
@ -8,7 +12,7 @@ import me.zeroeightsix.kami.util.color.DyeColors
import me.zeroeightsix.kami.util.color.HueCycler import me.zeroeightsix.kami.util.color.HueCycler
import me.zeroeightsix.kami.util.graphics.ESPRenderer import me.zeroeightsix.kami.util.graphics.ESPRenderer
import me.zeroeightsix.kami.util.graphics.GeometryMasks import me.zeroeightsix.kami.util.graphics.GeometryMasks
import me.zeroeightsix.kami.util.threads.safeListener import me.zeroeightsix.kami.util.threads.safeAsyncListener
import net.minecraft.entity.Entity import net.minecraft.entity.Entity
import net.minecraft.entity.item.* import net.minecraft.entity.item.*
import net.minecraft.item.ItemShulkerBox import net.minecraft.item.ItemShulkerBox
@ -16,103 +20,118 @@ import net.minecraft.tileentity.*
import net.minecraft.util.math.AxisAlignedBB import net.minecraft.util.math.AxisAlignedBB
import net.minecraftforge.fml.common.gameevent.TickEvent import net.minecraftforge.fml.common.gameevent.TickEvent
import org.kamiblue.event.listener.listener import org.kamiblue.event.listener.listener
import java.util.concurrent.ConcurrentHashMap
object StorageESP : Module( object StorageESP : Module(
name = "StorageESP", name = "StorageESP",
description = "Draws an ESP on top of storage units", description = "Draws an ESP on top of storage units",
category = Category.RENDER category = Category.RENDER
) { ) {
private val page = setting("Page", Page.TYPE) private val page by setting("Page", Page.TYPE)
/* Type settings */ /* Type settings */
private val chest = setting("Chest", true, { page.value == Page.TYPE }) private val chest by setting("Chest", true, { page == Page.TYPE })
private val shulker = setting("Shulker", true, { page.value == Page.TYPE }) private val shulker by setting("Shulker", true, { page == Page.TYPE })
private val enderChest = setting("EnderChest", true, { page.value == Page.TYPE }) private val enderChest by setting("EnderChest", true, { page == Page.TYPE })
private val frame = setting("ItemFrame", true, { page.value == Page.TYPE }) private val frame by setting("ItemFrame", true, { page == Page.TYPE })
private val frameShulker = setting("ItFShulkerOnly", true, { frame.value && page.value == Page.TYPE }) private val withShulkerOnly by setting("WithShulkerOnly", true, { page == Page.TYPE && frame })
private val furnace = setting("Furnace", false, { page.value == Page.TYPE }) private val furnace by setting("Furnace", false, { page == Page.TYPE })
private val dispenser = setting("Dispenser", false, { page.value == Page.TYPE }) private val dispenser by setting("Dispenser", false, { page == Page.TYPE })
private val hopper = setting("Hopper", false, { page.value == Page.TYPE }) private val hopper by setting("Hopper", false, { page == Page.TYPE })
private val cart = setting("Minecart", false, { page.value == Page.TYPE }) private val cart by setting("Minecart", false, { page == Page.TYPE })
/* Color settings */ /* Color settings */
private val colorChest = setting("ChestColor", DyeColors.ORANGE, { page.value == Page.COLOR }) private val colorChest by setting("ChestColor", DyeColors.ORANGE, { page == Page.COLOR })
private val colorDispenser = setting("DispenserColor", DyeColors.LIGHT_GRAY, { page.value == Page.COLOR }) private val colorDispenser by setting("DispenserColor", DyeColors.LIGHT_GRAY, { page == Page.COLOR })
private val colorShulker = setting("ShulkerColor", DyeColors.MAGENTA, { page.value == Page.COLOR }) private val colorShulker by setting("ShulkerColor", DyeColors.MAGENTA, { page == Page.COLOR })
private val colorEnderChest = setting("EnderChestColor", DyeColors.PURPLE, { page.value == Page.COLOR }) private val colorEnderChest by setting("EnderChestColor", DyeColors.PURPLE, { page == Page.COLOR })
private val colorFurnace = setting("FurnaceColor", DyeColors.LIGHT_GRAY, { page.value == Page.COLOR }) private val colorFurnace by setting("FurnaceColor", DyeColors.LIGHT_GRAY, { page == Page.COLOR })
private val colorHopper = setting("HopperColor", DyeColors.GRAY, { page.value == Page.COLOR }) private val colorHopper by setting("HopperColor", DyeColors.GRAY, { page == Page.COLOR })
private val colorCart = setting("CartColor", DyeColors.GREEN, { page.value == Page.COLOR }) private val colorCart by setting("CartColor", DyeColors.GREEN, { page == Page.COLOR })
private val colorFrame = setting("FrameColor", DyeColors.ORANGE, { page.value == Page.COLOR }) private val colorFrame by setting("FrameColor", DyeColors.ORANGE, { page == Page.COLOR })
/* Render settings */ /* Render settings */
private val filled = setting("Filled", true, { page.value == Page.RENDER }) private val filled by setting("Filled", true, { page == Page.RENDER })
private val outline = setting("Outline", true, { page.value == Page.RENDER }) private val outline by setting("Outline", true, { page == Page.RENDER })
private val tracer = setting("Tracer", false, { page.value == Page.RENDER }) private val tracer by setting("Tracer", false, { page == Page.RENDER })
private val cull = setting("Culling", true, { page.value == Page.RENDER }) private val aFilled by setting("FilledAlpha", 31, 0..255, 1, { page == Page.RENDER && filled })
private val aFilled = setting("FilledAlpha", 31, 0..255, 1, { page.value == Page.RENDER && filled.value }) private val aOutline by setting("OutlineAlpha", 127, 0..255, 1, { page == Page.RENDER && outline })
private val aOutline = setting("OutlineAlpha", 127, 0..255, 1, { page.value == Page.RENDER && outline.value }) private val aTracer by setting("TracerAlpha", 200, 0..255, 1, { page == Page.RENDER && tracer })
private val aTracer = setting("TracerAlpha", 200, 0..255, 1, { page.value == Page.RENDER && tracer.value }) private val thickness by setting("LineThickness", 2.0f, 0.25f..5.0f, 0.25f, { page == Page.RENDER })
private val thickness = setting("LineThickness", 2.0f, 0.25f..5.0f, 0.25f, { page.value == Page.RENDER })
private enum class Page { private enum class Page {
TYPE, COLOR, RENDER TYPE, COLOR, RENDER
} }
private val renderList = ConcurrentHashMap<AxisAlignedBB, Pair<ColorHolder, Int>>() override fun getHudInfo(): String {
return renderer.getSize().toString()
}
private var cycler = HueCycler(600) private var cycler = HueCycler(600)
private val renderer = ESPRenderer()
init { init {
listener<RenderWorldEvent> { listener<RenderWorldEvent> {
val renderer = ESPRenderer() renderer.render(false)
renderer.aFilled = if (filled.value) aFilled.value else 0
renderer.aOutline = if (outline.value) aOutline.value else 0
renderer.aTracer = if (tracer.value) aTracer.value else 0
renderer.thickness = thickness.value
for ((box, pair) in renderList) {
renderer.add(box, pair.first, pair.second)
}
renderer.render(true, cull.value)
} }
safeListener<TickEvent.ClientTickEvent> { safeAsyncListener<TickEvent.ClientTickEvent> {
if (it.phase != TickEvent.Phase.START) return@safeAsyncListener
cycler++ cycler++
renderList.clear() renderer.clear()
for (tileEntity in world.loadedTileEntityList) { val cached = ArrayList<Triple<AxisAlignedBB, ColorHolder, Int>>()
if (tileEntity is TileEntityChest && chest.value
|| tileEntity is TileEntityDispenser && dispenser.value coroutineScope {
|| tileEntity is TileEntityShulkerBox && shulker.value launch(Dispatchers.Default) {
|| tileEntity is TileEntityEnderChest && enderChest.value updateRenderer()
|| tileEntity is TileEntityFurnace && furnace.value }
|| tileEntity is TileEntityHopper && hopper.value) { launch(Dispatchers.Default) {
val box = world.getBlockState(tileEntity.pos).getSelectedBoundingBox(world, tileEntity.pos) updateTileEntities(cached)
val color = getTileEntityColor(tileEntity) ?: continue }
var side = GeometryMasks.Quad.ALL launch(Dispatchers.Default) {
if (tileEntity is TileEntityChest) { updateEntities(cached)
// Leave only the colliding face and then flip the bits (~) to have ALL but that face
if (tileEntity.adjacentChestZNeg != null) side = (side and GeometryMasks.Quad.NORTH).inv()
if (tileEntity.adjacentChestXPos != null) side = (side and GeometryMasks.Quad.EAST).inv()
if (tileEntity.adjacentChestZPos != null) side = (side and GeometryMasks.Quad.SOUTH).inv()
if (tileEntity.adjacentChestXNeg != null) side = (side and GeometryMasks.Quad.WEST).inv()
}
renderList[box] = Pair(color, side)
} }
} }
for (entity in world.loadedEntityList) { renderer.replaceAll(cached)
if (entity is EntityItemFrame && frameShulkerOrAny(entity)
|| (entity is EntityMinecartChest
|| entity is EntityMinecartHopper
|| entity is EntityMinecartFurnace) && cart.value) {
val box = entity.renderBoundingBox
val color = getEntityColor(entity) ?: continue
renderList[box] = Pair(color, GeometryMasks.Quad.ALL)
}
}
} }
} }
private fun updateRenderer() {
renderer.aFilled = if (filled) aFilled else 0
renderer.aOutline = if (outline) aOutline else 0
renderer.aTracer = if (tracer) aTracer else 0
renderer.thickness = thickness
}
private fun SafeClientEvent.updateTileEntities(list: MutableList<Triple<AxisAlignedBB, ColorHolder, Int>>) {
for (tileEntity in world.loadedTileEntityList.toList()) {
if (!checkTileEntityType(tileEntity)) continue
val box = world.getBlockState(tileEntity.pos).getSelectedBoundingBox(world, tileEntity.pos)
val color = getTileEntityColor(tileEntity) ?: continue
var side = GeometryMasks.Quad.ALL
if (tileEntity is TileEntityChest) {
// Leave only the colliding face and then flip the bits (~) to have ALL but that face
if (tileEntity.adjacentChestZNeg != null) side = (side and GeometryMasks.Quad.NORTH).inv()
if (tileEntity.adjacentChestXPos != null) side = (side and GeometryMasks.Quad.EAST).inv()
if (tileEntity.adjacentChestZPos != null) side = (side and GeometryMasks.Quad.SOUTH).inv()
if (tileEntity.adjacentChestXNeg != null) side = (side and GeometryMasks.Quad.WEST).inv()
}
list.add(Triple(box, color, side))
}
}
private fun checkTileEntityType(tileEntity: TileEntity) =
chest && tileEntity is TileEntityChest
|| dispenser && tileEntity is TileEntityDispenser
|| shulker && tileEntity is TileEntityShulkerBox
|| enderChest && tileEntity is TileEntityEnderChest
|| furnace && tileEntity is TileEntityFurnace
|| hopper && tileEntity is TileEntityHopper
private fun getTileEntityColor(tileEntity: TileEntity): ColorHolder? { private fun getTileEntityColor(tileEntity: TileEntity): ColorHolder? {
val color = when (tileEntity) { val color = when (tileEntity) {
is TileEntityChest -> colorChest is TileEntityChest -> colorChest
@ -122,28 +141,38 @@ object StorageESP : Module(
is TileEntityFurnace -> colorFurnace is TileEntityFurnace -> colorFurnace
is TileEntityHopper -> colorHopper is TileEntityHopper -> colorHopper
else -> return null else -> return null
}.value.color }.color
return if (color == DyeColors.RAINBOW.color) { return if (color == DyeColors.RAINBOW.color) {
cycler.currentRgb() cycler.currentRgb()
} else color } else color
} }
private fun SafeClientEvent.updateEntities(list: MutableList<Triple<AxisAlignedBB, ColorHolder, Int>>) {
for (entity in world.loadedEntityList.toList()) {
if (!checkEntityType(entity)) continue
val box = entity.renderBoundingBox
val color = getEntityColor(entity) ?: continue
list.add(Triple(box, color, GeometryMasks.Quad.ALL))
}
}
private fun checkEntityType(entity: Entity) =
entity is EntityItemFrame && frameShulkerOrAny(entity)
|| (entity is EntityMinecartChest || entity is EntityMinecartHopper || entity is EntityMinecartFurnace) && cart
private fun getEntityColor(entity: Entity): ColorHolder? { private fun getEntityColor(entity: Entity): ColorHolder? {
val color = when (entity) { val color = when (entity) {
is EntityMinecartContainer -> colorCart is EntityMinecartContainer -> colorCart
is EntityItemFrame -> colorFrame is EntityItemFrame -> colorFrame
else -> return null else -> return null
}.value.color }.color
return if (color == DyeColors.RAINBOW.color) { return if (color == DyeColors.RAINBOW.color) {
cycler.currentRgb() cycler.currentRgb()
} else color } else color
} }
private fun frameShulkerOrAny(e: EntityItemFrame): Boolean { private fun frameShulkerOrAny(entity: EntityItemFrame) =
return when { frame && (!withShulkerOnly || entity.displayedItem.item is ItemShulkerBox)
!frame.value -> false
!frameShulker.value -> true
else -> e.displayedItem.getItem() is ItemShulkerBox
}
}
} }

View File

@ -1,32 +1,32 @@
package me.zeroeightsix.kami.module.modules.render package me.zeroeightsix.kami.module.modules.render
import kotlinx.coroutines.launch
import me.zeroeightsix.kami.event.SafeClientEvent import me.zeroeightsix.kami.event.SafeClientEvent
import me.zeroeightsix.kami.event.events.RenderWorldEvent import me.zeroeightsix.kami.event.events.RenderWorldEvent
import me.zeroeightsix.kami.module.Module import me.zeroeightsix.kami.module.Module
import me.zeroeightsix.kami.setting.ModuleConfig.setting import me.zeroeightsix.kami.setting.ModuleConfig.setting
import me.zeroeightsix.kami.util.TickTimer
import me.zeroeightsix.kami.util.color.ColorHolder import me.zeroeightsix.kami.util.color.ColorHolder
import me.zeroeightsix.kami.util.graphics.ESPRenderer import me.zeroeightsix.kami.util.graphics.ESPRenderer
import me.zeroeightsix.kami.util.graphics.GeometryMasks import me.zeroeightsix.kami.util.graphics.GeometryMasks
import me.zeroeightsix.kami.util.math.VectorUtils.distanceTo import me.zeroeightsix.kami.util.math.VectorUtils.distanceTo
import me.zeroeightsix.kami.util.threads.defaultScope
import me.zeroeightsix.kami.util.threads.safeListener import me.zeroeightsix.kami.util.threads.safeListener
import net.minecraft.util.math.AxisAlignedBB
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
import net.minecraftforge.fml.common.gameevent.TickEvent
import org.kamiblue.event.listener.listener
object VoidESP : Module( object VoidESP : Module(
name = "VoidESP", name = "VoidESP",
description = "Highlights holes leading to the void", description = "Highlights holes leading to the void",
category = Category.RENDER category = Category.RENDER
) { ) {
private val renderDistance = setting("RenderDistance", 6, 4..32, 1) private val filled by setting("Filled", true)
private val filled = setting("Filled", true) private val outline by setting("Outline", true)
private val outline = setting("Outline", true) private val color by setting("Color", ColorHolder(148, 161, 255), false)
private val r = setting("Red", 148, 0..255, 1) private val aFilled by setting("FilledAlpha", 127, 0..255, 1)
private val g = setting("Green", 161, 0..255, 1) private val aOutline by setting("OutlineAlpha", 255, 0..255, 1)
private val b = setting("Blue", 255, 0..255, 1) private val renderMode by setting("Mode", Mode.BLOCK_HOLE)
private val aFilled = setting("FilledAlpha", 127, 0..255, 1) private val range by setting("Range", 8, 4..32, 1)
private val aOutline = setting("OutlineAlpha", 255, 0..255, 1)
private val renderMode = setting("Mode", Mode.BLOCK_HOLE)
@Suppress("UNUSED") @Suppress("UNUSED")
private enum class Mode { private enum class Mode {
@ -34,32 +34,42 @@ object VoidESP : Module(
} }
private val renderer = ESPRenderer() private val renderer = ESPRenderer()
private val timer = TickTimer()
init { init {
safeListener<TickEvent.ClientTickEvent> { safeListener<RenderWorldEvent> {
if (it.phase != TickEvent.Phase.END) return@safeListener if (timer.tick(133L)) { // Avoid running this on a tick
renderer.clear() updateRenderer()
renderer.aFilled = if (filled.value) aFilled.value else 0
renderer.aOutline = if (outline.value) aOutline.value else 0
val color = ColorHolder(r.value, g.value, b.value)
val side = if (renderMode.value != Mode.FLAT) GeometryMasks.Quad.ALL else GeometryMasks.Quad.DOWN
for (x in -renderDistance.value..renderDistance.value) for (z in -renderDistance.value..renderDistance.value) {
val pos = BlockPos(player.posX + x, 0.0, player.posZ + z)
if (player.distanceTo(pos) > renderDistance.value) continue
if (!isVoid(pos)) continue
val renderPos = if (renderMode.value == Mode.BLOCK_VOID) pos.down() else pos
renderer.add(renderPos, color, side)
} }
}
listener<RenderWorldEvent> {
renderer.render(false) renderer.render(false)
} }
} }
private fun SafeClientEvent.updateRenderer() {
renderer.aFilled = if (filled) aFilled else 0
renderer.aOutline = if (outline) aOutline else 0
val color = color.clone()
val side = if (renderMode != Mode.FLAT) GeometryMasks.Quad.ALL else GeometryMasks.Quad.DOWN
defaultScope.launch {
val cached = ArrayList<Triple<AxisAlignedBB, ColorHolder, Int>>()
for (x in -range..range) for (z in -range..range) {
val pos = BlockPos(player.posX + x, 0.0, player.posZ + z)
if (player.distanceTo(pos) > range) continue
if (!isVoid(pos)) continue
val renderPos = if (renderMode == Mode.BLOCK_VOID) pos.down() else pos
cached.add(Triple(AxisAlignedBB(renderPos), color, side))
}
renderer.replaceAll(cached)
}
}
private fun SafeClientEvent.isVoid(pos: BlockPos) = world.isAirBlock(pos) private fun SafeClientEvent.isVoid(pos: BlockPos) = world.isAirBlock(pos)
&& world.isAirBlock(pos.up()) && world.isAirBlock(pos.up())
&& world.isAirBlock(pos.up().up()) && world.isAirBlock(pos.up().up())
} }

View File

@ -1,7 +1,10 @@
package me.zeroeightsix.kami.util.combat package me.zeroeightsix.kami.util.combat
import me.zeroeightsix.kami.event.ClientEvent
import me.zeroeightsix.kami.event.SafeClientEvent
import me.zeroeightsix.kami.util.EntityUtils.flooredPosition import me.zeroeightsix.kami.util.EntityUtils.flooredPosition
import me.zeroeightsix.kami.util.Wrapper import me.zeroeightsix.kami.util.Wrapper
import me.zeroeightsix.kami.util.threads.toSafe
import net.minecraft.block.Block import net.minecraft.block.Block
import net.minecraft.entity.Entity import net.minecraft.entity.Entity
import net.minecraft.init.Blocks import net.minecraft.init.Blocks
@ -39,14 +42,15 @@ object SurroundUtils {
return checkHole(entity.flooredPosition) return checkHole(entity.flooredPosition)
} }
@JvmStatic fun checkHole(pos: BlockPos) = ClientEvent().toSafe()?.checkHole(pos) ?: HoleType.NONE
fun checkHole(pos: BlockPos): HoleType {
fun SafeClientEvent.checkHole(pos: BlockPos): HoleType {
// Must be a 1 * 3 * 1 empty space // Must be a 1 * 3 * 1 empty space
if (!mc.world.isAirBlock(pos) || !mc.world.isAirBlock(pos.up()) || !mc.world.isAirBlock(pos.up().up())) return HoleType.NONE if (!world.isAirBlock(pos) || !world.isAirBlock(pos.up()) || !world.isAirBlock(pos.up().up())) return HoleType.NONE
var type = HoleType.BEDROCK var type = HoleType.BEDROCK
for (offset in surroundOffset) { for (offset in surroundOffset) {
val block = mc.world.getBlockState(pos.add(offset)).block val block = world.getBlockState(pos.add(offset)).block
if (!checkBlock(block)) { if (!checkBlock(block)) {
type = HoleType.NONE type = HoleType.NONE
break break
@ -56,7 +60,6 @@ object SurroundUtils {
return type return type
} }
@JvmStatic
fun checkBlock(block: Block): Boolean { fun checkBlock(block: Block): Boolean {
return block == Blocks.BEDROCK || block == Blocks.OBSIDIAN || block == Blocks.ENDER_CHEST || block == Blocks.ANVIL return block == Blocks.BEDROCK || block == Blocks.OBSIDIAN || block == Blocks.ENDER_CHEST || block == Blocks.ANVIL
} }

View File

@ -1,15 +1,15 @@
package me.zeroeightsix.kami.util.graphics package me.zeroeightsix.kami.util.graphics
import me.zeroeightsix.kami.util.EntityUtils
import me.zeroeightsix.kami.util.EntityUtils.getInterpolatedAmount import me.zeroeightsix.kami.util.EntityUtils.getInterpolatedAmount
import me.zeroeightsix.kami.util.Wrapper
import me.zeroeightsix.kami.util.color.ColorHolder import me.zeroeightsix.kami.util.color.ColorHolder
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.GlStateManager import net.minecraft.client.renderer.GlStateManager
import net.minecraft.client.renderer.culling.Frustum import net.minecraft.client.renderer.culling.Frustum
import net.minecraft.client.renderer.culling.ICamera import net.minecraft.client.renderer.culling.ICamera
import net.minecraft.entity.Entity import net.minecraft.entity.Entity
import net.minecraft.util.math.AxisAlignedBB import net.minecraft.util.math.AxisAlignedBB
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Vec3d
import org.lwjgl.opengl.GL11.GL_LINES import org.lwjgl.opengl.GL11.GL_LINES
import org.lwjgl.opengl.GL11.GL_QUADS import org.lwjgl.opengl.GL11.GL_QUADS
@ -19,10 +19,8 @@ import org.lwjgl.opengl.GL11.GL_QUADS
* Created by Xiaro on 30/07/20 * Created by Xiaro on 30/07/20
*/ */
class ESPRenderer { class ESPRenderer {
private lateinit var camPos: Vec3d
private val frustumCamera: ICamera = Frustum() private val frustumCamera: ICamera = Frustum()
private val mc = Minecraft.getMinecraft() private var toRender: MutableList<Triple<AxisAlignedBB, ColorHolder, Int>> = ArrayList()
private val toRender = HashMap<AxisAlignedBB, Pair<ColorHolder, Int>>()
var aFilled = 0 var aFilled = 0
var aOutline = 0 var aOutline = 0
@ -58,15 +56,28 @@ class ESPRenderer {
} }
fun add(box: AxisAlignedBB, color: ColorHolder, sides: Int) { fun add(box: AxisAlignedBB, color: ColorHolder, sides: Int) {
toRender[box] = Pair(color, sides) add(Triple(box, color, sides))
}
fun add(triple: Triple<AxisAlignedBB, ColorHolder, Int>) {
toRender.add(triple)
}
fun replaceAll(list: MutableList<Triple<AxisAlignedBB, ColorHolder, Int>>) {
toRender = list
} }
fun clear() { fun clear() {
toRender.clear() toRender.clear()
} }
fun render(clear: Boolean, cull: Boolean = false) { fun render(clear: Boolean, cull: Boolean = true) {
if (toRender.isEmpty() && (aFilled == 0 && aOutline == 0 && aTracer == 0)) return if (toRender.isEmpty() && (aFilled == 0 && aOutline == 0 && aTracer == 0)) return
val entity = Wrapper.minecraft.renderViewEntity ?: Wrapper.player ?: return
val interpolatedPos = EntityUtils.getInterpolatedPos(entity, KamiTessellator.pTicks())
frustumCamera.setPosition(interpolatedPos.x, interpolatedPos.y, interpolatedPos.z)
if (through) GlStateManager.disableDepth() if (through) GlStateManager.disableDepth()
if (aFilled != 0) drawList(Type.FILLED, cull) if (aFilled != 0) drawList(Type.FILLED, cull)
@ -81,41 +92,39 @@ class ESPRenderer {
private fun drawList(type: Type, cull: Boolean = false) { private fun drawList(type: Type, cull: Boolean = false) {
KamiTessellator.begin(if (type == Type.FILLED) GL_QUADS else GL_LINES) KamiTessellator.begin(if (type == Type.FILLED) GL_QUADS else GL_LINES)
camPos = KamiTessellator.camPos.add(0.0, (-mc.player.eyeHeight).toDouble(), 0.0) // realign camPos to player eye pos
frustumCamera.setPosition(camPos.x, camPos.y, camPos.z)
for ((box, pair) in toRender) when (type) { for ((box, color, sides) in toRender) when (type) {
Type.FILLED -> drawFilled(cull, box, pair) Type.FILLED -> drawFilled(cull, box, color, sides)
Type.OUTLINE -> drawOutline(cull, box, pair) Type.OUTLINE -> drawOutline(cull, box, color, sides)
Type.TRACER -> drawTracer(box, pair) Type.TRACER -> drawTracer(box, color)
} }
KamiTessellator.render() KamiTessellator.render()
} }
private fun drawFilled(cull: Boolean, box: AxisAlignedBB, pair: Pair<ColorHolder, Int>) { private fun drawFilled(cull: Boolean, box: AxisAlignedBB, color: ColorHolder, sides: Int) {
val a = (aFilled * (pair.first.a / 255f)).toInt() val a = (aFilled * (color.a / 255f)).toInt()
if (!cull || frustumCamera.isBoundingBoxInFrustum(box)) { if (!cull || frustumCamera.isBoundingBoxInFrustum(box)) {
KamiTessellator.drawBox(box, pair.first, a, pair.second) KamiTessellator.drawBox(box, color, a, sides)
} }
} }
private fun drawOutline(cull: Boolean, box: AxisAlignedBB, pair: Pair<ColorHolder, Int>) { private fun drawOutline(cull: Boolean, box: AxisAlignedBB, color: ColorHolder, sides: Int) {
val a = (aOutline * (pair.first.a / 255f)).toInt() val a = (aOutline * (color.a / 255f)).toInt()
val side = if (fullOutline) GeometryMasks.Quad.ALL else pair.second val side = if (fullOutline) GeometryMasks.Quad.ALL else sides
if (!cull || frustumCamera.isBoundingBoxInFrustum(box)) { if (!cull || frustumCamera.isBoundingBoxInFrustum(box)) {
KamiTessellator.drawOutline(box, pair.first, a, side, thickness) KamiTessellator.drawOutline(box, color, a, side, thickness)
} }
} }
private fun drawTracer(box: AxisAlignedBB, pair: Pair<ColorHolder, Int>) { private fun drawTracer(box: AxisAlignedBB, color: ColorHolder) {
val a = (aTracer * (pair.first.a / 255f)).toInt() val a = (aTracer * (color.a / 255f)).toInt()
val offset = (tracerOffset - 50) / 100.0 * (box.maxY - box.minY) val offset = (tracerOffset - 50) / 100.0 * (box.maxY - box.minY)
val offsetBox = box.center.add(0.0, offset, 0.0) val offsetBox = box.center.add(0.0, offset, 0.0)
KamiTessellator.drawLineTo(offsetBox, pair.first, a, thickness) KamiTessellator.drawLineTo(offsetBox, color, a, thickness)
} }
private enum class Type { private enum class Type {