@@ -10,31 +10,43 @@ import com.lambda.event.events.KeyPressEvent
1010import com.lambda.event.events.MovementEvent
1111import com.lambda.event.events.RotationEvent
1212import com.lambda.event.listener.SafeListener.Companion.listener
13+ import com.lambda.gui.impl.clickgui.LambdaClickGui
1314import com.lambda.interaction.rotation.Rotation
1415import com.lambda.interaction.rotation.RotationContext
1516import com.lambda.interaction.rotation.RotationMode
1617import com.lambda.module.Module
18+ import com.lambda.module.modules.client.GuiSettings
1719import com.lambda.module.modules.player.Replay.InputAction.Companion.toAction
1820import com.lambda.module.tag.ModuleTag
1921import com.lambda.util.Communication.info
2022import com.lambda.util.Communication.logError
2123import com.lambda.util.Communication.warn
2224import com.lambda.util.FolderRegister
25+ import com.lambda.util.Formatting.asString
2326import com.lambda.util.KeyCode
2427import com.lambda.util.primitives.extension.rotation
28+ import com.lambda.util.text.buildText
29+ import com.lambda.util.text.color
30+ import com.lambda.util.text.literal
2531import kotlinx.coroutines.Dispatchers
2632import kotlinx.coroutines.launch
2733import net.minecraft.client.input.Input
34+ import net.minecraft.client.sound.PositionedSoundInstance
35+ import net.minecraft.client.sound.SoundInstance
36+ import net.minecraft.sound.SoundEvents
2837import net.minecraft.util.math.Vec3d
38+ import java.awt.Color
2939import java.io.File
3040import java.lang.reflect.Type
3141import kotlin.time.Duration
3242import kotlin.time.DurationUnit
3343import kotlin.time.toDuration
3444
3545// ToDo:
36- // - Use a custom binary format to store the data (Protobuf / DB?)
37- // - Record other types of inputs: (Interactions, etc.)
46+ // - Record other types of inputs: (place, break, inventory, etc.)
47+ // - Fancy logging
48+ // - Add HUD for recording / replaying info
49+ // - Maybe use a custom binary format to store the data (Protobuf / DB?)
3850object Replay : Module(
3951 name = " Replay" ,
4052 description = " Record gameplay actions and replay them like a TAS." ,
@@ -78,7 +90,12 @@ object Replay : Module(
7890 fun loadRecording (file : File ) {
7991 recording = gsonCompact.fromJson(file.readText(), Recording ::class .java)
8092
81- info(" Recording ${file.nameWithoutExtension} loaded. Duration: ${recording?.duration} ." )
93+ info(buildText {
94+ literal(" Recording " )
95+ color(Color .GRAY ) { literal(file.nameWithoutExtension) }
96+ literal(" loaded. Duration: " )
97+ color(Color .GRAY ) { literal(recording?.duration.toString()) }
98+ })
8299 }
83100
84101 init {
@@ -110,10 +127,10 @@ object Replay : Module(
110127 val diff = pos.subtract(player.pos).length()
111128 if (diff < 0.001 ) return @a
112129
113- this @Replay.warn(" Position deviates from the recording by ${" %.3f" .format(diff)} blocks." )
130+ this @Replay.warn(" Position deviates from the recording by ${" %.3f" .format(diff)} blocks. Desired position: ${pos.asString( 3 )} " )
114131 if (cancelOnDeviation && diff > deviationThreshold) {
115132 state = State .INACTIVE
116- this @Replay.warn (" Replay cancelled due to exceeding deviation threshold." )
133+ this @Replay.logError (" Replay cancelled due to exceeding deviation threshold." )
117134 return @listener
118135 }
119136 }
@@ -165,12 +182,21 @@ object Replay : Module(
165182 } else {
166183 if (state != State .PLAYING_CHECKPOINTS ) {
167184 state = State .INACTIVE
168- this @Replay.info(" Replay finished after ${recording?.duration} ." )
185+ this @Replay.info(buildText {
186+ literal(" Replay finished after " )
187+ color(Color .GRAY ) { literal(it.duration.toString()) }
188+ literal(" ." )
189+ })
169190 return @listener
170191 }
171192
172193 state = State .RECORDING
173194 recording = checkpoint?.duplicate()
195+ mc.soundManager.play(
196+ PositionedSoundInstance .master(
197+ SoundEvents .ENTITY_EXPERIENCE_ORB_PICKUP , 1.0f
198+ )
199+ )
174200 this @Replay.info(" Checkpoint replayed. Continued recording..." )
175201 }
176202 }
@@ -186,7 +212,7 @@ object Replay : Module(
186212 recording?.let {
187213 state = State .PLAYING
188214 replay = it.duplicate()
189- this @Replay.info(" Replay started. ETA : ${it.duration} ." )
215+ this @Replay.info(" Replay started. Duration : ${it.duration} ." )
190216 } ? : run {
191217 this @Replay.warn(" No recording to replay." )
192218 }
@@ -210,10 +236,13 @@ object Replay : Module(
210236 }
211237 }
212238
213- private fun handleRecord () {
239+ private fun SafeContext. handleRecord () {
214240 when (state) {
215241 State .RECORDING -> {
216242 state = State .INACTIVE
243+ recording?.let {
244+ save(it, " recording" )
245+ }
217246 this @Replay.info(" Recording stopped. Recorded for ${recording?.duration} ." )
218247 }
219248 State .INACTIVE -> {
@@ -225,10 +254,13 @@ object Replay : Module(
225254 }
226255 }
227256
228- private fun handleStop () {
257+ private fun SafeContext. handleStop () {
229258 when (state) {
230259 State .RECORDING , State .PAUSED_RECORDING -> {
231260 state = State .INACTIVE
261+ recording?.let {
262+ save(it, " recording" )
263+ }
232264 this @Replay.info(" Recording stopped. Recorded for ${recording?.duration} ." )
233265 }
234266 State .PLAYING , State .PAUSED_REPLAY , State .PLAYING_CHECKPOINTS -> {
@@ -248,15 +280,8 @@ object Replay : Module(
248280 }
249281
250282 checkpoint = recording?.duplicate()
251- lambdaScope.launch(Dispatchers .IO ) {
252- FolderRegister .replay.mkdirs()
253- FolderRegister .replay.resolve(" checkpoint-${
254- mc.currentServerEntry?.address?.replace(" :" , " _" )
255- } -${
256- world.dimensionKey?.value?.path?.replace(" /" , " _" )
257- } -${
258- System .currentTimeMillis()
259- } .json" ).writeText(gsonCompact.toJson(checkpoint))
283+ checkpoint?.let {
284+ save(it, " checkpoint" )
260285 }
261286 this @Replay.info(" Checkpoint created." )
262287 }
@@ -269,17 +294,31 @@ object Replay : Module(
269294 State .INACTIVE -> {
270295 state = State .PLAYING_CHECKPOINTS
271296 replay = checkpoint?.duplicate()
272- this @Replay.info(" Replaying until last set checkpoint. ETA : ${checkpoint?.duration} " )
297+ this @Replay.info(" Replaying until last set checkpoint. Duration : ${checkpoint?.duration} " )
273298 }
274299 else -> {}
275300 }
276301 }
277302
303+ private fun SafeContext.save (recording : Recording , name : String ) {
304+ lambdaScope.launch(Dispatchers .IO ) {
305+ FolderRegister .replay.mkdirs()
306+ FolderRegister .replay.resolve(" $name -${
307+ mc.currentServerEntry?.address?.replace(" :" , " _" )
308+ } -${
309+ world.dimensionKey?.value?.path?.replace(" /" , " _" )
310+ } -${
311+ System .currentTimeMillis()
312+ } .json" ).writeText(gsonCompact.toJson(recording))
313+ }
314+ }
315+
278316 data class Recording (
279317 val input : MutableList <InputAction > = mutableListOf(),
280318 val rotation : MutableList <Rotation > = mutableListOf(),
281319 val sprint : MutableList <Boolean > = mutableListOf(),
282- val position : MutableList <Vec3d > = mutableListOf()
320+ val position : MutableList <Vec3d > = mutableListOf(),
321+ // val interaction: MutableList<Interaction> = mutableListOf()
283322 ) : JsonSerializer<Recording>, JsonDeserializer<Recording> {
284323 val size: Int
285324 get() = minOf(input.size, rotation.size, sprint.size, position.size)
0 commit comments