Skip to content
Open
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
4 changes: 2 additions & 2 deletions .github/workflows/matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ jobs:
- name: Настройка доступа к gradlew
run: chmod +x ./gradlew
- name: Сборка плагина в jar
run: ./gradlew jar
run: ./gradlew clean jar
- name: Запуск Jar файла
run: java -jar build/libs/uwu.jar
run: java -jar build/libs/uwu.jar --target 109.94.209.233:6570 --rps 5
37 changes: 27 additions & 10 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,38 +1,55 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.gradle.jvm.tasks.Jar

plugins {
kotlin("jvm") version "1.7.10"
application
}

group = "io.lucin"
version = "1.0"

application {
mainClass.set("io.lucin.MainKt")
}

group = "io.lucin"
version = "1.0"

repositories {
mavenCentral()
maven(url = "https://jitpack.io")
// Only use Zelaux’s repo (no JitPack, avoids commit hashes)
maven {
url = uri("https://raw.githubusercontent.com/Zelaux/MindustryRepo/master/repository")
}
}

dependencies {
val mindustryVersion = "v146"
implementation("com.github.Anuken.Arc:arcnet:$mindustryVersion")
implementation("com.github.Anuken.Arc:arc-core:$mindustryVersion")

// Mindustry Core
implementation("com.github.Anuken.Mindustry:core:$mindustryVersion")

testImplementation(kotlin("test"))
// Arc + required submodules
implementation("com.github.Anuken.Arc:arc-core:$mindustryVersion")
implementation("com.github.Anuken.Arc:flabel:$mindustryVersion")
implementation("com.github.Anuken.Arc:freetype:$mindustryVersion")
implementation("com.github.Anuken.Arc:g3d:$mindustryVersion")
implementation("com.github.Anuken.Arc:fx:$mindustryVersion")
implementation("com.github.Anuken.Arc:arcnet:$mindustryVersion")
}

tasks.jar {
tasks.named<Jar>("jar") {
manifest {
attributes["Main-Class"] = application.mainClass
attributes["Main-Class"] = application.mainClass.get()
}

archiveFileName.set("uwu.jar")
from(configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) })

// Fat jar with dependencies
from({
configurations.runtimeClasspath.get()
.filter { it.name.endsWith("jar") }
.map { zipTree(it) }
})

duplicatesStrategy = DuplicatesStrategy.INCLUDE
}

Expand Down
191 changes: 149 additions & 42 deletions src/main/kotlin/io/lucin/Main.kt
Original file line number Diff line number Diff line change
@@ -1,42 +1,96 @@
package io.lucin

import arc.math.Rand
import arc.struct.Seq
import arc.util.Http
import arc.util.Log
import arc.util.serialization.Base64Coder
import arc.util.serialization.Jval
import io.lucin.core.Entity
import mindustry.Vars
import mindustry.core.*
import mindustry.gen.Groups
import mindustry.net.Packets.ConnectPacket
import java.time.Duration
import java.util.Timer
import java.util.TimerTask

var targets = listOf(
"5.228.122.149:6567"
)
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.TimeUnit
import java.security.SecureRandom
import java.util.Base64

var counter = 0

