Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: tensorflow smooth mode #5640

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,11 @@ dependencies {
// includeDependency "org.graalvm.polyglot:ruby-community:24.0.2"
// includeDependency "org.graalvm.polyglot:llvm-native-community:24.0.2"

// Kotlin DL
implementation 'org.jetbrains.kotlinx:kotlin-deeplearning-tensorflow:0.5.2'
implementation 'org.jetbrains.kotlinx:kotlin-deeplearning-dataset:0.5.2'
implementation 'org.tensorflow:libtensorflow:1.15.0'

// HTTP library
includeDependency ("com.squareup.okhttp3:okhttp:5.0.0-alpha.14") {
exclude group: "org.jetbrains.kotlin", module: "kotlin-stdlib"
Expand Down
182 changes: 182 additions & 0 deletions src/main/kotlin/DataModelTrainer.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import com.google.gson.Gson
import com.google.gson.annotations.SerializedName
import com.google.gson.reflect.TypeToken
import net.ccbluex.liquidbounce.utils.aiming.data.Rotation
import net.ccbluex.liquidbounce.utils.kotlin.random
import org.jetbrains.kotlinx.dl.api.core.SavingFormat
import org.jetbrains.kotlinx.dl.api.core.Sequential
import org.jetbrains.kotlinx.dl.api.core.WritingMode
import org.jetbrains.kotlinx.dl.api.core.activation.Activations
import org.jetbrains.kotlinx.dl.api.core.initializer.HeNormal
import org.jetbrains.kotlinx.dl.api.core.layer.core.Dense
import org.jetbrains.kotlinx.dl.api.core.layer.core.Input
import org.jetbrains.kotlinx.dl.api.core.loss.Losses
import org.jetbrains.kotlinx.dl.api.core.metric.Metrics
import org.jetbrains.kotlinx.dl.api.core.optimizer.Adam
import org.jetbrains.kotlinx.dl.dataset.OnHeapDataset
import java.io.File
import kotlin.math.abs

data class Vector(val x: Double, val y: Double, val z: Double)
data class Vec2f(val x: Float, val y: Float)
data class TrainingData(
@SerializedName("c_vector")
val currentVector: Vector,
@SerializedName("w_vector")
val targetVector: Vector,
val delta: Vec2f,
val distance: Double
)

fun main() {
val linearTrainingData = generateTrainingData()
println("Generated ${linearTrainingData.size} training samples")
val trainingData = readTrainingDataFromFolder("./LiquidBounce/debug-recorder/Aim")
println("Read ${trainingData.size} training samples")

val dataset = prepareData(linearTrainingData + trainingData)
val modelYaw = createAndTrainModel(dataset.features, dataset.labelX)
val modelPitch = createAndTrainModel(dataset.features, dataset.labelY)

// Save the models
saveModel(modelYaw, "yaw_model")
saveModel(modelPitch, "pitch_model")

println("Models trained and saved successfully.")
}

const val SAMPLES = 100

/**
* Linear train data generator
*/
fun generateTrainingData(): List<TrainingData> {
val trainingData = mutableListOf<TrainingData>()

repeat(SAMPLES) {
val targetYaw = (-180..180).random()
val targetPitch = (-5..10).random()

val distance = (0f..4f).random()
println("Generating training data for distance $distance, yaw $targetYaw, pitch $targetPitch")

val targetRotation = Rotation(targetYaw.toFloat(), targetPitch.toFloat())

val clientYaw = (-180..180).random()
val clientPitch = (-80..80).random()
var clientRotation = Rotation(clientYaw.toFloat(), clientPitch.toFloat())

while (clientRotation.angleTo(targetRotation) > 1.0E-5f) {
val diff = clientRotation.rotationDeltaTo(targetRotation)
val rotationDifference = diff.length()
val factorH = (40f..60f).random()
val straightLineYaw = (abs(diff.deltaYaw / rotationDifference) * factorH).toFloat()
val factorV = (40f..60f).random()
val straightLinePitch = (abs(diff.deltaPitch / rotationDifference) * factorV).toFloat()

val nextClientRotation = Rotation(
clientRotation.yaw + diff.deltaYaw.coerceIn(-straightLineYaw, straightLineYaw),
clientRotation.pitch + diff.deltaPitch.coerceIn(-straightLinePitch, straightLinePitch)
)
val diff2 = clientRotation.rotationDeltaTo(nextClientRotation)

trainingData += TrainingData(
Vector(
clientRotation.directionVector.x,
clientRotation.directionVector.y,
clientRotation.directionVector.z
),
Vector(
targetRotation.directionVector.x,
targetRotation.directionVector.y,
targetRotation.directionVector.z
),
Vec2f(diff2.deltaYaw, diff2.deltaPitch),
distance
)

clientRotation = nextClientRotation
}
}

println("Generated ${trainingData.size} training samples")

return trainingData
}

fun saveModel(model: Sequential, modelName: String) {
val savePath = File("./models/$modelName")
savePath.mkdirs()
model.save(
modelDirectory = savePath,
savingFormat = SavingFormat.TF_GRAPH_CUSTOM_VARIABLES,
saveOptimizerState = true,
writingMode = WritingMode.OVERRIDE
)
println("Model saved to ${savePath.absolutePath}")
}


fun parseJson(jsonString: String): List<TrainingData> {
val gson = Gson()
val listType = object : TypeToken<List<TrainingData>>() {}.type
return gson.fromJson(jsonString, listType)
}

fun readTrainingDataFromFolder(folderPath: String): List<TrainingData> {
val folder = File(folderPath)
require(folder.exists() && folder.isDirectory) { "Invalid folder path: $folderPath" }

return folder.listFiles { file -> file.isFile && file.extension == "json" }
?.flatMap { file ->
try {
parseJson(file.readText())
} catch (e: Exception) {
println("Error parsing file ${file.name}: ${e.message}")
emptyList()
}
} ?: emptyList()
}

data class Dataset(val features: Array<FloatArray>, val labelX: FloatArray, val labelY: FloatArray)

fun prepareData(trainingData: List<TrainingData>): Dataset {
val features = trainingData.map { data ->
floatArrayOf(
data.currentVector.x.toFloat(), data.currentVector.y.toFloat(), data.currentVector.z.toFloat(),
data.targetVector.x.toFloat(), data.targetVector.y.toFloat(), data.targetVector.z.toFloat(),
data.distance.toFloat()
)
}.toTypedArray()

return Dataset(features, trainingData.map { data ->
data.delta.x
}.toFloatArray(), trainingData.map { data ->
data.delta.y
}.toFloatArray())
}

fun createAndTrainModel(features: Array<FloatArray>, labels: FloatArray): Sequential {
val model = Sequential.of(
Input(7),
Dense(64, Activations.Relu, kernelInitializer = HeNormal()),
Dense(32, Activations.Relu, kernelInitializer = HeNormal()),
Dense(1, Activations.Linear)
)

val dataset = OnHeapDataset.create(features, labels)

model.compile(
optimizer = Adam(),
loss = Losses.MSE,
metric = Metrics.MAE
)

model.fit(
dataset = dataset,
epochs = 100,
batchSize = 32
)

return model
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,78 +22,58 @@
package net.ccbluex.liquidbounce.features.module.modules.misc.debugrecorder.modes

import com.google.gson.JsonObject
import net.ccbluex.liquidbounce.config.gson.util.json
import net.ccbluex.liquidbounce.event.tickHandler
import net.ccbluex.liquidbounce.features.module.modules.misc.debugrecorder.ModuleDebugRecorder
import net.ccbluex.liquidbounce.utils.aiming.data.Rotation
import net.ccbluex.liquidbounce.utils.combat.shouldBeAttacked
import net.ccbluex.liquidbounce.utils.entity.box
import net.ccbluex.liquidbounce.utils.client.FloatValueProvider
import net.ccbluex.liquidbounce.utils.combat.TargetPriority
import net.ccbluex.liquidbounce.utils.combat.TargetTracker
import net.ccbluex.liquidbounce.utils.entity.lastRotation
import net.ccbluex.liquidbounce.utils.entity.prevPos
import net.ccbluex.liquidbounce.utils.entity.rotation
import net.ccbluex.liquidbounce.utils.math.minus
import net.ccbluex.liquidbounce.utils.entity.squaredBoxedDistanceTo
import net.minecraft.util.hit.EntityHitResult
import net.minecraft.util.hit.HitResult

object AimDebugRecorder : ModuleDebugRecorder.DebugRecorderMode("Aim") {

val repeatable = tickHandler {
private val targetTracker = TargetTracker(TargetPriority.DIRECTION, FloatValueProvider("Range", 7f, 4f..10f))

@Suppress("unused")
private val tickHandler = tickHandler {
val playerRotation = player.rotation
val playerLastRotation = player.lastRotation

val target = targetTracker.selectFirst() ?: return@tickHandler
val turnSpeed = playerLastRotation.rotationDeltaTo(playerRotation)

val crosshairTarget = mc.crosshairTarget

recordPacket(JsonObject().apply {
addProperty("health", player.health)
addProperty("yaw", playerRotation.yaw)
addProperty("pitch", playerRotation.pitch)
addProperty("last_yaw", playerLastRotation.yaw)
addProperty("last_pitch", playerLastRotation.pitch)
addProperty("turn_speed_h", turnSpeed.deltaYaw)
addProperty("turn_speed_v", turnSpeed.deltaPitch)

add("velocity", JsonObject().apply {
addProperty("x", player.velocity.x)
addProperty("y", player.velocity.y)
addProperty("z", player.velocity.z)
val cDirVec = player.rotation.directionVector
add("c_vector", json {
"x" to cDirVec.x
"y" to cDirVec.y
"z" to cDirVec.z
})
val rotation = Rotation.lookingAt(point = target.eyePos, from = player.eyePos)
val wDirVec = rotation.directionVector
add("w_vector", json {
"x" to wDirVec.x
"y" to wDirVec.y
"z" to wDirVec.z
})
add("delta", json {
"x" to turnSpeed.deltaYaw
"y" to turnSpeed.deltaPitch
})
addProperty("distance", player.squaredBoxedDistanceTo(target))

world.entities.filter {
it.shouldBeAttacked() && it.distanceTo(player) < 10.0f
}.minByOrNull {
it.distanceTo(player)
}?.let {
val vector = it.pos - player.pos
add("vec", JsonObject().apply {
addProperty("x", vector.x)
addProperty("y", vector.y)
addProperty("z", vector.z)
})
val velocity = it.pos - it.prevPos
add("velocity", JsonObject().apply {
addProperty("x", velocity.x)
addProperty("y", velocity.y)
addProperty("z", velocity.z)
})
addProperty("distance", player.distanceTo(it))
val rotation = Rotation.lookingAt(point = it.box.center, from = player.eyePos)

val delta = rotation.rotationDeltaTo(playerRotation)

addProperty("diff_h", delta.deltaYaw)
addProperty("diff_v", delta.deltaPitch)
addProperty("yaw_target", rotation.yaw)
addProperty("pitch_target", rotation.pitch)

addProperty("crosshair",
if (crosshairTarget?.type == HitResult.Type.ENTITY && crosshairTarget is EntityHitResult) {
crosshairTarget.entity.id == it.id
} else {
false
}
)
}
val crosshairTarget = mc.crosshairTarget
addProperty("reward",
if (crosshairTarget?.type == HitResult.Type.ENTITY && crosshairTarget is EntityHitResult) {
crosshairTarget.entity.id == target.id
} else {
false
}
)
})
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ open class RotationsConfigurable(
BezierAngleSmoothMode(it),
SigmoidAngleSmoothMode(it),
ConditionalLinearAngleSmoothMode(it),
AccelerationSmoothMode(it)
AccelerationSmoothMode(it),
TensorflowSmoothMode(it),
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class ShortStop(owner: EventListener? = null)
// The currently set transition duration, randomized within the defined range
private var currentTransitionInDuration = stopDuration.random()
val isInStopState: Boolean
get() = enabled && ticksElapsed < currentTransitionInDuration
get() = running && ticksElapsed < currentTransitionInDuration

@Suppress("unused")
private val gameHandler = handler<GameTickEvent>(priority = FIRST_PRIORITY) {
Expand Down
Loading
Loading