[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") {
execute("Override the Intel Integrated GPU check") {
Search.overrideWarning.value = true
Search.overrideWarning = true
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.TextComponent
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.minecraftforge.fml.common.gameevent.TickEvent
import org.kamiblue.commons.extension.sumByFloat
@ -67,8 +67,8 @@ object ModuleList : HudElement(
.toMutableMap()
init {
safeAsyncListener<TickEvent.ClientTickEvent> { event ->
if (event.phase != TickEvent.Phase.END) return@safeAsyncListener
safeListener<TickEvent.ClientTickEvent> { event ->
if (event.phase != TickEvent.Phase.END) return@safeListener
val moduleSet = ModuleManager.modules.toSet()

View File

@ -1,22 +1,27 @@
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.module.Module
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.combat.SurroundUtils
import me.zeroeightsix.kami.util.combat.SurroundUtils.checkHole
import me.zeroeightsix.kami.util.graphics.ESPRenderer
import me.zeroeightsix.kami.util.graphics.GeometryMasks
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(
name = "HoleESP",
category = Category.COMBAT,
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 outline = setting("Outline", true)
private val hideOwn = setting("HideOwn", true)
@ -43,38 +48,46 @@ object HoleESP : Module(
private val timer = TickTimer()
init {
listener<RenderWorldEvent> {
if (mc.world == null || mc.player == null) return@listener
if (timer.tick(133L)) updateRenderer() // Avoid running this on a tick
safeListener<RenderWorldEvent> {
if (timer.tick(133L)) { // Avoid running this on a tick
updateRenderer()
}
renderer.render(false)
}
}
private fun updateRenderer() {
renderer.clear()
private fun SafeClientEvent.updateRenderer() {
renderer.aFilled = if (filled.value) aFilled.value else 0
renderer.aOutline = if (outline.value) aOutline.value else 0
val colorObsidian = ColorHolder(r1.value, g1.value, b1.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
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) {
if (hideOwn.value && x == 0 && y == 0 && z == 0) continue
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
defaultScope.launch {
val cached = ArrayList<Triple<AxisAlignedBB, ColorHolder, Int>>()
if (holeType == SurroundUtils.HoleType.OBBY && shouldAddObsidian()) {
renderer.add(renderPos, colorObsidian, side)
for (x in -range.value..range.value) for (y in -range.value..range.value) for (z in -range.value..range.value) {
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.add(renderPos, colorBedrock, side)
}
renderer.replaceAll(cached)
}
}

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
when (mode.value) {
ESPMode.BOX -> {
val colour = ColorHolder(r.value, g.value, b.value)
val color = ColorHolder(r.value, g.value, b.value)
val renderer = ESPRenderer()
renderer.aFilled = if (filled.value) aFilled.value else 0
renderer.aOutline = if (outline.value) aOutline.value else 0
renderer.thickness = width.value
for (entity in entityList) {
renderer.add(entity, colour)
renderer.add(entity, color)
}
renderer.render(true)
}

View File

@ -1,30 +1,34 @@
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.event.SafeClientEvent
import me.zeroeightsix.kami.event.events.RenderWorldEvent
import me.zeroeightsix.kami.module.Module
import me.zeroeightsix.kami.setting.ModuleConfig.setting
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.graphics.ESPRenderer
import me.zeroeightsix.kami.util.graphics.GeometryMasks
import me.zeroeightsix.kami.util.graphics.ShaderHelper
import me.zeroeightsix.kami.util.math.VectorUtils.distanceTo
import me.zeroeightsix.kami.util.text.MessageSendHelper
import me.zeroeightsix.kami.util.text.formatValue
import me.zeroeightsix.kami.util.threads.defaultScope
import me.zeroeightsix.kami.util.threads.safeListener
import net.minecraft.block.state.IBlockState
import net.minecraft.init.Blocks
import net.minecraft.util.math.AxisAlignedBB
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.ChunkPos
import net.minecraftforge.fml.common.gameevent.TickEvent
import org.kamiblue.event.listener.listener
import net.minecraft.util.math.Vec3d
import net.minecraft.world.chunk.Chunk
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executors
import kotlin.collections.ArrayList
import kotlin.collections.set
import kotlin.math.max
object Search : Module(
name = "Search",
@ -33,189 +37,130 @@ object Search : Module(
) {
private val defaultSearchList = linkedSetOf("minecraft:portal", "minecraft:end_portal_frame", "minecraft:bed")
private val renderUpdate = setting("RenderUpdate", 1500, 500..3000, 100)
val overrideWarning = setting("OverrideWarning", false, { false })
private val range = setting("SearchRange", 128, 0..256, 8)
private val maximumBlocks = setting("MaximumBlocks", 256, 16..4096, 128)
private val filled = setting("Filled", true)
private val outline = setting("Outline", true)
private val tracer = setting("Tracer", true)
private val customColours = setting("CustomColours", false)
private val r = setting("Red", 155, 0..255, 1, { customColours.value })
private val g = setting("Green", 144, 0..255, 1, { customColours.value })
private val b = setting("Blue", 255, 0..255, 1, { customColours.value })
private val aFilled = setting("FilledAlpha", 31, 0..255, 1, { filled.value })
private val aOutline = setting("OutlineAlpha", 127, 0..255, 1, { outline.value })
private val aTracer = setting("TracerAlpha", 200, 0..255, 1, { tracer.value })
private val thickness = setting("LineThickness", 2.0f, 0.25f..5.0f, 0.25f)
private val updateDelay by setting("UpdateDelay", 1000, 500..3000, 50)
private val range by setting("SearchRange", 128, 0..256, 8)
private val maximumBlocks by setting("MaximumBlocks", 256, 16..4096, 128)
private val filled by setting("Filled", true)
private val outline by setting("Outline", true)
private val tracer by setting("Tracer", true)
private val customColors by setting("CustomColors", false)
private val r by setting("Red", 155, 0..255, 1, { customColors })
private val g by setting("Green", 144, 0..255, 1, { customColors })
private val b by setting("Blue", 255, 0..255, 1, { customColors })
private val aFilled by setting("FilledAlpha", 31, 0..255, 1, { filled })
private val aOutline by setting("OutlineAlpha", 127, 0..255, 1, { outline })
private val aTracer by setting("TracerAlpha", 200, 0..255, 1, { tracer })
private val thickness by setting("LineThickness", 2.0f, 0.25f..5.0f, 0.25f)
var overrideWarning by setting("OverrideWarning", false, { 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 var dirty = 0
private var startTimeChunk = 0L
private var startTimeRender = 0L
private val updateTimer = TickTimer()
override fun getHudInfo(): String {
return if (renderList.isNotEmpty()) renderList.size.toString() else "0"
return renderer.getSize().toString()
}
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.sendWarningMessage("$chatName If you're sure you want to try, run the ${formatValue("${CommandManager.prefix}search override")} command")
disable()
return
}
startTimeChunk = 0L
startTimeRender = 0L
}
init {
safeListener<TickEvent.ClientTickEvent> {
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)
}
safeListener<RenderWorldEvent> {
renderer.render(false)
if (updateTimer.tick(updateDelay.toLong())) {
updateRenderer()
}
}
}
/* Main list updating */
private fun shouldUpdateChunk(): Boolean {
return if (System.currentTimeMillis() - startTimeChunk < max(renderUpdate.value * 2, 500)) {
false
} else {
startTimeChunk = System.currentTimeMillis()
true
}
}
private fun SafeClientEvent.updateRenderer() {
defaultScope.launch {
val posMap = TreeMap<Double, Pair<BlockPos, IBlockState>>()
private fun SafeClientEvent.updateLoadedChunkList() {
/* Removes unloaded chunks from the list */
Thread {
for (chunkPos in loadedChunks) {
if (isChunkLoaded(chunkPos)) continue
chunkThreads.remove(chunkPos)
loadedChunks.remove(chunkPos)
mainList.remove(chunkPos)
coroutineScope {
launch {
updateAlpha()
}
launch {
val eyePos = player.getPositionEyes(1f)
getBlockPosList(eyePos, posMap)
}
}
/* Adds new loaded chunks to the list */
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)
val renderList = ArrayList<Triple<AxisAlignedBB, ColorHolder, Int>>()
val sides = GeometryMasks.Quad.ALL
for ((index, pair) in posMap.values.withIndex()) {
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) {
val chunk = world.getChunk(x, z)
if (!chunk.isLoaded) continue
loadedChunks.add(chunk.pos)
}
}.start()
}
if (player.distanceTo(chunk.pos) > range + 16) continue
private fun updateMainList() {
Thread {
for (chunkPos in loadedChunks) {
val thread = Thread {
findBlocksInChunk(chunkPos, searchList.toHashSet())
launch {
findBlocksInChunk(chunk, eyePos, map)
}
thread.priority = 1
chunkThreads.putIfAbsent(chunkPos, thread)
delay(1L)
}
for (thread in chunkThreads.values) {
chunkThreadPool.execute(thread)
Thread.sleep(5L)
}
}.start()
}
}
private fun findBlocksInChunk(chunkPos: ChunkPos, blocksToFind: HashSet<String>) {
val yRange = IntRange(0, 256)
val xRange = IntRange(chunkPos.xStart, chunkPos.xEnd)
val zRange = IntRange(chunkPos.zStart, chunkPos.zEnd)
val foundBlocks = ArrayList<BlockPos>()
private fun findBlocksInChunk(chunk: Chunk, eyePos: Vec3d, map: MutableMap<Double, Pair<BlockPos, IBlockState>>) {
val yRange = 0..256
val xRange = (chunk.x shl 4)..(chunk.x shl 4) + 15
val zRange = (chunk.z shl 4)..(chunk.z shl 4) + 15
for (y in yRange) for (x in xRange) for (z in zRange) {
val blockPos = BlockPos(x, y, z)
val block = mc.world.getBlockState(blockPos).block
val pos = BlockPos(x, y, z)
val blockState = chunk.getBlockState(pos)
val block = blockState.block
if (block == Blocks.AIR) continue
if (!blocksToFind.contains(block.registryName.toString())) continue
foundBlocks.add(BlockPos(blockPos))
}
mainList[chunkPos] = foundBlocks
}
/* End of main list updating */
if (!searchList.contains(block.registryName.toString())) continue
/* Rendering */
private fun shouldUpdateRender(): Boolean {
return if (System.currentTimeMillis() - startTimeRender < renderUpdate.value) {
false
} else {
startTimeRender = System.currentTimeMillis()
true
val dist = eyePos.distanceTo(pos)
if (dist > range) continue
map[dist] = (pos to blockState)
}
}
private fun SafeClientEvent.updateRenderList() {
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)
private fun SafeClientEvent.getBlockColor(pos: BlockPos, blockState: IBlockState): ColorHolder {
val block = blockState.block
return if (!customColours.value) {
return if (!customColors) {
if (block == Blocks.PORTAL) {
ColorHolder(82, 49, 153)
} else {
@ -223,12 +168,8 @@ object Search : Module(
ColorHolder((colorInt shr 16), (colorInt shr 8 and 255), (colorInt and 255))
}
} 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
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.module.Module
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.graphics.ESPRenderer
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.item.*
import net.minecraft.item.ItemShulkerBox
@ -16,103 +20,118 @@ import net.minecraft.tileentity.*
import net.minecraft.util.math.AxisAlignedBB
import net.minecraftforge.fml.common.gameevent.TickEvent
import org.kamiblue.event.listener.listener
import java.util.concurrent.ConcurrentHashMap
object StorageESP : Module(
name = "StorageESP",
description = "Draws an ESP on top of storage units",
category = Category.RENDER
) {
private val page = setting("Page", Page.TYPE)
private val page by setting("Page", Page.TYPE)
/* Type settings */
private val chest = setting("Chest", true, { page.value == Page.TYPE })
private val shulker = setting("Shulker", true, { page.value == Page.TYPE })
private val enderChest = setting("EnderChest", true, { page.value == Page.TYPE })
private val frame = setting("ItemFrame", true, { page.value == Page.TYPE })
private val frameShulker = setting("ItFShulkerOnly", true, { frame.value && page.value == Page.TYPE })
private val furnace = setting("Furnace", false, { page.value == Page.TYPE })
private val dispenser = setting("Dispenser", false, { page.value == Page.TYPE })
private val hopper = setting("Hopper", false, { page.value == Page.TYPE })
private val cart = setting("Minecart", false, { page.value == Page.TYPE })
private val chest by setting("Chest", true, { page == Page.TYPE })
private val shulker by setting("Shulker", true, { page == Page.TYPE })
private val enderChest by setting("EnderChest", true, { page == Page.TYPE })
private val frame by setting("ItemFrame", true, { page == Page.TYPE })
private val withShulkerOnly by setting("WithShulkerOnly", true, { page == Page.TYPE && frame })
private val furnace by setting("Furnace", false, { page == Page.TYPE })
private val dispenser by setting("Dispenser", false, { page == Page.TYPE })
private val hopper by setting("Hopper", false, { page == Page.TYPE })
private val cart by setting("Minecart", false, { page == Page.TYPE })
/* Color settings */
private val colorChest = setting("ChestColor", DyeColors.ORANGE, { page.value == Page.COLOR })
private val colorDispenser = setting("DispenserColor", DyeColors.LIGHT_GRAY, { page.value == Page.COLOR })
private val colorShulker = setting("ShulkerColor", DyeColors.MAGENTA, { page.value == Page.COLOR })
private val colorEnderChest = setting("EnderChestColor", DyeColors.PURPLE, { page.value == Page.COLOR })
private val colorFurnace = setting("FurnaceColor", DyeColors.LIGHT_GRAY, { page.value == Page.COLOR })
private val colorHopper = setting("HopperColor", DyeColors.GRAY, { page.value == Page.COLOR })
private val colorCart = setting("CartColor", DyeColors.GREEN, { page.value == Page.COLOR })
private val colorFrame = setting("FrameColor", DyeColors.ORANGE, { page.value == Page.COLOR })
private val colorChest by setting("ChestColor", DyeColors.ORANGE, { page == Page.COLOR })
private val colorDispenser by setting("DispenserColor", DyeColors.LIGHT_GRAY, { page == Page.COLOR })
private val colorShulker by setting("ShulkerColor", DyeColors.MAGENTA, { page == Page.COLOR })
private val colorEnderChest by setting("EnderChestColor", DyeColors.PURPLE, { page == Page.COLOR })
private val colorFurnace by setting("FurnaceColor", DyeColors.LIGHT_GRAY, { page == Page.COLOR })
private val colorHopper by setting("HopperColor", DyeColors.GRAY, { page == Page.COLOR })
private val colorCart by setting("CartColor", DyeColors.GREEN, { page == Page.COLOR })
private val colorFrame by setting("FrameColor", DyeColors.ORANGE, { page == Page.COLOR })
/* Render settings */
private val filled = setting("Filled", true, { page.value == Page.RENDER })
private val outline = setting("Outline", true, { page.value == Page.RENDER })
private val tracer = setting("Tracer", false, { page.value == Page.RENDER })
private val cull = setting("Culling", true, { page.value == Page.RENDER })
private val aFilled = setting("FilledAlpha", 31, 0..255, 1, { page.value == Page.RENDER && filled.value })
private val aOutline = setting("OutlineAlpha", 127, 0..255, 1, { page.value == Page.RENDER && outline.value })
private val aTracer = setting("TracerAlpha", 200, 0..255, 1, { page.value == Page.RENDER && tracer.value })
private val thickness = setting("LineThickness", 2.0f, 0.25f..5.0f, 0.25f, { page.value == Page.RENDER })
private val filled by setting("Filled", true, { page == Page.RENDER })
private val outline by setting("Outline", true, { page == Page.RENDER })
private val tracer by setting("Tracer", false, { page == Page.RENDER })
private val aFilled by setting("FilledAlpha", 31, 0..255, 1, { page == Page.RENDER && filled })
private val aOutline by setting("OutlineAlpha", 127, 0..255, 1, { page == Page.RENDER && outline })
private val aTracer by setting("TracerAlpha", 200, 0..255, 1, { page == Page.RENDER && tracer })
private val thickness by setting("LineThickness", 2.0f, 0.25f..5.0f, 0.25f, { page == Page.RENDER })
private enum class Page {
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 val renderer = ESPRenderer()
init {
listener<RenderWorldEvent> {
val renderer = ESPRenderer()
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)
renderer.render(false)
}
safeListener<TickEvent.ClientTickEvent> {
safeAsyncListener<TickEvent.ClientTickEvent> {
if (it.phase != TickEvent.Phase.START) return@safeAsyncListener
cycler++
renderList.clear()
for (tileEntity in world.loadedTileEntityList) {
if (tileEntity is TileEntityChest && chest.value
|| tileEntity is TileEntityDispenser && dispenser.value
|| tileEntity is TileEntityShulkerBox && shulker.value
|| tileEntity is TileEntityEnderChest && enderChest.value
|| tileEntity is TileEntityFurnace && furnace.value
|| tileEntity is TileEntityHopper && hopper.value) {
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()
}
renderList[box] = Pair(color, side)
renderer.clear()
val cached = ArrayList<Triple<AxisAlignedBB, ColorHolder, Int>>()
coroutineScope {
launch(Dispatchers.Default) {
updateRenderer()
}
launch(Dispatchers.Default) {
updateTileEntities(cached)
}
launch(Dispatchers.Default) {
updateEntities(cached)
}
}
for (entity in world.loadedEntityList) {
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)
}
}
renderer.replaceAll(cached)
}
}
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? {
val color = when (tileEntity) {
is TileEntityChest -> colorChest
@ -122,28 +141,38 @@ object StorageESP : Module(
is TileEntityFurnace -> colorFurnace
is TileEntityHopper -> colorHopper
else -> return null
}.value.color
}.color
return if (color == DyeColors.RAINBOW.color) {
cycler.currentRgb()
} 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? {
val color = when (entity) {
is EntityMinecartContainer -> colorCart
is EntityItemFrame -> colorFrame
else -> return null
}.value.color
}.color
return if (color == DyeColors.RAINBOW.color) {
cycler.currentRgb()
} else color
}
private fun frameShulkerOrAny(e: EntityItemFrame): Boolean {
return when {
!frame.value -> false
!frameShulker.value -> true
else -> e.displayedItem.getItem() is ItemShulkerBox
}
}
private fun frameShulkerOrAny(entity: EntityItemFrame) =
frame && (!withShulkerOnly || entity.displayedItem.item is ItemShulkerBox)
}

View File

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

View File

@ -1,7 +1,10 @@
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.Wrapper
import me.zeroeightsix.kami.util.threads.toSafe
import net.minecraft.block.Block
import net.minecraft.entity.Entity
import net.minecraft.init.Blocks
@ -39,14 +42,15 @@ object SurroundUtils {
return checkHole(entity.flooredPosition)
}
@JvmStatic
fun checkHole(pos: BlockPos): HoleType {
fun checkHole(pos: BlockPos) = ClientEvent().toSafe()?.checkHole(pos) ?: HoleType.NONE
fun SafeClientEvent.checkHole(pos: BlockPos): HoleType {
// 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
for (offset in surroundOffset) {
val block = mc.world.getBlockState(pos.add(offset)).block
val block = world.getBlockState(pos.add(offset)).block
if (!checkBlock(block)) {
type = HoleType.NONE
break
@ -56,7 +60,6 @@ object SurroundUtils {
return type
}
@JvmStatic
fun checkBlock(block: Block): Boolean {
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
import me.zeroeightsix.kami.util.EntityUtils
import me.zeroeightsix.kami.util.EntityUtils.getInterpolatedAmount
import me.zeroeightsix.kami.util.Wrapper
import me.zeroeightsix.kami.util.color.ColorHolder
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.GlStateManager
import net.minecraft.client.renderer.culling.Frustum
import net.minecraft.client.renderer.culling.ICamera
import net.minecraft.entity.Entity
import net.minecraft.util.math.AxisAlignedBB
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_QUADS
@ -19,10 +19,8 @@ import org.lwjgl.opengl.GL11.GL_QUADS
* Created by Xiaro on 30/07/20
*/
class ESPRenderer {
private lateinit var camPos: Vec3d
private val frustumCamera: ICamera = Frustum()
private val mc = Minecraft.getMinecraft()
private val toRender = HashMap<AxisAlignedBB, Pair<ColorHolder, Int>>()
private var toRender: MutableList<Triple<AxisAlignedBB, ColorHolder, Int>> = ArrayList()
var aFilled = 0
var aOutline = 0
@ -58,15 +56,28 @@ class ESPRenderer {
}
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() {
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
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 (aFilled != 0) drawList(Type.FILLED, cull)
@ -81,41 +92,39 @@ class ESPRenderer {
private fun drawList(type: Type, cull: Boolean = false) {
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) {
Type.FILLED -> drawFilled(cull, box, pair)
Type.OUTLINE -> drawOutline(cull, box, pair)
Type.TRACER -> drawTracer(box, pair)
for ((box, color, sides) in toRender) when (type) {
Type.FILLED -> drawFilled(cull, box, color, sides)
Type.OUTLINE -> drawOutline(cull, box, color, sides)
Type.TRACER -> drawTracer(box, color)
}
KamiTessellator.render()
}
private fun drawFilled(cull: Boolean, box: AxisAlignedBB, pair: Pair<ColorHolder, Int>) {
val a = (aFilled * (pair.first.a / 255f)).toInt()
private fun drawFilled(cull: Boolean, box: AxisAlignedBB, color: ColorHolder, sides: Int) {
val a = (aFilled * (color.a / 255f)).toInt()
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>) {
val a = (aOutline * (pair.first.a / 255f)).toInt()
val side = if (fullOutline) GeometryMasks.Quad.ALL else pair.second
private fun drawOutline(cull: Boolean, box: AxisAlignedBB, color: ColorHolder, sides: Int) {
val a = (aOutline * (color.a / 255f)).toInt()
val side = if (fullOutline) GeometryMasks.Quad.ALL else sides
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>) {
val a = (aTracer * (pair.first.a / 255f)).toInt()
private fun drawTracer(box: AxisAlignedBB, color: ColorHolder) {
val a = (aTracer * (color.a / 255f)).toInt()
val offset = (tracerOffset - 50) / 100.0 * (box.maxY - box.minY)
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 {