fun main() {
fun main(args: Array<String>) {
init()

val timer = Timer()
timer.scheduleAtFixedRate(object : TimerTask() {
override fun run() {
System.gc()
}
}, 0, 1000)
val config = parseArgs(args)

if (config == null) {
printHelp()
return
}

if (config.solo) {
runSolo(config)
return
}

targets.forEach { target ->
Log.info(target)
runStress(config)
}

private data class Config(
val target: String,
val rps: Int,
val duration: Duration,
val solo: Boolean,
val uuidOverride: String?,
val usidOverride: String?
)

val thread = Thread { task(target) }
thread.priority = Thread.MAX_PRIORITY;
thread.start()
private fun parseArgs(args: Array<String>): Config? {
var target: String? = null
var rps = 1
var durationSeconds: Long = 10
var solo = false
var uuidOverride: String? = null
var usidOverride: String? = null

var i = 0
while (i < args.size) {
when (args[i]) {
"--target" -> {
if (i + 1 < args.size) target = args[i + 1]
i += 1
}
"--rps" -> {
if (i + 1 < args.size) rps = args[i + 1].toIntOrNull() ?: return null
i += 1
}
"--duration" -> {
if (i + 1 < args.size) durationSeconds = args[i + 1].toLongOrNull() ?: return null
i += 1
}
"--solo" -> solo = true
"--uuid" -> {
if (i + 1 < args.size) uuidOverride = args[i + 1]
i += 1
}
"--usid" -> {
if (i + 1 < args.size) usidOverride = args[i + 1]
i += 1
}
}
i += 1
}

if (target == null) return null
if (rps <= 0) return null
if (durationSeconds <= 0) return null

return Config(target!!, rps, Duration.ofSeconds(durationSeconds), solo, uuidOverride, usidOverride)
}

private fun printHelp() {
Log.info("Usage: --target <host:port> [--rps <n>] [--duration <sec>] [--solo] [--uuid <id>] [--usid <id>]")
}

private fun init() {
Expand All @@ -48,49 +102,102 @@ private fun init() {

Vars.state = GameState()

Version.build = 141
Version.build = 146
}

private fun packet(): ConnectPacket {
private fun packet(config: Config): ConnectPacket {
val packet = ConnectPacket()

packet.version = -1
packet.versionType = "hentai"
packet.version = Version.build
packet.versionType = "official"

packet.name = "nekonya-" + counter++
packet.color = 255
packet.locale = "nya"
packet.name = "client-" + counter++
packet.color = 228
packet.locale = "en"

packet.mods = Seq()
packet.mobile = true
packet.mobile = false

packet.uuid = config.uuidOverride ?: uuid()
packet.usid = config.usidOverride ?: usid()

packet.uuid = uuid()
packet.usid = usid()
Log.info("uuid=${packet.uuid} usid=${packet.usid}")

return packet
}

private fun task(address: String) {
val fullAddress = address.split(':')
val ip = fullAddress[0]
val port = fullAddress[1].toInt()
private fun runSolo(config: Config) {
val parts = config.target.split(':')
val ip = parts[0]
val port = parts[1].toInt()

while (true) {
Entity.EntityBuilder(false, packet(), ip, port, port)
Thread.sleep(1)
Log.info("Solo: connecting single client to ${config.target}")
val entity = Entity.EntityBuilder(false, packet(config), ip, port, port)

val timer = Timer()
timer.schedule(object : TimerTask() {
override fun run() {
entity.stop()
Log.info("Solo: client stopped")
}
}, 3000)

Thread.sleep(4000)
}

private fun runStress(config: Config) {
val parts = config.target.split(':')
val ip = parts[0]
val port = parts[1].toInt()

val scheduler: ScheduledExecutorService = Executors.newScheduledThreadPool(1)
val periodNanos = 1_000_000_000L / config.rps

Log.info("Stress: target=${config.target} rps=${config.rps} duration=${config.duration.seconds}s")

val start = System.nanoTime()
val task = Runnable {
val elapsed = System.nanoTime() - start
if (elapsed >= config.duration.toNanos()) {
return@Runnable
}
try {
Entity.EntityBuilder(false, packet(config), ip, port, port)
} catch (_: Exception) {
}
}

while (true) {}
scheduler.scheduleAtFixedRate(task, 0, periodNanos, TimeUnit.NANOSECONDS)

Thread.sleep(config.duration.toMillis())
scheduler.shutdownNow()
Log.info("Stress: completed")
}

private val secureRng: SecureRandom = SecureRandom()

private fun uuid(): String {
val bytes = ByteArray(8)
Rand().nextBytes(bytes)
return String(Base64Coder.encode(bytes))
fillUnique(bytes)
return Base64.getEncoder().encodeToString(bytes)
}

private fun usid(): String {
val result = ByteArray(8)
Rand().nextBytes(result)
return String(Base64Coder.encode(result))
val bytes = ByteArray(16)
fillUnique(bytes)
return Base64.getEncoder().encodeToString(bytes)
}

private fun fillUnique(bytes: ByteArray) {
do {
secureRng.nextBytes(bytes)
val c = counter
bytes[0] = (bytes[0].toInt() xor (c and 0xFF)).toByte()
bytes[1] = (bytes[1].toInt() xor ((c ushr 8) and 0xFF)).toByte()
} while (allZero(bytes))
}

private fun allZero(bytes: ByteArray): Boolean {
for (b in bytes) if (b.toInt() != 0) return false
return true
}
8 changes: 8 additions & 0 deletions src/main/kotlin/io/lucin/core/Entity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ object Entity {
err(e)
}
}

fun stop() {
try {
client.stop()
} catch (e: Exception) {
err(e)
}
}
}

private class EntityListener(val entityBuilder: EntityBuilder) : NetListener {
Expand Down