11package com.lambda.module.modules.player
22
3+ import com.google.gson.*
34import com.google.gson.annotations.SerializedName
45import com.lambda.config.RotationSettings
56import com.lambda.context.SafeContext
67import com.lambda.core.TimerManager
8+ import com.lambda.event.EventFlow.lambdaScope
79import com.lambda.event.events.KeyPressEvent
810import com.lambda.event.events.MovementEvent
911import com.lambda.event.events.RotationEvent
@@ -15,11 +17,17 @@ import com.lambda.module.Module
1517import com.lambda.module.modules.player.Replay.InputAction.Companion.toAction
1618import com.lambda.module.tag.ModuleTag
1719import com.lambda.util.Communication.info
20+ import com.lambda.util.Communication.logError
1821import com.lambda.util.Communication.warn
22+ import com.lambda.util.FolderRegister
1923import com.lambda.util.KeyCode
2024import com.lambda.util.primitives.extension.rotation
25+ import kotlinx.coroutines.Dispatchers
26+ import kotlinx.coroutines.launch
2127import net.minecraft.client.input.Input
2228import net.minecraft.util.math.Vec3d
29+ import java.io.File
30+ import java.lang.reflect.Type
2331import kotlin.time.Duration
2432import kotlin.time.DurationUnit
2533import kotlin.time.toDuration
@@ -29,7 +37,6 @@ import kotlin.time.toDuration
2937// - Actually store the data in a file
3038// - Implement a way to save and load the data (Commands?)
3139// - Record other types of inputs: (Interactions, etc.)
32- // - Fix continue deviation after replaying the checkpoint for spliced runs
3340object Replay : Module(
3441 name = " Replay" ,
3542 description = " Record gameplay actions and replay them like a TAS." ,
@@ -66,6 +73,16 @@ object Replay : Module(
6673 private var replay: Recording ? = null
6774 private var repeats = 0
6875
76+ private val gsonCompact = GsonBuilder ()
77+ .registerTypeAdapter(Recording ::class .java, Recording ())
78+ .create()
79+
80+ fun loadRecording (file : File ) {
81+ recording = gsonCompact.fromJson(file.readText(), Recording ::class .java)
82+
83+ info(" Recording ${file.nameWithoutExtension} loaded. Duration: ${recording?.duration} ." )
84+ }
85+
6986 init {
7087 listener<KeyPressEvent > {
7188 if (mc.currentScreen != null && ! mc.options.commandKey.isPressed) return @listener
@@ -232,11 +249,21 @@ object Replay : Module(
232249 when (state) {
233250 State .RECORDING -> {
234251 if (player.velocity != Vec3d (0.0 , - 0.0784000015258789 , 0.0 )) {
235- this @Replay.info (" Cannot create checkpoint while moving." )
252+ this @Replay.logError (" Cannot create checkpoint while moving. Try again! " )
236253 return
237254 }
238255
239256 checkpoint = recording?.duplicate()
257+ lambdaScope.launch(Dispatchers .IO ) {
258+ FolderRegister .replays.mkdirs()
259+ FolderRegister .replays.resolve(" checkpoint-${
260+ mc.currentServerEntry?.address?.replace(" :" , " _" )
261+ } -${
262+ world.dimensionKey?.value?.path?.replace(" /" , " _" )
263+ } -${
264+ System .currentTimeMillis()
265+ } .json" ).writeText(gsonCompact.toJson(checkpoint))
266+ }
240267 this @Replay.info(" Checkpoint created." )
241268 }
242269 else -> {}
@@ -259,7 +286,7 @@ object Replay : Module(
259286 val rotation : MutableList <Rotation > = mutableListOf(),
260287 val sprint : MutableList <Boolean > = mutableListOf(),
261288 val position : MutableList <Vec3d > = mutableListOf()
262- ) {
289+ ) : JsonSerializer<Recording>, JsonDeserializer<Recording> {
263290 val size: Int
264291 get() = minOf(input.size, rotation.size, sprint.size, position.size)
265292 val duration: Duration
@@ -271,6 +298,66 @@ object Replay : Module(
271298 sprint.take(size).toMutableList(),
272299 position.take(size).toMutableList()
273300 )
301+
302+ override fun serialize (
303+ src : Recording ? ,
304+ typeOfSrc : Type ? ,
305+ context : JsonSerializationContext ? ,
306+ ): JsonElement = src?.let { recording ->
307+ JsonArray ().apply {
308+ repeat(recording.size) { i ->
309+ add(JsonArray ().apply {
310+ val inputI = recording.input[i]
311+ add(inputI.movementSideways)
312+ add(inputI.movementForward)
313+ add(inputI.pressingForward)
314+ add(inputI.pressingBack)
315+ add(inputI.pressingLeft)
316+ add(inputI.pressingRight)
317+ add(inputI.jumping)
318+ add(inputI.sneaking)
319+ val rotationI = recording.rotation[i]
320+ add(rotationI.yaw)
321+ add(rotationI.pitch)
322+ add(recording.sprint[i])
323+ val positionI = recording.position[i]
324+ add(positionI.x)
325+ add(positionI.y)
326+ add(positionI.z)
327+ })
328+ }
329+ }
330+ } ? : JsonNull .INSTANCE
331+
332+ override fun deserialize (
333+ json : JsonElement ? ,
334+ typeOfT : Type ? ,
335+ context : JsonDeserializationContext ?
336+ ): Recording = json?.asJsonArray?.let {
337+ val input = mutableListOf<InputAction >()
338+ val rotation = mutableListOf<Rotation >()
339+ val sprint = mutableListOf<Boolean >()
340+ val position = mutableListOf<Vec3d >()
341+
342+ it.forEach { element ->
343+ val array = element.asJsonArray
344+ input.add(InputAction (
345+ array[0 ].asFloat,
346+ array[1 ].asFloat,
347+ array[2 ].asBoolean,
348+ array[3 ].asBoolean,
349+ array[4 ].asBoolean,
350+ array[5 ].asBoolean,
351+ array[6 ].asBoolean,
352+ array[7 ].asBoolean
353+ ))
354+ rotation.add(Rotation (array[8 ].asDouble, array[9 ].asDouble))
355+ sprint.add(array[10 ].asBoolean)
356+ position.add(Vec3d (array[11 ].asDouble, array[12 ].asDouble, array[13 ].asDouble))
357+ }
358+
359+ Recording (input, rotation, sprint, position)
360+ } ? : Recording ()
274361 }
275362
276363 data class InputAction (
0 commit comments