From 91b5e88b4d4eb42e0acb78aa065080a0b839e3d5 Mon Sep 17 00:00:00 2001 From: Scribble Date: Fri, 13 Dec 2024 21:05:18 +0100 Subject: [PATCH 01/16] [PlaybackSerialiser] Renamed "integrated" packages to "builtin" Was always unsatisfied with the name "integrated"... But I recently took a look at martinvonz/jj and found that they use the name "builtin" and I just had to switch to it... --- src/main/java/com/minecrafttas/tasmod/TASmod.java | 2 +- .../java/com/minecrafttas/tasmod/TASmodClient.java | 12 ++++++------ .../java/com/minecrafttas/tasmod/gui/InfoHud.java | 2 +- .../DesyncMonitorFileCommandExtension.java | 7 +++++-- .../LabelFileCommandExtension.java | 2 +- .../OptionsFileCommandExtension.java | 2 +- .../CreditsMetadataExtension.java | 2 +- .../StartpositionMetadataExtension.java | 2 +- .../playback/tasfile/PlaybackSerialiserOld.java | 2 +- .../flavor/{integrated => builtin}/Beta1Flavor.java | 2 +- .../tasmod/registries/TASmodAPIRegistry.java | 2 +- 11 files changed, 20 insertions(+), 17 deletions(-) rename src/main/java/com/minecrafttas/tasmod/playback/filecommands/{integrated => builtin}/DesyncMonitorFileCommandExtension.java (97%) rename src/main/java/com/minecrafttas/tasmod/playback/filecommands/{integrated => builtin}/LabelFileCommandExtension.java (96%) rename src/main/java/com/minecrafttas/tasmod/playback/filecommands/{integrated => builtin}/OptionsFileCommandExtension.java (97%) rename src/main/java/com/minecrafttas/tasmod/playback/metadata/{integrated => builtin}/CreditsMetadataExtension.java (98%) rename src/main/java/com/minecrafttas/tasmod/playback/metadata/{integrated => builtin}/StartpositionMetadataExtension.java (99%) rename src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/{integrated => builtin}/Beta1Flavor.java (80%) diff --git a/src/main/java/com/minecrafttas/tasmod/TASmod.java b/src/main/java/com/minecrafttas/tasmod/TASmod.java index a495dfad..c0ead5bd 100644 --- a/src/main/java/com/minecrafttas/tasmod/TASmod.java +++ b/src/main/java/com/minecrafttas/tasmod/TASmod.java @@ -26,7 +26,7 @@ import com.minecrafttas.tasmod.commands.CommandTickrate; import com.minecrafttas.tasmod.commands.TabCompletionUtils; import com.minecrafttas.tasmod.playback.PlaybackControllerServer; -import com.minecrafttas.tasmod.playback.metadata.integrated.StartpositionMetadataExtension; +import com.minecrafttas.tasmod.playback.metadata.builtin.StartpositionMetadataExtension; import com.minecrafttas.tasmod.registries.TASmodPackets; import com.minecrafttas.tasmod.savestates.SavestateHandlerServer; import com.minecrafttas.tasmod.savestates.storage.SavestateMotionStorage; diff --git a/src/main/java/com/minecrafttas/tasmod/TASmodClient.java b/src/main/java/com/minecrafttas/tasmod/TASmodClient.java index 1d6a854a..1201f950 100644 --- a/src/main/java/com/minecrafttas/tasmod/TASmodClient.java +++ b/src/main/java/com/minecrafttas/tasmod/TASmodClient.java @@ -25,12 +25,12 @@ import com.minecrafttas.tasmod.handlers.LoadingScreenHandler; import com.minecrafttas.tasmod.playback.PlaybackControllerClient; import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; -import com.minecrafttas.tasmod.playback.filecommands.integrated.DesyncMonitorFileCommandExtension; -import com.minecrafttas.tasmod.playback.filecommands.integrated.LabelFileCommandExtension; -import com.minecrafttas.tasmod.playback.filecommands.integrated.OptionsFileCommandExtension; -import com.minecrafttas.tasmod.playback.metadata.integrated.CreditsMetadataExtension; -import com.minecrafttas.tasmod.playback.metadata.integrated.StartpositionMetadataExtension; -import com.minecrafttas.tasmod.playback.tasfile.flavor.integrated.Beta1Flavor; +import com.minecrafttas.tasmod.playback.filecommands.builtin.DesyncMonitorFileCommandExtension; +import com.minecrafttas.tasmod.playback.filecommands.builtin.LabelFileCommandExtension; +import com.minecrafttas.tasmod.playback.filecommands.builtin.OptionsFileCommandExtension; +import com.minecrafttas.tasmod.playback.metadata.builtin.CreditsMetadataExtension; +import com.minecrafttas.tasmod.playback.metadata.builtin.StartpositionMetadataExtension; +import com.minecrafttas.tasmod.playback.tasfile.flavor.builtin.Beta1Flavor; import com.minecrafttas.tasmod.registries.TASmodAPIRegistry; import com.minecrafttas.tasmod.registries.TASmodConfig; import com.minecrafttas.tasmod.registries.TASmodKeybinds; diff --git a/src/main/java/com/minecrafttas/tasmod/gui/InfoHud.java b/src/main/java/com/minecrafttas/tasmod/gui/InfoHud.java index f2008f90..da37107f 100644 --- a/src/main/java/com/minecrafttas/tasmod/gui/InfoHud.java +++ b/src/main/java/com/minecrafttas/tasmod/gui/InfoHud.java @@ -19,7 +19,7 @@ import com.minecrafttas.tasmod.TASmodClient; import com.minecrafttas.tasmod.events.EventClient.EventDrawHotbar; import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; -import com.minecrafttas.tasmod.playback.filecommands.integrated.DesyncMonitorFileCommandExtension; +import com.minecrafttas.tasmod.playback.filecommands.builtin.DesyncMonitorFileCommandExtension; import com.mojang.realmsclient.gui.ChatFormatting; import net.minecraft.client.Minecraft; diff --git a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/integrated/DesyncMonitorFileCommandExtension.java b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/DesyncMonitorFileCommandExtension.java similarity index 97% rename from src/main/java/com/minecrafttas/tasmod/playback/filecommands/integrated/DesyncMonitorFileCommandExtension.java rename to src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/DesyncMonitorFileCommandExtension.java index 045e377a..354cd7e9 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/integrated/DesyncMonitorFileCommandExtension.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/DesyncMonitorFileCommandExtension.java @@ -1,5 +1,6 @@ -package com.minecrafttas.tasmod.playback.filecommands.integrated; +package com.minecrafttas.tasmod.playback.filecommands.builtin; +import java.io.File; import java.io.IOException; import java.io.Serializable; import java.text.NumberFormat; @@ -29,6 +30,8 @@ */ public class DesyncMonitorFileCommandExtension extends PlaybackFileCommandExtension implements EventPlaybackClient.EventControllerStateChange { + private File tempDir = new File(Minecraft.getMinecraft().mcDataDir.getAbsolutePath() + File.separator + "saves" + File.separator + "tasfiles" + File.separator + "temp" + File.separator + "monitoring"); + /** * List containing {@link MonitorContainer MonitorContainers} in a TASfile */ @@ -354,4 +357,4 @@ public void onClear() { lastPos = ""; lastMotion = ""; } -} +} \ No newline at end of file diff --git a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/integrated/LabelFileCommandExtension.java b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/LabelFileCommandExtension.java similarity index 96% rename from src/main/java/com/minecrafttas/tasmod/playback/filecommands/integrated/LabelFileCommandExtension.java rename to src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/LabelFileCommandExtension.java index 97a523f3..d7503ea8 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/integrated/LabelFileCommandExtension.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/LabelFileCommandExtension.java @@ -1,4 +1,4 @@ -package com.minecrafttas.tasmod.playback.filecommands.integrated; +package com.minecrafttas.tasmod.playback.filecommands.builtin; import java.io.IOException; diff --git a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/integrated/OptionsFileCommandExtension.java b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/OptionsFileCommandExtension.java similarity index 97% rename from src/main/java/com/minecrafttas/tasmod/playback/filecommands/integrated/OptionsFileCommandExtension.java rename to src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/OptionsFileCommandExtension.java index 2a1a5ef1..c1629eb6 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/integrated/OptionsFileCommandExtension.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/OptionsFileCommandExtension.java @@ -1,4 +1,4 @@ -package com.minecrafttas.tasmod.playback.filecommands.integrated; +package com.minecrafttas.tasmod.playback.filecommands.builtin; import java.io.IOException; diff --git a/src/main/java/com/minecrafttas/tasmod/playback/metadata/integrated/CreditsMetadataExtension.java b/src/main/java/com/minecrafttas/tasmod/playback/metadata/builtin/CreditsMetadataExtension.java similarity index 98% rename from src/main/java/com/minecrafttas/tasmod/playback/metadata/integrated/CreditsMetadataExtension.java rename to src/main/java/com/minecrafttas/tasmod/playback/metadata/builtin/CreditsMetadataExtension.java index a7eec12d..2c849b9e 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/metadata/integrated/CreditsMetadataExtension.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/metadata/builtin/CreditsMetadataExtension.java @@ -1,4 +1,4 @@ -package com.minecrafttas.tasmod.playback.metadata.integrated; +package com.minecrafttas.tasmod.playback.metadata.builtin; import static com.minecrafttas.tasmod.TASmod.LOGGER; diff --git a/src/main/java/com/minecrafttas/tasmod/playback/metadata/integrated/StartpositionMetadataExtension.java b/src/main/java/com/minecrafttas/tasmod/playback/metadata/builtin/StartpositionMetadataExtension.java similarity index 99% rename from src/main/java/com/minecrafttas/tasmod/playback/metadata/integrated/StartpositionMetadataExtension.java rename to src/main/java/com/minecrafttas/tasmod/playback/metadata/builtin/StartpositionMetadataExtension.java index 1534db65..e3adc118 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/metadata/integrated/StartpositionMetadataExtension.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/metadata/builtin/StartpositionMetadataExtension.java @@ -1,4 +1,4 @@ -package com.minecrafttas.tasmod.playback.metadata.integrated; +package com.minecrafttas.tasmod.playback.metadata.builtin; import static com.minecrafttas.tasmod.TASmod.LOGGER; diff --git a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiserOld.java b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiserOld.java index 79e2805c..7aac7671 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiserOld.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiserOld.java @@ -14,7 +14,7 @@ import com.dselent.bigarraylist.BigArrayList; import com.minecrafttas.tasmod.playback.PlaybackControllerClient; import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TickContainer; -import com.minecrafttas.tasmod.playback.filecommands.integrated.DesyncMonitorFileCommandExtension; +import com.minecrafttas.tasmod.playback.filecommands.builtin.DesyncMonitorFileCommandExtension; import com.minecrafttas.tasmod.util.FileThread; import com.minecrafttas.tasmod.util.LoggerMarkers; import com.minecrafttas.tasmod.virtual.VirtualCameraAngle; diff --git a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/integrated/Beta1Flavor.java b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/Beta1Flavor.java similarity index 80% rename from src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/integrated/Beta1Flavor.java rename to src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/Beta1Flavor.java index 39f5164c..8801f687 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/integrated/Beta1Flavor.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/Beta1Flavor.java @@ -1,4 +1,4 @@ -package com.minecrafttas.tasmod.playback.tasfile.flavor.integrated; +package com.minecrafttas.tasmod.playback.tasfile.flavor.builtin; import com.minecrafttas.tasmod.playback.tasfile.flavor.SerialiserFlavorBase; diff --git a/src/main/java/com/minecrafttas/tasmod/registries/TASmodAPIRegistry.java b/src/main/java/com/minecrafttas/tasmod/registries/TASmodAPIRegistry.java index 62804042..308cedf9 100644 --- a/src/main/java/com/minecrafttas/tasmod/registries/TASmodAPIRegistry.java +++ b/src/main/java/com/minecrafttas/tasmod/registries/TASmodAPIRegistry.java @@ -4,7 +4,7 @@ import com.minecrafttas.tasmod.playback.metadata.PlaybackMetadataRegistry; import com.minecrafttas.tasmod.playback.tasfile.flavor.SerialiserFlavorBase; import com.minecrafttas.tasmod.playback.tasfile.flavor.SerialiserFlavorRegistry; -import com.minecrafttas.tasmod.playback.tasfile.flavor.integrated.Beta1Flavor; +import com.minecrafttas.tasmod.playback.tasfile.flavor.builtin.Beta1Flavor; public class TASmodAPIRegistry { /** From 76e615499dbf2843afa6fe50ae652439e6484b12 Mon Sep 17 00:00:00 2001 From: Scribble Date: Fri, 13 Dec 2024 23:00:49 +0100 Subject: [PATCH 02/16] [Flavor] Add AlphaFlavor and finished header serialisation - [Metadata] Added enum options to CreditsMetadataExtension and StartpositionMetadataExtension for easier retrieval of values --- .../mctcommon/registry/Registerable.java | 9 ++ .../com/minecrafttas/tasmod/TASmodClient.java | 3 + .../playback/metadata/PlaybackMetadata.java | 8 ++ .../builtin/CreditsMetadataExtension.java | 34 +++++-- .../StartpositionMetadataExtension.java | 49 +++++++--- .../tasfile/flavor/SerialiserFlavorBase.java | 90 +++++++++++++++---- .../tasfile/flavor/builtin/AlphaFlavor.java | 76 ++++++++++++++++ 7 files changed, 231 insertions(+), 38 deletions(-) create mode 100644 src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java diff --git a/src/main/java/com/minecrafttas/mctcommon/registry/Registerable.java b/src/main/java/com/minecrafttas/mctcommon/registry/Registerable.java index 226179a6..a7b49278 100644 --- a/src/main/java/com/minecrafttas/mctcommon/registry/Registerable.java +++ b/src/main/java/com/minecrafttas/mctcommon/registry/Registerable.java @@ -1,5 +1,14 @@ package com.minecrafttas.mctcommon.registry; +/** + * Interface for declaring that a class can be registered by a class of type {@link AbstractRegistry} + * + * @author Scribble + */ public interface Registerable { + + /** + * @return The name of the extension that is registered + */ public String getExtensionName(); } \ No newline at end of file diff --git a/src/main/java/com/minecrafttas/tasmod/TASmodClient.java b/src/main/java/com/minecrafttas/tasmod/TASmodClient.java index 1201f950..ffd81dae 100644 --- a/src/main/java/com/minecrafttas/tasmod/TASmodClient.java +++ b/src/main/java/com/minecrafttas/tasmod/TASmodClient.java @@ -30,6 +30,7 @@ import com.minecrafttas.tasmod.playback.filecommands.builtin.OptionsFileCommandExtension; import com.minecrafttas.tasmod.playback.metadata.builtin.CreditsMetadataExtension; import com.minecrafttas.tasmod.playback.metadata.builtin.StartpositionMetadataExtension; +import com.minecrafttas.tasmod.playback.tasfile.flavor.builtin.AlphaFlavor; import com.minecrafttas.tasmod.playback.tasfile.flavor.builtin.Beta1Flavor; import com.minecrafttas.tasmod.registries.TASmodAPIRegistry; import com.minecrafttas.tasmod.registries.TASmodConfig; @@ -312,9 +313,11 @@ private void registerPlaybackMetadata(Minecraft mc) { } public static Beta1Flavor betaFlavor = new Beta1Flavor(); + public static AlphaFlavor alphaFlavor = new AlphaFlavor(); private void registerSerialiserFlavors(Minecraft mc) { TASmodAPIRegistry.SERIALISER_FLAVOR.register(betaFlavor); + TASmodAPIRegistry.SERIALISER_FLAVOR.register(alphaFlavor); } public static DesyncMonitorFileCommandExtension desyncMonitorFileCommandExtension; diff --git a/src/main/java/com/minecrafttas/tasmod/playback/metadata/PlaybackMetadata.java b/src/main/java/com/minecrafttas/tasmod/playback/metadata/PlaybackMetadata.java index fe4da30c..50ec7a06 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/metadata/PlaybackMetadata.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/metadata/PlaybackMetadata.java @@ -37,10 +37,18 @@ public void setValue(String key, String value) { data.put(key, value); } + public void setValue(Object key, String value) { + setValue(key.toString(), value); + } + public String getValue(String key) { return data.get(key); } + public String getValue(Object key) { + return getValue(key.toString()); + } + @Override public String toString() { String out = ""; diff --git a/src/main/java/com/minecrafttas/tasmod/playback/metadata/builtin/CreditsMetadataExtension.java b/src/main/java/com/minecrafttas/tasmod/playback/metadata/builtin/CreditsMetadataExtension.java index 2c849b9e..66434703 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/metadata/builtin/CreditsMetadataExtension.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/metadata/builtin/CreditsMetadataExtension.java @@ -54,23 +54,41 @@ public void onCreate() { // Unused atm } + public enum CreditFields { + Title("Title"), + Author("Author"), + PlayTime("Playing Time"), + Rerecords("Rerecords"); + + private final String name; + + private CreditFields(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } + } + @Override public PlaybackMetadata onStore() { PlaybackMetadata metadata = new PlaybackMetadata(this); - metadata.setValue("Title", title); - metadata.setValue("Author", authors); - metadata.setValue("Playing Time", playtime); - metadata.setValue("Rerecords", Integer.toString(rerecords)); + metadata.setValue(CreditFields.Title, title); + metadata.setValue(CreditFields.Author, authors); + metadata.setValue(CreditFields.PlayTime, playtime); + metadata.setValue(CreditFields.Rerecords, Integer.toString(rerecords)); return metadata; } @Override public void onLoad(PlaybackMetadata metadata) { - title = metadata.getValue("Title"); - authors = metadata.getValue("Author"); - playtime = metadata.getValue("Playing Time"); + title = metadata.getValue(CreditFields.Title); + authors = metadata.getValue(CreditFields.Author); + playtime = metadata.getValue(CreditFields.PlayTime); try { - rerecords = Integer.parseInt(metadata.getValue("Rerecords")); + rerecords = Integer.parseInt(metadata.getValue(CreditFields.Rerecords)); } catch (NumberFormatException e) { rerecords = 0; throw new PlaybackLoadException(e); diff --git a/src/main/java/com/minecrafttas/tasmod/playback/metadata/builtin/StartpositionMetadataExtension.java b/src/main/java/com/minecrafttas/tasmod/playback/metadata/builtin/StartpositionMetadataExtension.java index e3adc118..efe2d3c0 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/metadata/builtin/StartpositionMetadataExtension.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/metadata/builtin/StartpositionMetadataExtension.java @@ -70,6 +70,25 @@ public void onCreate() { // Unused atm } + public enum StartPositionFields { + X("x"), + Y("y"), + Z("z"), + Pitch("pitch"), + Yaw("yaw"); + + private final String name; + + private StartPositionFields(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } + } + @Override public PlaybackMetadata onStore() { PlaybackMetadata metadata = new PlaybackMetadata(this); @@ -78,25 +97,29 @@ public PlaybackMetadata onStore() { if (this.startPosition != null) { startPositionToStore = startPosition; } - metadata.setValue("x", Double.toString(startPositionToStore.x)); - metadata.setValue("y", Double.toString(startPositionToStore.y)); - metadata.setValue("z", Double.toString(startPositionToStore.z)); - metadata.setValue("pitch", Float.toString(startPositionToStore.pitch)); - metadata.setValue("yaw", Float.toString(startPositionToStore.yaw)); + metadata.setValue(StartPositionFields.X, Double.toString(startPositionToStore.x)); + metadata.setValue(StartPositionFields.Y, Double.toString(startPositionToStore.y)); + metadata.setValue(StartPositionFields.Z, Double.toString(startPositionToStore.z)); + metadata.setValue(StartPositionFields.Pitch, Float.toString(startPositionToStore.pitch)); + metadata.setValue(StartPositionFields.Yaw, Float.toString(startPositionToStore.yaw)); return metadata; } @Override public void onLoad(PlaybackMetadata metadata) { - double x = getDouble("x", metadata); - double y = getDouble("y", metadata); - double z = getDouble("z", metadata); - float pitch = getFloat("pitch", metadata); - float yaw = getFloat("yaw", metadata); + double x = getDouble(StartPositionFields.X, metadata); + double y = getDouble(StartPositionFields.Y, metadata); + double z = getDouble(StartPositionFields.Z, metadata); + float pitch = getFloat(StartPositionFields.Pitch, metadata); + float yaw = getFloat(StartPositionFields.Yaw, metadata); this.startPosition = new StartPosition(x, y, z, pitch, yaw); } + private double getDouble(Object key, PlaybackMetadata metadata) { + return getDouble(key.toString(), metadata); + } + private double getDouble(String key, PlaybackMetadata metadata) { String out = metadata.getValue(key); if (out != null) { @@ -110,6 +133,10 @@ private double getDouble(String key, PlaybackMetadata metadata) { } } + private float getFloat(Object key, PlaybackMetadata metadata) { + return getFloat(key.toString(), metadata); + } + private float getFloat(String key, PlaybackMetadata metadata) { String out = metadata.getValue(key); if (out != null) { @@ -158,7 +185,7 @@ public void updateStartPosition() { if (player != null) startPosition = new StartPosition(player.posX, player.posY, player.posZ, player.rotationPitch, player.rotationYaw); else - LOGGER.warn("Start position not set, the player was null! This will make problems when storing inputs!"); + LOGGER.warn("Start position not set, the player was null! This will create problems when storing inputs!"); } diff --git a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java index 592bcf69..a24eb2bf 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java @@ -27,6 +27,32 @@ import com.minecrafttas.tasmod.virtual.VirtualKeyboard; import com.minecrafttas.tasmod.virtual.VirtualMouse; +/** + *

The base class of a flavor. + * + *

All serialisation and deserialisation is broken apart into functions whenever possible,
+ * with the intention of allowing small changes to the existing syntax. + * + *

Adding functionality to playback should be made via {@link PlaybackFileCommand PlaybackFileCommands} instead of creating a new syntax
+ * and adding new information to the header should be made via {@link PlaybackMetadata} + * + *

Sections

+ *

The TASfile has 2 main sections: + * + *

    + *
  1. + * {@link #serialiseHeader() Header}: Contains metadata about this TAS, like credits and start position,
    + * but also a list of enabled extensions and the name of the flavor that was used to encode the file. + *
  2. + *
  3. + * {@link #serialise(BigArrayList, long) Content}: Contains the actual inputs per tick, inputs in a subtick (a.k.a in a frame), comments and other extensions. + *
  4. + *
+ * + * Clicking on either of these will lead you to a breakdown in their respective javadocs + * + * @author Scribble + */ public abstract class SerialiserFlavorBase implements Registerable { protected long currentLine = 1; @@ -48,25 +74,6 @@ public abstract class SerialiserFlavorBase implements Registerable { */ protected boolean processExtensions = true; - protected String headerStart() { - return createCenteredHeading("TASfile", '#', 50); - } - - /** - * @return The regex used for detecting comment lines - */ - protected String singleComment() { - return "^//"; - } - - protected String endlineComment() { - return "(//.+)"; - } - - protected String headerEnd() { - return createPaddedString('#', 50); - } - /*============================================== _____ _ _ _ / ____| (_) | (_) @@ -77,6 +84,33 @@ protected String headerEnd() { ==============================================*/ + /* + _ _ ____ __ ____ ____ ____ + ( )_( )( ___) /__\ ( _ \( ___)( _ \ + ) _ ( )__) /(__)\ )(_) ))__) ) / + (_) (_)(____)(__)(__)(____/(____)(_)\_) + + */ + + /** + * @return The very top of the header + */ + protected String headerStart() { + return createCenteredHeading("TASfile", '#', 50); + } + + /** + * The end of the header, used for detecting when the header stops + * @return The end of the header + */ + protected String headerEnd() { + return createPaddedString('#', 50); + } + + /** + * + * @return List of lines containing the header + */ public List serialiseHeader() { List out = new ArrayList<>(); out.add(headerStart()); @@ -1070,6 +1104,17 @@ protected > void pruneListEndEmptySubtickable(List l } } + /** + * @return The regex used for detecting comment lines + */ + protected String singleComment() { + return "^//"; + } + + protected String endlineComment() { + return "(//.+)"; + } + @Override public abstract SerialiserFlavorBase clone(); @@ -1082,6 +1127,13 @@ public boolean equals(Object obj) { return super.equals(obj); } + /** + * Set if extensions should be loaded. + * + * Setting this to false will stop {@link TASmodAPIRegistry#PLAYBACK_FILE_COMMAND} and {@link TASmodAPIRegistry#PLAYBACK_METADATA} from being processed + * + * @param processExtensions + */ public void setProcessExtensions(boolean processExtensions) { this.processExtensions = processExtensions; } diff --git a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java new file mode 100644 index 00000000..ca67239c --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java @@ -0,0 +1,76 @@ +package com.minecrafttas.tasmod.playback.tasfile.flavor.builtin; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; + +import com.minecrafttas.tasmod.playback.metadata.PlaybackMetadata; +import com.minecrafttas.tasmod.playback.metadata.builtin.CreditsMetadataExtension.CreditFields; +import com.minecrafttas.tasmod.playback.tasfile.flavor.SerialiserFlavorBase; +import com.minecrafttas.tasmod.registries.TASmodAPIRegistry; + +public class AlphaFlavor extends SerialiserFlavorBase { + + @Override + public String getExtensionName() { + return "alpha"; + } + + @Override + public SerialiserFlavorBase clone() { + return new AlphaFlavor(); + } + + @Override + public List serialiseHeader() { + List out = new ArrayList<>(); + + out.add("################################################# TASFile ###################################################\n" + + "# Version:1 #\n" + + "# This file was generated using the Minecraft TASMod #\n" + + "# #\n" + + "# Any errors while reading this file will be printed out in the console and the chat #\n" + + "# #"); + serialiseMetadata(out); + out.add("#############################################################################################################\n" + + "#Comments start with \"//\" at the start of the line, comments with # will not be saved"); + return out; + } + + @Override + protected void serialiseMetadata(List out) { + if (!processExtensions) + return; + + List metadataList = TASmodAPIRegistry.PLAYBACK_METADATA.handleOnStore(); + + PlaybackMetadata credits = null; + PlaybackMetadata startPosition = null; + + for (PlaybackMetadata metadata : metadataList) { + String name = metadata.getExtensionName(); + if (name.equals("Credits")) + credits = metadata; + else if (name.equals("Start Position")) + startPosition = metadata; + } + out.add("#------------------------------------------------ Header ---------------------------------------------------#\n" + + "#Author:" + credits.getValue(CreditFields.Author) + "\n" + + "# #\n" + + "#Title:" + credits.getValue(CreditFields.Title) + "\n" + + "# #\n" + + "#Playing Time:" + credits.getValue(CreditFields.PlayTime) + "\n" + + "# #\n" + + "#Rerecords:" + credits.getValue(CreditFields.Rerecords) + "\n" + + "# #\n" + + "#----------------------------------------------- Settings --------------------------------------------------#\n" + + "#StartPosition:" + processStartPosition(startPosition) + "\n" + + "# #\n" + + "#StartSeed:" + 0); // TODO Add ktrng seed? + } + + protected String processStartPosition(PlaybackMetadata startPosition) { + LinkedHashMap data = startPosition.getData(); + return String.join(",", data.values()); + } +} From f67d61151231ad1ae1089c3bc0cce09f99e2771b Mon Sep 17 00:00:00 2001 From: Scribble Date: Sun, 15 Dec 2024 14:25:48 +0100 Subject: [PATCH 03/16] [Flavor] Add header deserialisation to alpha flavor - Add test for alpha flavor - Made some metadata values protected --- .../PlaybackFileCommandsRegistry.java | 50 ++-- .../DesyncMonitorFileCommandExtension.java | 5 +- .../builtin/OptionsFileCommandExtension.java | 5 + .../playback/metadata/PlaybackMetadata.java | 4 +- .../builtin/CreditsMetadataExtension.java | 24 +- .../StartpositionMetadataExtension.java | 12 +- .../playback/tasfile/PlaybackSerialiser.java | 2 +- .../tasfile/flavor/SerialiserFlavorBase.java | 17 +- .../tasfile/flavor/builtin/AlphaFlavor.java | 111 ++++++++- .../tasfile/builtin/AlphaFlavorTest.java | 216 ++++++++++++++++++ 10 files changed, 390 insertions(+), 56 deletions(-) create mode 100644 src/test/java/tasmod/playback/tasfile/builtin/AlphaFlavorTest.java diff --git a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/PlaybackFileCommandsRegistry.java b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/PlaybackFileCommandsRegistry.java index f5fd3c3f..29d472df 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/PlaybackFileCommandsRegistry.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/PlaybackFileCommandsRegistry.java @@ -16,9 +16,9 @@ public class PlaybackFileCommandsRegistry extends AbstractRegistry implements EventPlaybackClient.EventRecordTick, EventPlaybackClient.EventPlaybackTick, EventPlaybackClient.EventRecordClear { private List enabledExtensions = new ArrayList<>(); - + private Configuration config = null; - + public PlaybackFileCommandsRegistry() { super("FILECOMMAND_REGISTRY", new LinkedHashMap<>()); } @@ -38,16 +38,16 @@ public void unregister(PlaybackFileCommandExtension extension) { public boolean setEnabled(String extensionName, boolean enabled) { return setEnabled(extensionName, enabled, true); } - + public boolean setEnabled(String extensionName, boolean enabled, boolean saveToConfig) { PlaybackFileCommandExtension extension = REGISTRY.get(extensionName); - if(extension == null) { + if (extension == null) { return false; } extension.setEnabled(enabled); enabledExtensions = getEnabled(); - - if(saveToConfig) { + + if (saveToConfig) { saveConfig(); } return true; @@ -59,16 +59,20 @@ private void disableAll() { }); } + public void setEnabled(String... extensionNames) { + setEnabled(Arrays.asList(extensionNames)); + } + public void setEnabled(List extensionNames) { setEnabled(extensionNames, false); } - + public void setEnabled(List extensionNames, boolean saveToConfig) { disableAll(); for (String name : extensionNames) { setEnabled(name, true, false); } - if(saveToConfig) + if (saveToConfig) saveConfig(); } @@ -83,24 +87,24 @@ public List getEnabled() { return out; } - - public List getAll(){ + + public List getAll() { return new ArrayList<>(REGISTRY.values()); } - + @Override public void onRecordTick(long index, TickContainer container) { enabledExtensions.forEach(extension -> { - if(extension.isEnabled()) { + if (extension.isEnabled()) { extension.onRecord(index, container); } }); } - + @Override public void onPlaybackTick(long index, TickContainer container) { enabledExtensions.forEach(extension -> { - if(extension.isEnabled()) { + if (extension.isEnabled()) { extension.onPlayback(index, container); } }); @@ -109,8 +113,8 @@ public void onPlaybackTick(long index, TickContainer container) { public PlaybackFileCommandContainer handleOnSerialiseInline(long currentTick, TickContainer container) { PlaybackFileCommandContainer out = new PlaybackFileCommandContainer(); for (PlaybackFileCommandExtension extension : enabledExtensions) { - PlaybackFileCommandContainer extensionContainer=extension.onSerialiseInlineComment(currentTick, container); - if(extensionContainer!=null) { + PlaybackFileCommandContainer extensionContainer = extension.onSerialiseInlineComment(currentTick, container); + if (extensionContainer != null) { out.putAll(extensionContainer); } } @@ -120,8 +124,8 @@ public PlaybackFileCommandContainer handleOnSerialiseInline(long currentTick, Ti public PlaybackFileCommandContainer handleOnSerialiseEndline(long currentTick, TickContainer container) { PlaybackFileCommandContainer out = new PlaybackFileCommandContainer(); for (PlaybackFileCommandExtension extension : enabledExtensions) { - PlaybackFileCommandContainer extensionContainer=extension.onSerialiseEndlineComment(currentTick, container); - if(extensionContainer!=null) { + PlaybackFileCommandContainer extensionContainer = extension.onSerialiseEndlineComment(currentTick, container); + if (extensionContainer != null) { out.putAll(extensionContainer); } } @@ -155,7 +159,7 @@ public void setConfig(Configuration config) { this.config = config; loadConfig(); } - + private void loadConfig() { if (config == null) { return; @@ -163,14 +167,14 @@ private void loadConfig() { String enabled = config.get(TASmodConfig.EnabledFileCommands); setEnabled(Arrays.asList(enabled.split(", "))); } - + private void saveConfig() { if (config == null) { return; } - List nameList = new ArrayList<>(); - - enabledExtensions.forEach(element ->{ + List nameList = new ArrayList<>(); + + enabledExtensions.forEach(element -> { nameList.add(element.getExtensionName()); }); config.set(TASmodConfig.EnabledFileCommands, String.join(", ", nameList)); diff --git a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/DesyncMonitorFileCommandExtension.java b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/DesyncMonitorFileCommandExtension.java index 354cd7e9..dd3cf042 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/DesyncMonitorFileCommandExtension.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/DesyncMonitorFileCommandExtension.java @@ -1,6 +1,5 @@ package com.minecrafttas.tasmod.playback.filecommands.builtin; -import java.io.File; import java.io.IOException; import java.io.Serializable; import java.text.NumberFormat; @@ -30,8 +29,6 @@ */ public class DesyncMonitorFileCommandExtension extends PlaybackFileCommandExtension implements EventPlaybackClient.EventControllerStateChange { - private File tempDir = new File(Minecraft.getMinecraft().mcDataDir.getAbsolutePath() + File.separator + "saves" + File.separator + "tasfiles" + File.separator + "temp" + File.separator + "monitoring"); - /** * List containing {@link MonitorContainer MonitorContainers} in a TASfile */ @@ -352,7 +349,7 @@ public void onClear() { } catch (IOException e) { e.printStackTrace(); } - monitorContainer = new BigArrayList(tempDir.toString()); + monitorContainer = new BigArrayList(); lastStatus = TextFormatting.GRAY + "Empty"; lastPos = ""; lastMotion = ""; diff --git a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/OptionsFileCommandExtension.java b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/OptionsFileCommandExtension.java index c1629eb6..0e1193b4 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/OptionsFileCommandExtension.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/OptionsFileCommandExtension.java @@ -58,6 +58,11 @@ public void onPlayback(long tick, TickContainer tickContainer) { for (PlaybackFileCommand command : line) { String[] args = command.getArgs(); if (args.length == 1) { + /* + * Ok this may seem dumb, but Boolean.parseBoolean returns false, + * even if something other then true or false was passed... + * If someone finds something less idiotic please tell me... + */ switch (args[0]) { case "true": shouldRenderHud = true; diff --git a/src/main/java/com/minecrafttas/tasmod/playback/metadata/PlaybackMetadata.java b/src/main/java/com/minecrafttas/tasmod/playback/metadata/PlaybackMetadata.java index 50ec7a06..aada9d18 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/metadata/PlaybackMetadata.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/metadata/PlaybackMetadata.java @@ -23,12 +23,12 @@ public PlaybackMetadata(PlaybackMetadataExtension extension) { this(extension.getExtensionName()); } - private PlaybackMetadata(String extensionName) { + public PlaybackMetadata(String extensionName) { this.extensionName = extensionName; this.data = new LinkedHashMap(); } - private PlaybackMetadata(String extensionName, LinkedHashMap data) { + public PlaybackMetadata(String extensionName, LinkedHashMap data) { this.extensionName = extensionName; this.data = data; } diff --git a/src/main/java/com/minecrafttas/tasmod/playback/metadata/builtin/CreditsMetadataExtension.java b/src/main/java/com/minecrafttas/tasmod/playback/metadata/builtin/CreditsMetadataExtension.java index 66434703..c1e7275b 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/metadata/builtin/CreditsMetadataExtension.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/metadata/builtin/CreditsMetadataExtension.java @@ -25,24 +25,24 @@ public class CreditsMetadataExtension extends PlaybackMetadataExtension implemen /** * The title/category of the TAS (e.g. KillSquid - Any% Glitched) */ - private String title = "Insert TAS category here"; + protected String title = "Insert TAS category here"; /** * The author(s) of the TAS (e.g. Scribble, Pancake) */ - private String authors = "Insert author here"; + protected String authors = "Insert author here"; /** * How long the TAS is going to take (e.g. 00:01.0 or 20ticks) */ - private String playtime = "00:00.0"; + protected String playtime = "00:00.0"; /** * How often a savestate was loaded as a measurement of effort (e.g. 200) */ - private int rerecords = 0; + protected int rerecords = 0; /** * If the credits where already printed in this instance */ - private boolean creditsPrinted = false; + protected boolean creditsPrinted = false; @Override public String getExtensionName() { @@ -84,17 +84,21 @@ public PlaybackMetadata onStore() { @Override public void onLoad(PlaybackMetadata metadata) { - title = metadata.getValue(CreditFields.Title); - authors = metadata.getValue(CreditFields.Author); - playtime = metadata.getValue(CreditFields.PlayTime); + title = getOrDefault(metadata.getValue(CreditFields.Title), title); + authors = getOrDefault(metadata.getValue(CreditFields.Author), authors); + playtime = getOrDefault(metadata.getValue(CreditFields.PlayTime), playtime); try { - rerecords = Integer.parseInt(metadata.getValue(CreditFields.Rerecords)); + rerecords = Integer.parseInt(getOrDefault(metadata.getValue(CreditFields.Rerecords), Integer.toString(rerecords))); } catch (NumberFormatException e) { rerecords = 0; throw new PlaybackLoadException(e); } } + protected String getOrDefault(String value, String defaultVal) { + return value != null ? value : defaultVal; + } + @Override public void onClear() { title = "Insert TAS category here"; @@ -119,7 +123,7 @@ public void onPlaybackJoinedWorld(TASstate state) { } } - private void printMessage(String msg, TextFormatting format) { + protected void printMessage(String msg, TextFormatting format) { String formatString = ""; if (format != null) formatString = format.toString(); diff --git a/src/main/java/com/minecrafttas/tasmod/playback/metadata/builtin/StartpositionMetadataExtension.java b/src/main/java/com/minecrafttas/tasmod/playback/metadata/builtin/StartpositionMetadataExtension.java index efe2d3c0..1e07763e 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/metadata/builtin/StartpositionMetadataExtension.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/metadata/builtin/StartpositionMetadataExtension.java @@ -36,15 +36,15 @@ public class StartpositionMetadataExtension extends PlaybackMetadataExtension im /** * The startposition of the playback */ - StartPosition startPosition = null; + protected StartPosition startPosition = null; public static class StartPosition { - final double x; - final double y; - final double z; - final float pitch; - final float yaw; + public final double x; + public final double y; + public final double z; + public final float pitch; + public final float yaw; public StartPosition(double x, double y, double z, float pitch, float yaw) { this.x = x; diff --git a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiser.java b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiser.java index 8a03969a..c1b24116 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiser.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiser.java @@ -234,7 +234,7 @@ public static BigArrayList loadFromFile(Path file, SerialiserFlav */ public static SerialiserFlavorBase searchForFlavor(List lines, List flavorList) { for (SerialiserFlavorBase flavor : flavorList) { - if (flavor.deserialiseFlavorName(lines)) { + if (flavor.checkFlavorName(lines)) { return flavor.clone(); } } diff --git a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java index a24eb2bf..82284888 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java @@ -361,6 +361,13 @@ protected String joinNotEmpty(String delimiter, Iterable args) { return out; } + /** + * Joins strings together but ignores empty strings + * + * @param delimiter The delimiter of the joined string + * @param args The strings to join + * @return Joined string + */ protected String joinNotEmpty(String delimiter, String... args) { return joinNotEmpty(delimiter, Arrays.asList(args)); } @@ -377,7 +384,13 @@ protected String joinNotEmpty(String delimiter, String... args) { * */ - public boolean deserialiseFlavorName(List headerLines) { + /** + *

Checks if the name of this flavor is present in the header of the TASfile. + *

Used to determine the flavor of the file if the flavor is not given + * @param headerLines The lines from the header to check + * @return True, if the flavor name is present in the header + */ + public boolean checkFlavorName(List headerLines) { for (String line : headerLines) { Matcher matcher = extract("^Flavor: " + getExtensionName(), line); @@ -425,7 +438,7 @@ protected void deserialiseFileCommandNames(List headerLines) { String extensionStrings = matcher.group(1); String[] extensionNames = extensionStrings.split(", ?"); - TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.setEnabled(Arrays.asList(extensionNames)); + TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.setEnabled(extensionNames); return; } } diff --git a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java index ca67239c..6e7044e7 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java @@ -1,11 +1,21 @@ package com.minecrafttas.tasmod.playback.tasfile.flavor.builtin; +import static com.minecrafttas.tasmod.playback.metadata.builtin.CreditsMetadataExtension.CreditFields.Author; +import static com.minecrafttas.tasmod.playback.metadata.builtin.CreditsMetadataExtension.CreditFields.PlayTime; +import static com.minecrafttas.tasmod.playback.metadata.builtin.CreditsMetadataExtension.CreditFields.Rerecords; +import static com.minecrafttas.tasmod.playback.metadata.builtin.CreditsMetadataExtension.CreditFields.Title; +import static com.minecrafttas.tasmod.playback.metadata.builtin.StartpositionMetadataExtension.StartPositionFields.Pitch; +import static com.minecrafttas.tasmod.playback.metadata.builtin.StartpositionMetadataExtension.StartPositionFields.X; +import static com.minecrafttas.tasmod.playback.metadata.builtin.StartpositionMetadataExtension.StartPositionFields.Y; +import static com.minecrafttas.tasmod.playback.metadata.builtin.StartpositionMetadataExtension.StartPositionFields.Yaw; +import static com.minecrafttas.tasmod.playback.metadata.builtin.StartpositionMetadataExtension.StartPositionFields.Z; + import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; +import java.util.regex.Matcher; import com.minecrafttas.tasmod.playback.metadata.PlaybackMetadata; -import com.minecrafttas.tasmod.playback.metadata.builtin.CreditsMetadataExtension.CreditFields; import com.minecrafttas.tasmod.playback.tasfile.flavor.SerialiserFlavorBase; import com.minecrafttas.tasmod.registries.TASmodAPIRegistry; @@ -21,22 +31,32 @@ public SerialiserFlavorBase clone() { return new AlphaFlavor(); } + @Override + protected String headerStart() { + return "################################################# TASFile ###################################################\n"; + } + @Override public List serialiseHeader() { List out = new ArrayList<>(); - out.add("################################################# TASFile ###################################################\n" + out.add(headerStart() + "# Version:1 #\n" + "# This file was generated using the Minecraft TASMod #\n" + "# #\n" + "# Any errors while reading this file will be printed out in the console and the chat #\n" + "# #"); serialiseMetadata(out); - out.add("#############################################################################################################\n" - + "#Comments start with \"//\" at the start of the line, comments with # will not be saved"); + out.add(headerEnd()); return out; } + @Override + protected String headerEnd() { + return "#############################################################################################################\n" + + "#Comments start with \"//\" at the start of the line, comments with # will not be saved"; + } + @Override protected void serialiseMetadata(List out) { if (!processExtensions) @@ -55,13 +75,13 @@ else if (name.equals("Start Position")) startPosition = metadata; } out.add("#------------------------------------------------ Header ---------------------------------------------------#\n" - + "#Author:" + credits.getValue(CreditFields.Author) + "\n" + + "#Author:" + credits.getValue(Author) + "\n" + "# #\n" - + "#Title:" + credits.getValue(CreditFields.Title) + "\n" + + "#Title:" + credits.getValue(Title) + "\n" + "# #\n" - + "#Playing Time:" + credits.getValue(CreditFields.PlayTime) + "\n" + + "#Playing Time:" + credits.getValue(PlayTime) + "\n" + "# #\n" - + "#Rerecords:" + credits.getValue(CreditFields.Rerecords) + "\n" + + "#Rerecords:" + credits.getValue(Rerecords) + "\n" + "# #\n" + "#----------------------------------------------- Settings --------------------------------------------------#\n" + "#StartPosition:" + processStartPosition(startPosition) + "\n" @@ -73,4 +93,79 @@ protected String processStartPosition(PlaybackMetadata startPosition) { LinkedHashMap data = startPosition.getData(); return String.join(",", data.values()); } + + @Override + public boolean checkFlavorName(List headerLines) { + for (String line : headerLines) { + Matcher matcher = extract("^#.*Version:1", line); + + if (matcher.find()) { + return true; + } + } + return false; + } + + @Override + protected void deserialiseMetadata(List headerLines) { + String author = "Insert author here"; + + String title = "Insert TAS category here"; + + String playtime = "00:00.0"; + + String rerecords = "0"; + // No default start location + String startLocation = ""; + + for (String line : headerLines) { + if (line.startsWith("#Author:")) { + author = line.split(":")[1]; + // Read title tag + } else if (line.startsWith("#Title:")) { + title = line.split(":")[1]; + // Read playtime + } else if (line.startsWith("#Playing Time:")) { + playtime = line.split("Playing Time:")[1]; + // Read rerecords + } else if (line.startsWith("#Rerecords:")) { + rerecords = line.split(":")[1]; + // Read start position + } else if (line.startsWith("#StartPosition:")) { + startLocation = line.replace("#StartPosition:", ""); + } +// // Read start seed +// else if (line.startsWith("#StartSeed:")) { +// startSeed = Long.parseLong(line.replace("#StartSeed:", "")); +// } + } + + PlaybackMetadata creditsMetada = new PlaybackMetadata("Credits"); + creditsMetada.setValue(Author, author); + creditsMetada.setValue(Title, title); + creditsMetada.setValue(PlayTime, playtime); + creditsMetada.setValue(Rerecords, rerecords); + + PlaybackMetadata startPositionMetadata = new PlaybackMetadata("Start Position"); + String[] split = startLocation.split(","); + startPositionMetadata.setValue(X, split[0]); + startPositionMetadata.setValue(Y, split[1]); + startPositionMetadata.setValue(Z, split[2]); + startPositionMetadata.setValue(Pitch, split[3]); + startPositionMetadata.setValue(Yaw, split[4]); + + List metadataList = new ArrayList<>(); + metadataList.add(creditsMetada); + metadataList.add(startPositionMetadata); + + TASmodAPIRegistry.PLAYBACK_METADATA.handleOnLoad(metadataList); + } + + @Override + protected void deserialiseFileCommandNames(List headerLines) { + /* + * Alpha has these file commands hardcoded + */ + TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.setEnabled("tasmod_label@v1", "tasmod_desyncMonitor@v1", "tasmod_options@v1"); + } } diff --git a/src/test/java/tasmod/playback/tasfile/builtin/AlphaFlavorTest.java b/src/test/java/tasmod/playback/tasfile/builtin/AlphaFlavorTest.java new file mode 100644 index 00000000..81342d67 --- /dev/null +++ b/src/test/java/tasmod/playback/tasfile/builtin/AlphaFlavorTest.java @@ -0,0 +1,216 @@ +package tasmod.playback.tasfile.builtin; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertIterableEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.minecrafttas.tasmod.playback.filecommands.PlaybackFileCommand.PlaybackFileCommandExtension; +import com.minecrafttas.tasmod.playback.filecommands.builtin.DesyncMonitorFileCommandExtension; +import com.minecrafttas.tasmod.playback.filecommands.builtin.LabelFileCommandExtension; +import com.minecrafttas.tasmod.playback.filecommands.builtin.OptionsFileCommandExtension; +import com.minecrafttas.tasmod.playback.metadata.PlaybackMetadata; +import com.minecrafttas.tasmod.playback.metadata.builtin.CreditsMetadataExtension; +import com.minecrafttas.tasmod.playback.metadata.builtin.CreditsMetadataExtension.CreditFields; +import com.minecrafttas.tasmod.playback.metadata.builtin.StartpositionMetadataExtension; +import com.minecrafttas.tasmod.playback.metadata.builtin.StartpositionMetadataExtension.StartPosition; +import com.minecrafttas.tasmod.playback.tasfile.flavor.builtin.AlphaFlavor; +import com.minecrafttas.tasmod.registries.TASmodAPIRegistry; + +public class AlphaFlavorTest extends AlphaFlavor { + + CreditsMetadataExtensionTest creditsMetadataExtension; + StartpositionMetadataExtensionTest startpositionMetadataExtension; + + private class CreditsMetadataExtensionTest extends CreditsMetadataExtension { + public String getTitle() { + return title; + } + + public String getAuthors() { + return authors; + } + + public String getPlayTime() { + return playtime; + } + + public int getRerecords() { + return rerecords; + } + } + + private class StartpositionMetadataExtensionTest extends StartpositionMetadataExtension { + public StartPosition getStartPosition() { + return startPosition; + } + } + + @AfterEach + void afterEach() { + TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.clear(); + TASmodAPIRegistry.PLAYBACK_METADATA.clear(); + TASmodAPIRegistry.SERIALISER_FLAVOR.clear(); + + this.currentTick = 0; + this.currentSubtick = 0; + this.previousTickContainer = null; + } + + @BeforeEach + void beforeEach() { + creditsMetadataExtension = new CreditsMetadataExtensionTest(); + startpositionMetadataExtension = new StartpositionMetadataExtensionTest(); + + PlaybackMetadata creditsMetadata = new PlaybackMetadata(creditsMetadataExtension); + creditsMetadata.setValue(CreditFields.Author, "Scribal"); + creditsMetadataExtension.onLoad(creditsMetadata); + + TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.register(new LabelFileCommandExtension(), new DesyncMonitorFileCommandExtension(), new OptionsFileCommandExtension()); + + TASmodAPIRegistry.PLAYBACK_METADATA.register(creditsMetadataExtension, startpositionMetadataExtension); + } + + @Test + void testSerialiseHeader() { + List actual = serialiseHeader(); + List expected = new ArrayList<>(); + + expected.add("################################################# TASFile ###################################################\n" + + "# Version:1 #\n" + + "# This file was generated using the Minecraft TASMod #\n" + + "# #\n" + + "# Any errors while reading this file will be printed out in the console and the chat #\n" + + "# #"); + expected.add("#------------------------------------------------ Header ---------------------------------------------------#\n" + + "#Author:" + "Scribal" + "\n" + + "# #\n" + + "#Title:" + "Insert TAS category here" + "\n" + + "# #\n" + + "#Playing Time:" + "00:00.0" + "\n" + + "# #\n" + + "#Rerecords:" + 0 + "\n" + + "# #\n" + + "#----------------------------------------------- Settings --------------------------------------------------#\n" + + "#StartPosition:" + "0.0,0.0,0.0,0.0,0.0" + "\n" + + "# #\n" + + "#StartSeed:" + 0); + expected.add("#############################################################################################################\n" + + "#Comments start with \"//\" at the start of the line, comments with # will not be saved"); + + assertIterableEquals(expected, actual); + } + + @Test + void testCheckFlavorName() { + List data = new ArrayList<>(); + data.add("################################################# TASFile ###################################################\n" + + "# Version:1 #\n" + + "# This file was generated using the Minecraft TASMod #\n" + + "# #\n" + + "# Any errors while reading this file will be printed out in the console and the chat #\n" + + "# #"); + data.add("#------------------------------------------------ Header ---------------------------------------------------#\n" + + "#Author:" + "Scribal" + "\n" + + "# #\n" + + "#Title:" + "Insert TAS category here" + "\n" + + "# #\n" + + "#Playing Time:" + "00:00.0" + "\n" + + "# #\n" + + "#Rerecords:" + 0 + "\n" + + "# #\n" + + "#----------------------------------------------- Settings --------------------------------------------------#\n" + + "#StartPosition:" + "0.0,0.0,0.0,0.0,0.0" + "\n" + + "# #\n" + + "#StartSeed:" + 0); + data.add("#############################################################################################################\n" + + "#Comments start with \"//\" at the start of the line, comments with # will not be saved"); + + assertTrue(checkFlavorName(data)); + } + + @Test + void testCheckFlavorNameFalse() { + List data = new ArrayList<>(); + data.add("################################################# TASFile ###################################################\n" + + "# Version:2 #\n" + + "# This file was generated using the Minecraft TASMod #\n" + + "# #\n" + + "# Any errors while reading this file will be printed out in the console and the chat #\n" + + "# #"); + data.add("#------------------------------------------------ Header ---------------------------------------------------#\n" + + "#Author:" + "Scribal" + "\n" + + "# #\n" + + "#Title:" + "Insert TAS category here" + "\n" + + "# #\n" + + "#Playing Time:" + "00:00.0" + "\n" + + "# #\n" + + "#Rerecords:" + 0 + "\n" + + "# #\n" + + "#----------------------------------------------- Settings --------------------------------------------------#\n" + + "#StartPosition:" + "0.0,0.0,0.0,0.0,0.0" + "\n" + + "# #\n" + + "#StartSeed:" + 0); + data.add("#############################################################################################################\n" + + "#Comments start with \"//\" at the start of the line, comments with # will not be saved"); + + assertFalse(checkFlavorName(data)); + } + + @Test + void testDeserialiseMetadata() { + List data = new ArrayList<>(); + data.add("################################################# TASFile ###################################################"); + data.add("# Version:1 #"); + data.add("# This file was generated using the Minecraft TASMod #"); + data.add("# #"); + data.add("# Any errors while reading this file will be printed out in the console and the chat #"); + data.add("# #"); + data.add("#------------------------------------------------ Header ---------------------------------------------------#"); + data.add("#Author:" + "Scribble"); + data.add("# #"); + data.add("#Title:" + "Beef"); + data.add("# #"); + data.add("#Playing Time:" + "00:01.0"); + data.add("# #"); + data.add("#Rerecords:" + 20); + data.add("# #"); + data.add("#----------------------------------------------- Settings --------------------------------------------------#"); + data.add("#StartPosition:" + "1.0,2.0,3.0,4.0,5.0"); + data.add("# #"); + data.add("#StartSeed:" + 0); + data.add("#############################################################################################################"); + data.add("#Comments start with \"//\" at the start of the line, comments with # will not be saved"); + + deserialiseMetadata(data); + + assertEquals("Scribble", creditsMetadataExtension.getAuthors()); + assertEquals("Beef", creditsMetadataExtension.getTitle()); + assertEquals("00:01.0", creditsMetadataExtension.getPlayTime()); + assertEquals(20, creditsMetadataExtension.getRerecords()); + + StartPosition pos = startpositionMetadataExtension.getStartPosition(); + assertEquals(1.0D, pos.x); + assertEquals(2.0D, pos.y); + assertEquals(3.0D, pos.z); + assertEquals(4.0F, pos.pitch); + assertEquals(5.0F, pos.yaw); + } + + @Test + void testDeserialiseFileCommandNames() { + deserialiseFileCommandNames(new ArrayList<>()); + + List fcList = TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.getEnabled(); + assertTrue(fcList.get(0) instanceof LabelFileCommandExtension); + assertTrue(fcList.get(1) instanceof DesyncMonitorFileCommandExtension); + assertTrue(fcList.get(2) instanceof OptionsFileCommandExtension); + } +} From a714a6a28905e6ff62d077bc36d034611fad1e96 Mon Sep 17 00:00:00 2001 From: Scribble Date: Tue, 17 Dec 2024 23:06:03 +0100 Subject: [PATCH 04/16] [Flavor] Start on documentation --- .../tasfile/flavor/SerialiserFlavorBase.java | 59 ++++++++++++++++--- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java index 82284888..5edabd61 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java @@ -19,6 +19,7 @@ import com.minecrafttas.tasmod.playback.filecommands.PlaybackFileCommand.PlaybackFileCommandContainer; import com.minecrafttas.tasmod.playback.filecommands.PlaybackFileCommand.PlaybackFileCommandExtension; import com.minecrafttas.tasmod.playback.metadata.PlaybackMetadata; +import com.minecrafttas.tasmod.playback.tasfile.PlaybackSerialiser; import com.minecrafttas.tasmod.playback.tasfile.exception.PlaybackLoadException; import com.minecrafttas.tasmod.registries.TASmodAPIRegistry; import com.minecrafttas.tasmod.virtual.Subtickable; @@ -33,32 +34,54 @@ *

All serialisation and deserialisation is broken apart into functions whenever possible,
* with the intention of allowing small changes to the existing syntax. * - *

Adding functionality to playback should be made via {@link PlaybackFileCommand PlaybackFileCommands} instead of creating a new syntax
- * and adding new information to the header should be made via {@link PlaybackMetadata} + *

Adding functionality to playback should be made via {@link PlaybackFileCommand PlaybackFileCommands}
+ * instead of creating a new syntax and adding new information to the header should be made via {@link PlaybackMetadata} * *

Sections

- *

The TASfile has 2 main sections: + *

The TASfile has 2 main sections, which are called seperately by the {@link PlaybackSerialiser}: * *

    *
  1. - * {@link #serialiseHeader() Header}: Contains metadata about this TAS, like credits and start position,
    + * Header
    + * Contains metadata about this TAS, like credits and start position,
    * but also a list of enabled extensions and the name of the flavor that was used to encode the file. *
  2. *
  3. - * {@link #serialise(BigArrayList, long) Content}: Contains the actual inputs per tick, inputs in a subtick (a.k.a in a frame), comments and other extensions. + * Content
    + * Contains the actual inputs per tick, inputs in a subtick (a.k.a in a frame), comments and other extensions. *
  4. *
* + * Both sections have serialise and deserialise methods: + * + *
    + *
  • Serialisation + *
      + *
    • {@link #serialiseHeader()}
    • + *
    • {@link #serialise(BigArrayList, long)}
    • + *
    + *
  • + *
  • Deserialisation + *
      + *
    • {@link #deserialiseHeader(List)}
    • + *
    • {@link #deserialise(BigArrayList, long)}
    • + *
    + *
  • + *
+ * * Clicking on either of these will lead you to a breakdown in their respective javadocs * * @author Scribble */ public abstract class SerialiserFlavorBase implements Registerable { + /** + * The current line that is being serialised or deserialised. Used for debugging + */ protected long currentLine = 1; /** - * The current tick that is being serialised or deserialised + * The current tick that is being serialised or deserialised. Used for debugging */ protected long currentTick = 0; @@ -108,7 +131,18 @@ protected String headerEnd() { } /** - * + *

Serialises the flavor of this file, the enabled file commands and other metadata. + *

{@link #serialiseFlavorName(List)} + *

+	 * serialiseHeader
+	 *	├── {@link #headerStart()}	// The start of the header
+	 *	├── {@link #serialiseFlavorName(List)}	// The name of the flavor
+	 *	├── {@link #serialiseFileCommandNames(List)}	// The names of the enabled file commands
+	 *	├── {@link #serialiseMetadata(List)}	// The metadata of this movie
+	 *	│   ├── {@link #serialiseMetadataName(List, String)}	// The metadata extension name
+	 *	│   └── {@link #serialiseMetadataValues(List, LinkedHashMap)}	// All values in the extension
+	 *	└── {@link #headerEnd()}	// The end of the header
+	 * 
* @return List of lines containing the header */ public List serialiseHeader() { @@ -121,6 +155,13 @@ public List serialiseHeader() { return out; } + /** + *

How the flavor name is serialised. + *

You normally don't have to edit this,
+ * as the flavor name is taken from the extension name. + * + * @param out The serialised lines, passed by reference + */ protected void serialiseFlavorName(List out) { out.add("Flavor: " + getExtensionName()); } @@ -143,7 +184,7 @@ protected void serialiseMetadata(List out) { for (PlaybackMetadata metadata : metadataList) { serialiseMetadataName(out, metadata.getExtensionName()); - serialiseMetadataValue(out, metadata.getData()); + serialiseMetadataValues(out, metadata.getData()); out.add(""); } } @@ -152,7 +193,7 @@ protected void serialiseMetadataName(List out, String name) { out.add(createCenteredHeading(name, '-', 50)); } - protected void serialiseMetadataValue(List out, LinkedHashMap data) { + protected void serialiseMetadataValues(List out, LinkedHashMap data) { data.forEach((key, value) -> { out.add(String.format("%s:%s", key, value)); }); From e776d366bc049657646b0e4aac4f739917aa3e2c Mon Sep 17 00:00:00 2001 From: Scribble Date: Wed, 18 Dec 2024 21:39:10 +0100 Subject: [PATCH 05/16] [Flavor] Fix tests failing --- .../playback/filecommands/PlaybackFileCommand.java | 13 +++++++++---- .../builtin/DesyncMonitorFileCommandExtension.java | 13 ++++++++++++- .../builtin/LabelFileCommandExtension.java | 13 ++++++++++++- .../builtin/OptionsFileCommandExtension.java | 13 ++++++++++++- .../playback/tasfile/builtin/AlphaFlavorTest.java | 6 +++++- 5 files changed, 50 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/PlaybackFileCommand.java b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/PlaybackFileCommand.java index 21a66872..a34beee4 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/PlaybackFileCommand.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/PlaybackFileCommand.java @@ -59,7 +59,7 @@ public static abstract class PlaybackFileCommandExtension implements Registerabl protected final Path tempDir; public PlaybackFileCommandExtension() { - this(null); + this((Path) null); } /** @@ -69,13 +69,18 @@ public PlaybackFileCommandExtension() { * @param tempFolderName The name of the temp folder */ public PlaybackFileCommandExtension(String tempFolderName) { - if (tempFolderName == null) { + this(TASmodClient.tasfiledirectory.resolve("temp").resolve(tempFolderName)); + } + + public PlaybackFileCommandExtension(Path tempDirectory) { + if (tempDirectory == null) { tempDir = null; return; } - this.tempDir = TASmodClient.tasfiledirectory.resolve("temp").resolve(tempFolderName); + + tempDir = tempDirectory; try { - AbstractDataFile.createDirectory(tempDir); + AbstractDataFile.createDirectory(tempDirectory); } catch (IOException e) { e.printStackTrace(); } diff --git a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/DesyncMonitorFileCommandExtension.java b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/DesyncMonitorFileCommandExtension.java index dd3cf042..5d50a604 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/DesyncMonitorFileCommandExtension.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/DesyncMonitorFileCommandExtension.java @@ -2,6 +2,7 @@ import java.io.IOException; import java.io.Serializable; +import java.nio.file.Path; import java.text.NumberFormat; import java.text.ParseException; import java.util.List; @@ -40,12 +41,22 @@ public class DesyncMonitorFileCommandExtension extends PlaybackFileCommandExtens private MonitorContainer currentValues; public DesyncMonitorFileCommandExtension() { - super("monitoring"); + this("monitoring"); + } + + public DesyncMonitorFileCommandExtension(String tempDirName) { + super(tempDirName); this.monitorContainer = new BigArrayList(tempDir.toString()); // Is enabled by default enabled = true; } + public DesyncMonitorFileCommandExtension(Path tempDir) { + super(tempDir); + this.monitorContainer = new BigArrayList<>(tempDir.toString()); + enabled = true; + } + @Override public String getExtensionName() { return "tasmod_desyncMonitor@v1"; diff --git a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/LabelFileCommandExtension.java b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/LabelFileCommandExtension.java index d7503ea8..f0176c1e 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/LabelFileCommandExtension.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/LabelFileCommandExtension.java @@ -1,6 +1,7 @@ package com.minecrafttas.tasmod.playback.filecommands.builtin; import java.io.IOException; +import java.nio.file.Path; import com.dselent.bigarraylist.BigArrayList; import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TickContainer; @@ -16,7 +17,17 @@ public class LabelFileCommandExtension extends PlaybackFileCommandExtension { BigArrayList label = new BigArrayList<>(); public LabelFileCommandExtension() { - super("label"); + this("label"); + } + + public LabelFileCommandExtension(String tempDirName) { + super(tempDirName); + this.label = new BigArrayList<>(tempDir.toString()); + enabled = true; + } + + public LabelFileCommandExtension(Path tempDir) { + super(tempDir); this.label = new BigArrayList<>(tempDir.toString()); enabled = true; } diff --git a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/OptionsFileCommandExtension.java b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/OptionsFileCommandExtension.java index 0e1193b4..b071afe2 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/OptionsFileCommandExtension.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/OptionsFileCommandExtension.java @@ -1,6 +1,7 @@ package com.minecrafttas.tasmod.playback.filecommands.builtin; import java.io.IOException; +import java.nio.file.Path; import com.dselent.bigarraylist.BigArrayList; import com.minecrafttas.tasmod.TASmod; @@ -18,11 +19,21 @@ public class OptionsFileCommandExtension extends PlaybackFileCommandExtension { BigArrayList hud; public OptionsFileCommandExtension() { - super("hud"); + this("hud"); + } + + public OptionsFileCommandExtension(String tempDirName) { + super(tempDirName); hud = new BigArrayList<>(tempDir.toString()); enabled = true; } + public OptionsFileCommandExtension(Path tempDir) { + super(tempDir); + this.hud = new BigArrayList<>(tempDir.toString()); + enabled = true; + } + @Override public String getExtensionName() { return "tasmod_options@v1"; diff --git a/src/test/java/tasmod/playback/tasfile/builtin/AlphaFlavorTest.java b/src/test/java/tasmod/playback/tasfile/builtin/AlphaFlavorTest.java index 81342d67..a8464575 100644 --- a/src/test/java/tasmod/playback/tasfile/builtin/AlphaFlavorTest.java +++ b/src/test/java/tasmod/playback/tasfile/builtin/AlphaFlavorTest.java @@ -5,6 +5,8 @@ import static org.junit.jupiter.api.Assertions.assertIterableEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; @@ -73,7 +75,9 @@ void beforeEach() { creditsMetadata.setValue(CreditFields.Author, "Scribal"); creditsMetadataExtension.onLoad(creditsMetadata); - TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.register(new LabelFileCommandExtension(), new DesyncMonitorFileCommandExtension(), new OptionsFileCommandExtension()); + Path temp = Paths.get("src/test/resources/temp"); + + TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.register(new LabelFileCommandExtension(temp), new DesyncMonitorFileCommandExtension(temp), new OptionsFileCommandExtension(temp)); TASmodAPIRegistry.PLAYBACK_METADATA.register(creditsMetadataExtension, startpositionMetadataExtension); } From d951e651532a8af1a1a88fbb9b0de55de748f164 Mon Sep 17 00:00:00 2001 From: Scribble Date: Sun, 22 Dec 2024 20:11:45 +0100 Subject: [PATCH 06/16] [Flavor] Add examples to documentation --- .../tasfile/flavor/SerialiserFlavorBase.java | 136 ++++++++++++++++-- 1 file changed, 124 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java index 5edabd61..5d50abb8 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java @@ -29,7 +29,7 @@ import com.minecrafttas.tasmod.virtual.VirtualMouse; /** - *

The base class of a flavor. + *

The base class of a flavor * *

All serialisation and deserialisation is broken apart into functions whenever possible,
* with the intention of allowing small changes to the existing syntax. @@ -116,6 +116,10 @@ public abstract class SerialiserFlavorBase implements Registerable { */ /** + *

Example

+ *
+	 * ##################### TASfile ####################
+	 * 
* @return The very top of the header */ protected String headerStart() { @@ -123,7 +127,11 @@ protected String headerStart() { } /** - * The end of the header, used for detecting when the header stops + *

The end of the header, used for detecting when the header stops + *

Example

+ *
+	 * ##################################################
+	 * 
* @return The end of the header */ protected String headerEnd() { @@ -131,17 +139,38 @@ protected String headerEnd() { } /** - *

Serialises the flavor of this file, the enabled file commands and other metadata. - *

{@link #serialiseFlavorName(List)} + *

Serialises the flavor of this file, the enabled file commands and other metadata + *

Tree

*
 	 * serialiseHeader
-	 *	├── {@link #headerStart()}	// The start of the header
-	 *	├── {@link #serialiseFlavorName(List)}	// The name of the flavor
-	 *	├── {@link #serialiseFileCommandNames(List)}	// The names of the enabled file commands
-	 *	├── {@link #serialiseMetadata(List)}	// The metadata of this movie
-	 *	│   ├── {@link #serialiseMetadataName(List, String)}	// The metadata extension name
-	 *	│   └── {@link #serialiseMetadataValues(List, LinkedHashMap)}	// All values in the extension
-	 *	└── {@link #headerEnd()}	// The end of the header
+	 *	├── {@link #headerStart()}
+	 *	├── {@link #serialiseFlavorName(List)}
+	 *	├── {@link #serialiseFileCommandNames(List)}
+	 *	├── {@link #serialiseMetadata(List)}
+	 *	│   ├── {@link #serialiseMetadataName(List, String)}
+	 *	│   └── {@link #serialiseMetadataValues(List, LinkedHashMap)}
+	 *	└── {@link #headerEnd()}
+	 * 
+ *

Example

+ *
+	 * ##################### TASfile ####################					// {@link #headerStart()}
+	 * Flavor: beta1 										// {@link #serialiseFlavorName(List)}
+	 * FileCommand-Extensions: tasmod_desyncMonitor@v1, tasmod_options@v1, tasmod_label@v1	// {@link #serialiseFileCommandNames(List)}
+	 * 
+	 * --------------------- Credits -------------------- 					// {@link #serialiseMetadataName(List, String)}
+	 * Title:Insert TAS category here 							// {@link #serialiseMetadataValues(List, LinkedHashMap)}
+	 * Author:Insert author here
+	 * Playing Time:00:00.0
+	 * Rerecords:0
+	 * 
+	 * ----------------- Start Position -----------------
+	 * x:-32.577311363268976
+	 * y:56.0
+	 * z:-4.457057187505265
+	 * pitch:29.25007
+	 * yaw:-88.80094
+	 * 
+	 * ##################################################					// {@link #headerEnd()}
 	 * 
* @return List of lines containing the header */ @@ -156,9 +185,13 @@ public List serialiseHeader() { } /** - *

How the flavor name is serialised. + *

How the flavor name is serialised *

You normally don't have to edit this,
* as the flavor name is taken from the extension name. + *

Example

+ *
+	 * Flavor: beta1
+	 * 
* * @param out The serialised lines, passed by reference */ @@ -166,6 +199,14 @@ protected void serialiseFlavorName(List out) { out.add("Flavor: " + getExtensionName()); } + /** + *

Adds the file commands that are enabled to the lines + *

Example

+ *
+	 * FileCommand-Extensions: tasmod_label@v1, tasmod_desyncMonitor@v1
+	 * 
+ * @param out The serialised lines, passed by reference + */ protected void serialiseFileCommandNames(List out) { List stringlist = new ArrayList<>(); List extensionList = TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.getEnabled(); @@ -176,6 +217,25 @@ protected void serialiseFileCommandNames(List out) { out.add(""); } + /** + *

Serialises the metadata to the header of the TASfile + *

Example

+ *
+	 * --------------------- Credits --------------------
+	 * Title:Insert TAS category here
+	 * Author:Insert author here
+	 * Playing Time:00:00.0
+	 * Rerecords:0
+	 * 
+	 * ----------------- Start Position -----------------
+	 * x:-32.577311363268976
+	 * y:56.0
+	 * z:-4.457057187505265
+	 * pitch:29.25007
+	 * yaw:-88.80094
+	 * 
+ * @param out + */ protected void serialiseMetadata(List out) { if (!processExtensions) return; @@ -189,16 +249,64 @@ protected void serialiseMetadata(List out) { } } + /** + *

Serialises only the name of the metadata section + *

Example

+ *
+	 * --------------------- Credits --------------------
+	 * 
+ * @param out The lines passed in by reference + * @param name The name to process + */ protected void serialiseMetadataName(List out, String name) { out.add(createCenteredHeading(name, '-', 50)); } + /** + *

Serialises only the values of the metadata section + *

Example

+ *
+	 * Title:Insert TAS category here
+	 * Author:Insert author here
+	 * Playing Time:00:00.0
+	 * Rerecords:0
+	 * 
+ * @param out + * @param data + */ protected void serialiseMetadataValues(List out, LinkedHashMap data) { data.forEach((key, value) -> { out.add(String.format("%s:%s", key, value)); }); } + /* + ___ _____ _ _ ____ ____ _ _ ____ + / __)( _ )( \( )(_ _)( ___)( \( )(_ _) + ( (__ )(_)( ) ( )( )__) ) ( )( + \___)(_____)(_)\_) (__) (____)(_)\_) (__) + + */ + + /** + *

Serialises a list of inputs into a list of strings + *

Tree

+ *
+	 * serialise
+	 * └── {@link #serialiseContainer(BigArrayList, TickContainer)}
+	 *     ├── {@link #serialiseKeyboard(VirtualKeyboard)}
+	 *     ├── {@link #serialiseMouse(VirtualMouse)}
+	 *     ├── {@link #serialiseCameraAngle(VirtualCameraAngle)}
+	 *     ├── {@link #serialiseInlineComments(List, List)}
+	 *     │   └── {@link #serialiseFileCommandsInLine(List)}
+	 *     ├── {@link #serialiseEndlineComments(List, List)}	// Same as serialiseInlineComments
+	 *     │   └── {@link #serialiseFileCommandsEndLine(List)}	// Unused
+	 *     └── {@link #mergeInputs(BigArrayList, List, List, List, List)}
+	 * 
+ * @param inputs The inputs to serialise + * @param toTick The tick where to stop, used for partial serialisation by savestates. -1 to serialise all + * @return The list of lines + */ public BigArrayList serialise(BigArrayList inputs, long toTick) { BigArrayList out = new BigArrayList<>(); @@ -253,6 +361,10 @@ protected String serialiseFileCommandsInLine(List fileComma return String.join(" ", serialisedCommands); } + protected String serialiseFileCommandsEndLine(List fileCommands) { + return serialiseFileCommandsInLine(fileCommands); + } + protected List serialiseKeyboard(VirtualKeyboard keyboard) { List out = new ArrayList<>(); From c6232a1d0122cb8938b64545aa03132fbf3141b8 Mon Sep 17 00:00:00 2001 From: Scribble Date: Tue, 24 Dec 2024 20:59:40 +0100 Subject: [PATCH 07/16] [Flavor] Add keyboard and mouse serialisation to AlphaFlavor - [PlaybackSersialiser] Switched from using toString() in keyboard and mouse to the same implementation. Reason is that toString in VirtualKeyboard might change in the fututure while the SerialiserFlavorBase is supposed to be backwards compatible --- .../tasfile/flavor/SerialiserFlavorBase.java | 17 ++- .../tasfile/flavor/builtin/AlphaFlavor.java | 120 ++++++++++++++++++ 2 files changed, 135 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java index 5d50abb8..703ed1e3 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java @@ -10,6 +10,9 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; import com.dselent.bigarraylist.BigArrayList; import com.minecrafttas.mctcommon.registry.Registerable; @@ -372,7 +375,7 @@ protected List serialiseKeyboard(VirtualKeyboard keyboard) { pruneListEndEmptySubtickable(subticks); for (VirtualKeyboard subtick : subticks) { - out.add(subtick.toString2()); + out.add(String.format("%s;%s", String.join(",", subtick.getCurrentPresses()), charListToString(subtick.getCharList()))); } return out; } @@ -384,7 +387,7 @@ protected List serialiseMouse(VirtualMouse mouse) { pruneListEndEmptySubtickable(subticks); for (VirtualMouse subtick : subticks) { - out.add(subtick.toString2()); + out.add(String.format("%s;%s,%s,%s", String.join(",", subtick.getCurrentPresses()), subtick.getScrollWheel(), subtick.getCursorX(), subtick.getCursorY())); } return out; } @@ -1303,4 +1306,14 @@ public boolean equals(Object obj) { public void setProcessExtensions(boolean processExtensions) { this.processExtensions = processExtensions; } + + protected String charListToString(List charList) { + String charString = ""; + if (!charList.isEmpty()) { + charString = charList.stream().map(Object::toString).collect(Collectors.joining()); + charString = StringUtils.replace(charString, "\r", "\\n"); + charString = StringUtils.replace(charString, "\n", "\\n"); + } + return charString; + } } diff --git a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java index 6e7044e7..cc5aff15 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java @@ -18,6 +18,10 @@ import com.minecrafttas.tasmod.playback.metadata.PlaybackMetadata; import com.minecrafttas.tasmod.playback.tasfile.flavor.SerialiserFlavorBase; import com.minecrafttas.tasmod.registries.TASmodAPIRegistry; +import com.minecrafttas.tasmod.virtual.VirtualCameraAngle; +import com.minecrafttas.tasmod.virtual.VirtualKey; +import com.minecrafttas.tasmod.virtual.VirtualKeyboard; +import com.minecrafttas.tasmod.virtual.VirtualMouse; public class AlphaFlavor extends SerialiserFlavorBase { @@ -106,6 +110,122 @@ public boolean checkFlavorName(List headerLines) { return false; } + @Override + protected List serialiseKeyboard(VirtualKeyboard keyboard) { + /* + * Old code from when I did not know String.join exists, + * kept relatively unaltered because I want to. + */ + List out = new ArrayList<>(); + + List stringy = keyboard.getCurrentPresses(); + String keyString = ""; + if (!stringy.isEmpty()) { + String seperator = ","; + for (int i = 0; i < stringy.size(); i++) { + if (i == stringy.size() - 1) { + seperator = ""; + } + keyString = keyString.concat(stringy.get(i) + seperator); + } + } + List charList = keyboard.getCharList(); + String charString = ""; + if (!charList.isEmpty()) { + for (int i = 0; i < charList.size(); i++) { + charString = charString.concat(Character.toString(charList.get(i))); + } + charString = charString.replace("\r", "\\n"); + charString = charString.replace("\n", "\\n"); + } + + out.add("Keyboard:" + keyString + ";" + charString); // Keyboard didn't support subticks, only the current key is processed + + return out; + } + + @Override + protected List serialiseMouse(VirtualMouse mouse) { + /* + * Old code from when I did not know String.join exists, + * kept relatively unaltered because I want to. + */ + List out = new ArrayList<>(); + List stringy = mouse.getCurrentPresses(); + String keyString = ""; + if (!stringy.isEmpty()) { + String seperator = ","; + for (int i = 0; i < stringy.size(); i++) { + if (i == stringy.size() - 1) { + seperator = ""; + } + keyString = keyString.concat(stringy.get(i) + seperator); + } + } + + List path = new ArrayList<>(mouse.getSubticks()); // I previously called subticks "paths" as it was mainly used for the mouse... +// pruneListEndEmptySubtickable(path); + + /* + * The mouse supported subticks, + * but it was handled differently in alpha... + * The subticks where added in square brackets, seperated by a "->" + * Not the best solution in hindsight, + * but that was apparently the first thing that came to my mind back then... + */ + String pathString = ""; + if (!path.isEmpty()) { + String seperator = "->"; + for (int i = 0; i < path.size(); i++) { + if (i == path.size() - 1) { + seperator = ""; + } + + VirtualMouse singlePath = path.get(i); + + pathString = pathString.concat("[" + serialisePath(singlePath) + "]" + seperator); + } + } + out.add("Mouse:" + keyString + ";" + pathString); + return out; + } + + protected String serialisePath(VirtualMouse path) { + String keyString = ""; + List strings = new ArrayList(); + + path.getPressedKeys().forEach((virtualkeys) -> { + strings.add(VirtualKey.getName(virtualkeys)); + }); + if (!strings.isEmpty()) { + String seperator = ","; + for (int i = 0; i < strings.size(); i++) { + if (i == strings.size() - 1) { + seperator = ""; + } + keyString = keyString.concat(strings.get(i) + seperator); + } + } + if (keyString.isEmpty()) { + return "MOUSEMOVED," + path.getScrollWheel() + "," + path.getCursorX() + "," + path.getCursorY(); + } else { + return keyString + "," + path.getScrollWheel() + "," + path.getCursorX() + "," + path.getCursorY(); + } + } + + @Override + protected List serialiseCameraAngle(VirtualCameraAngle subticks) { + List out = new ArrayList<>(); + + /* + * The camera was called "subticks" in previous iterations of this code. + * To honor this fact, it is also called subticks here, even though + * actual subticks were not supported + */ + out.add("Camera:" + subticks.getPitch() + ";" + subticks.getYaw()); + return out; + } + @Override protected void deserialiseMetadata(List headerLines) { String author = "Insert author here"; From d3eec6bfb7d8234bcd6ac80f410137d6e163569c Mon Sep 17 00:00:00 2001 From: Scribble Date: Thu, 26 Dec 2024 13:59:19 +0100 Subject: [PATCH 08/16] [PlaybackSerialiser] Fix endline comments not saving properly --- .../playback/PlaybackControllerClient.java | 13 ++- .../tasfile/flavor/SerialiserFlavorBase.java | 92 ++++++++++++++----- .../tasfile/SerialiserFlavorBaseTest.java | 5 +- 3 files changed, 83 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java b/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java index 08cca459..da44dcb6 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java @@ -580,7 +580,7 @@ public void setPlayUntil(int until) { // ============================================================== /** - * Storage class which stores the keyboard, mouse and subticks of a given tick. + * Storage class which stores the keyboard, mouse, subticks and comments of a given tick. * * @author Scribble * @@ -628,7 +628,13 @@ public VirtualCameraAngle getCameraAngle() { return cameraAngle; } + /** + * @return The comment container of this controller. If {@link #comments} is null, returns an empty container. + */ public CommentContainer getComments() { + if (comments == null) { + return new CommentContainer(); + } return comments; } @@ -647,6 +653,11 @@ public boolean equals(Object other) { } } + /** + * Storage class for storing {@link CommentContainer#inlineComments inline} and {@link CommentContainer#endlineComments endline} comments + * + * @author Scribble + */ public static class CommentContainer implements Serializable { /** diff --git a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java index 703ed1e3..3f9bab90 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java @@ -325,6 +325,12 @@ public BigArrayList serialise(BigArrayList inputs, long t return out; } + /** + * Main serialising method of a single {@link TickContainer} + * + * @param out The list of serialised lines, passed in by reference + * @param container The {@link TickContainer} to serialise + */ protected void serialiseContainer(BigArrayList out, TickContainer container) { currentLine = out.size() - 1; List serialisedKeyboard = serialiseKeyboard(container.getKeyboard()); @@ -336,15 +342,10 @@ protected void serialiseContainer(BigArrayList out, TickContainer contai PlaybackFileCommandContainer fileCommandsEndline = TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.handleOnSerialiseEndline(currentTick, container); CommentContainer comments = container.getComments(); - if (comments == null) { - comments = new CommentContainer(new ArrayList<>(), new ArrayList<>()); - } - List serialisedInlineCommments = serialiseInlineComments(comments.getInlineComments(), fileCommandsInline.valuesBySubtick()); + List serialisedInlineComments = serialiseInlineComments(comments.getInlineComments(), fileCommandsInline.valuesBySubtick()); List serialisedEndlineComments = serialiseEndlineComments(comments.getEndlineComments(), fileCommandsEndline.valuesBySubtick()); - addAll(out, serialisedInlineCommments); - - mergeInputs(out, serialisedKeyboard, serialisedMouse, serialisedCameraAngle, serialisedEndlineComments); + mergeInputs(out, serialisedKeyboard, serialisedMouse, serialisedCameraAngle, serialisedInlineComments, serialisedEndlineComments); } protected String serialiseFileCommand(PlaybackFileCommand fileCommand) { @@ -372,7 +373,7 @@ protected List serialiseKeyboard(VirtualKeyboard keyboard) { List out = new ArrayList<>(); List subticks = new ArrayList<>(keyboard.getAll()); - pruneListEndEmptySubtickable(subticks); +// pruneListEndEmptySubtickable(subticks); for (VirtualKeyboard subtick : subticks) { out.add(String.format("%s;%s", String.join(",", subtick.getCurrentPresses()), charListToString(subtick.getCharList()))); @@ -384,7 +385,7 @@ protected List serialiseMouse(VirtualMouse mouse) { List out = new ArrayList<>(); List subticks = new ArrayList<>(mouse.getAll()); - pruneListEndEmptySubtickable(subticks); +// pruneListEndEmptySubtickable(subticks); for (VirtualMouse subtick : subticks) { out.add(String.format("%s;%s,%s,%s", String.join(",", subtick.getCurrentPresses()), subtick.getScrollWheel(), subtick.getCursorX(), subtick.getCursorY())); @@ -461,31 +462,61 @@ protected List serialiseEndlineComments(List endlineComments, Li return serialiseInlineComments(endlineComments, fileCommandsEndline); } - protected void mergeInputs(BigArrayList out, List serialisedKeyboard, List serialisedMouse, List serialisedCameraAngle, List serialisedEndlineComments) { + protected void mergeInputs(BigArrayList out, List serialisedKeyboard, List serialisedMouse, List serialisedCameraAngle, List serialisedInlineComments, List serialisedEndlineComments) { + + /* + * Firstly add inline comments, as they appear before the inputs in the container: + * Example: + * // This is an inline comment + * 1|||0;0 + */ + addAll(out, serialisedInlineComments); + + /* + * Copy inputs with ticks and subticks into a queue, + * so they can be serialised even if the length is different + */ Queue keyboardQueue = new LinkedBlockingQueue<>(serialisedKeyboard); Queue mouseQueue = new LinkedBlockingQueue<>(serialisedMouse); Queue cameraAngleQueue = new LinkedBlockingQueue<>(serialisedCameraAngle); - Queue endlineQueue = new LinkedBlockingQueue<>(serialisedEndlineComments); + Queue endlineCommentQueue = new LinkedBlockingQueue<>(serialisedEndlineComments); String kb = getOrEmpty(keyboardQueue.poll()); String ms = getOrEmpty(mouseQueue.poll()); String ca = getOrEmpty(cameraAngleQueue.poll()); - String elc = getOrEmpty(endlineQueue.poll()); + String elc = getOrEmpty(endlineCommentQueue.poll()); if (!elc.isEmpty()) { elc = "\t\t" + elc; } + // Add tick line, not indented out.add(String.format("%s|%s|%s|%s%s", currentTick, kb, ms, ca, elc)); + // Add subtick lines, not indented currentSubtick = 0; while (!keyboardQueue.isEmpty() || !mouseQueue.isEmpty() || !cameraAngleQueue.isEmpty()) { currentSubtick++; kb = getOrEmpty(keyboardQueue.poll()); ms = getOrEmpty(mouseQueue.poll()); ca = getOrEmpty(cameraAngleQueue.poll()); + elc = getOrEmpty(endlineCommentQueue.poll()); + if (!elc.isEmpty()) { + elc = "\t\t" + elc; + } + + out.add(String.format("\t%s|%s|%s|%s%s", currentSubtick, kb, ms, ca, elc)); + } - out.add(String.format("\t%s|%s|%s|%s", currentSubtick, kb, ms, ca)); + /* + * Add the rest of the endline comments. + * Normally there shouldn't be more comments + * than subticks, but maybe some file command extension + * demands it + */ + while (!endlineCommentQueue.isEmpty()) { + elc = getOrEmpty(endlineCommentQueue.poll()); + out.add(String.format("\t|||;\t\t%s", elc)); } currentSubtick = 0; } @@ -642,7 +673,7 @@ protected void deserialiseMetadata(List headerLines) { * * @param lines The serialised lines of the TASfile * @param startPos The position when the header ends and the inputs start - * @return A list of {@link TickContainer} + * @return A list of {@link TickContainer TickContainers} */ public BigArrayList deserialise(BigArrayList lines, long startPos) { BigArrayList out = new BigArrayList<>(); @@ -821,8 +852,10 @@ protected void deserialiseContainer(BigArrayList out, List inlineComments = new ArrayList<>(); List tickLines = new ArrayList<>(); + splitContainer(containerLines, inlineComments, tickLines); + List> inlineFileCommands = new ArrayList<>(); - splitContainer(containerLines, inlineComments, tickLines, inlineFileCommands); + deserialiseMultipleInlineComments(inlineComments, inlineFileCommands); List keyboardStrings = new ArrayList<>(); List mouseStrings = new ArrayList<>(); @@ -830,7 +863,7 @@ protected void deserialiseContainer(BigArrayList out, List endlineComments = new ArrayList<>(); List> endlineFileCommands = new ArrayList<>(); - splitInputs(containerLines, keyboardStrings, mouseStrings, cameraAngleStrings, endlineComments, endlineFileCommands); + splitInputs(tickLines, keyboardStrings, mouseStrings, cameraAngleStrings, endlineComments, endlineFileCommands); pruneListEndNull(endlineComments); @@ -856,21 +889,30 @@ protected void deserialiseContainer(BigArrayList out, List lines, List comments, List tick, List> inlineFileCommands) { + protected void splitContainer(List lines, List inlineComments, List ticks) { for (String line : lines) { if (contains(singleComment(), line)) { - List deserialisedFileCommand = new ArrayList<>(); - comments.add(deserialiseInlineComment(line, deserialisedFileCommand)); - if (deserialisedFileCommand.isEmpty()) { - deserialisedFileCommand = null; - } - inlineFileCommands.add(deserialisedFileCommand); + inlineComments.add(line); } else { - tick.add(line); + ticks.add(line); } } } + protected void deserialiseMultipleInlineComments(List inlineComments, List> inlineFileCommands) { + for (int i = 0; i < inlineComments.size(); i++) { + List deserialisedFileCommand = new ArrayList<>(); + String comment = inlineComments.get(i); + + inlineComments.set(i, deserialiseInlineComment(comment, deserialisedFileCommand)); + + if (deserialisedFileCommand.isEmpty()) { + deserialisedFileCommand = null; + } + inlineFileCommands.add(deserialisedFileCommand); + } + } + protected String deserialiseInlineComment(String comment, List deserialisedFileCommands) { comment = deserialiseFileCommands(comment, deserialisedFileCommands); comment = extract("^// ?(.+)", comment, 1); @@ -890,6 +932,8 @@ protected String deserialiseEndlineComment(String comment, List deserialisedFileCommands) { Matcher matcher = extract("\\$(.+?)\\((.*?)\\);", comment); + + // Iterate through all file commands and add each to the list while (matcher.find()) { String name = matcher.group(1); String[] args = matcher.group(2).split(", ?"); diff --git a/src/test/java/tasmod/playback/tasfile/SerialiserFlavorBaseTest.java b/src/test/java/tasmod/playback/tasfile/SerialiserFlavorBaseTest.java index c5e2c610..6e060930 100644 --- a/src/test/java/tasmod/playback/tasfile/SerialiserFlavorBaseTest.java +++ b/src/test/java/tasmod/playback/tasfile/SerialiserFlavorBaseTest.java @@ -773,9 +773,10 @@ void testSplitContainer() { List actualComments = new ArrayList<>(); List actualTick = new ArrayList<>(); - List> actualInlineFileCommands = new ArrayList<>(); + splitContainer(lines, actualComments, actualTick); - splitContainer(lines, actualComments, actualTick, actualInlineFileCommands); + List> actualInlineFileCommands = new ArrayList<>(); + deserialiseMultipleInlineComments(actualComments, actualInlineFileCommands); List expectedComments = new ArrayList<>(); List expectedTicks = new ArrayList<>(); From 609529cffe201ed0ba409e536d45beeb171db86a Mon Sep 17 00:00:00 2001 From: Scribble Date: Wed, 19 Mar 2025 10:19:46 +0100 Subject: [PATCH 09/16] [Flavor] Add more documentation - Extracted serialiseKeyboardSubtick from serialiseKeyboard - Extracted serialiseMouseSubtick from serialiseMouse - Extracted serialiseCameraAngleSubtick from serialiseCameraAngle - Moved serialiseFileCommands methods further down --- .../tasfile/flavor/SerialiserFlavorBase.java | 176 ++++++++++++++---- 1 file changed, 142 insertions(+), 34 deletions(-) diff --git a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java index 3f9bab90..a2f4bd29 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java @@ -93,6 +93,9 @@ public abstract class SerialiserFlavorBase implements Registerable { */ protected int currentSubtick = 0; + /** + * Previous serialised or deserialised container, used for allowing relative values in {@link #deserialiseRelativeFloat(String, String, Float) deserialiseRelativeFloat} + */ protected TickContainer previousTickContainer = null; /** @@ -119,7 +122,7 @@ public abstract class SerialiserFlavorBase implements Registerable { */ /** - *

Example

+ *
Example
*
 	 * ##################### TASfile ####################
 	 * 
@@ -131,7 +134,7 @@ protected String headerStart() { /** *

The end of the header, used for detecting when the header stops - *

Example

+ *
Example
*
 	 * ##################################################
 	 * 
@@ -143,7 +146,7 @@ protected String headerEnd() { /** *

Serialises the flavor of this file, the enabled file commands and other metadata - *

Tree

+ *
Tree
*
 	 * serialiseHeader
 	 *	├── {@link #headerStart()}
@@ -154,7 +157,7 @@ protected String headerEnd() {
 	 *	│   └── {@link #serialiseMetadataValues(List, LinkedHashMap)}
 	 *	└── {@link #headerEnd()}
 	 * 
- *

Example

+ *
Example
*
 	 * ##################### TASfile ####################					// {@link #headerStart()}
 	 * Flavor: beta1 										// {@link #serialiseFlavorName(List)}
@@ -191,7 +194,7 @@ public List serialiseHeader() {
 	 * 

How the flavor name is serialised *

You normally don't have to edit this,
* as the flavor name is taken from the extension name. - *

Example

+ *
Example
*
 	 * Flavor: beta1
 	 * 
@@ -204,7 +207,7 @@ protected void serialiseFlavorName(List out) { /** *

Adds the file commands that are enabled to the lines - *

Example

+ *
Example
*
 	 * FileCommand-Extensions: tasmod_label@v1, tasmod_desyncMonitor@v1
 	 * 
@@ -222,7 +225,7 @@ protected void serialiseFileCommandNames(List out) { /** *

Serialises the metadata to the header of the TASfile - *

Example

+ *
Example
*
 	 * --------------------- Credits --------------------
 	 * Title:Insert TAS category here
@@ -254,7 +257,7 @@ protected void serialiseMetadata(List out) {
 
 	/**
 	 * 

Serialises only the name of the metadata section - *

Example

+ *
Example
*
 	 * --------------------- Credits --------------------
 	 * 
@@ -267,7 +270,7 @@ protected void serialiseMetadataName(List out, String name) { /** *

Serialises only the values of the metadata section - *

Example

+ *
Example
*
 	 * Title:Insert TAS category here
 	 * Author:Insert author here
@@ -293,17 +296,22 @@ protected void serialiseMetadataValues(List out, LinkedHashMapSerialises a list of inputs into a list of strings
-	 * 

Tree

+ *
Tree
*
 	 * serialise
 	 * └── {@link #serialiseContainer(BigArrayList, TickContainer)}
 	 *     ├── {@link #serialiseKeyboard(VirtualKeyboard)}
+	 *     │   └── {@link #serialiseKeyboardSubtick(VirtualKeyboard)}
 	 *     ├── {@link #serialiseMouse(VirtualMouse)}
+	 *     │   └── {@link #serialiseMouseSubtick(VirtualMouse)}
 	 *     ├── {@link #serialiseCameraAngle(VirtualCameraAngle)}
+	 *     │   └── {@link #serialiseCameraAngleSubtick(VirtualCameraAngle)}
 	 *     ├── {@link #serialiseInlineComments(List, List)}
 	 *     │   └── {@link #serialiseFileCommandsInLine(List)}
+	 *     │       └── {@link #serialiseFileCommand(PlaybackFileCommand)}
 	 *     ├── {@link #serialiseEndlineComments(List, List)}	// Same as serialiseInlineComments
 	 *     │   └── {@link #serialiseFileCommandsEndLine(List)}	// Unused
+	 *     │       └── {@link #serialiseFileCommand(PlaybackFileCommand)}
 	 *     └── {@link #mergeInputs(BigArrayList, List, List, List, List)}
 	 * 
* @param inputs The inputs to serialise @@ -348,27 +356,20 @@ protected void serialiseContainer(BigArrayList out, TickContainer contai mergeInputs(out, serialisedKeyboard, serialisedMouse, serialisedCameraAngle, serialisedInlineComments, serialisedEndlineComments); } - protected String serialiseFileCommand(PlaybackFileCommand fileCommand) { - if (!processExtensions) - return ""; - return String.format("$%s(%s);", fileCommand.getName(), String.join(", ", fileCommand.getArgs())); - } - - protected String serialiseFileCommandsInLine(List fileCommands) { - if (fileCommands == null) { - return null; - } - List serialisedCommands = new ArrayList<>(); - for (PlaybackFileCommand command : fileCommands) { - serialisedCommands.add(serialiseFileCommand(command)); - } - return String.join(" ", serialisedCommands); - } - - protected String serialiseFileCommandsEndLine(List fileCommands) { - return serialiseFileCommandsInLine(fileCommands); - } - + /** + *

Serialises a {@link VirtualKeyboard} + * + *

A {@link VirtualKeyboard} is most often comprised of multiple subticks,
+ * which are each serialised in {@link #serialiseKeyboardSubtick(VirtualKeyboard)} + *

Example
+ *
+	 * 	W;w
+	 * 	A;a
+	 * 	S,D;sd
+	 * 
+ * @param keyboard The keyboard to serialise + * @return A list of serialised keyboardSubticks + */ protected List serialiseKeyboard(VirtualKeyboard keyboard) { List out = new ArrayList<>(); @@ -376,11 +377,30 @@ protected List serialiseKeyboard(VirtualKeyboard keyboard) { // pruneListEndEmptySubtickable(subticks); for (VirtualKeyboard subtick : subticks) { - out.add(String.format("%s;%s", String.join(",", subtick.getCurrentPresses()), charListToString(subtick.getCharList()))); + out.add(serialiseKeyboardSubtick(subtick)); } return out; } + /** + *

Serialises a single keyboard subtick + *

Used for setting the format of a keyboard input in general + *

The input is split between keycodes and key characters by a semicolon.
+ * While the key code can be written with a number (e.g. the key with the label W is keycode 17),
+ * Only the "name of the keycode" is used for serialisation. After the semicolon a key character is used.
+ * This is what is used for the chat or books, as, when holding e.g. shift, a capitalized character is used instead (SHIFT,W;W).
+ * Keycodes on the other hand have no concept of capitalisation and are used for the actual movement keys (Forward, Backward) + *

Example
+ *
+	 * 	W,S;ws
+	 * 
+ * @param keyboardSubtick The subtick to serialise + * @return The serialised subtick + */ + protected String serialiseKeyboardSubtick(VirtualKeyboard keyboardSubtick) { + return String.format("%s;%s", String.join(",", keyboardSubtick.getCurrentPresses()), charListToString(keyboardSubtick.getCharList())); + } + protected List serialiseMouse(VirtualMouse mouse) { List out = new ArrayList<>(); @@ -388,11 +408,15 @@ protected List serialiseMouse(VirtualMouse mouse) { // pruneListEndEmptySubtickable(subticks); for (VirtualMouse subtick : subticks) { - out.add(String.format("%s;%s,%s,%s", String.join(",", subtick.getCurrentPresses()), subtick.getScrollWheel(), subtick.getCursorX(), subtick.getCursorY())); + out.add(serialiseMouseSubtick(subtick)); } return out; } + protected String serialiseMouseSubtick(VirtualMouse mouseSubtick) { + return String.format("%s;%s,%s,%s", String.join(",", mouseSubtick.getCurrentPresses()), mouseSubtick.getScrollWheel(), mouseSubtick.getCursorX(), mouseSubtick.getCursorY()); + } + protected List serialiseCameraAngle(VirtualCameraAngle cameraAngle) { VirtualCameraAngle previousCamera = null; @@ -401,13 +425,17 @@ protected List serialiseCameraAngle(VirtualCameraAngle cameraAngle) { for (VirtualCameraAngle subtick : cameraAngle.getAll()) { if (!subtick.equals(previousCamera)) - out.add(String.format("%s;%s", subtick.getYaw(), subtick.getPitch())); + out.add(serialiseCameraAngleSubtick(subtick)); previousCamera = subtick; } return out; } + protected String serialiseCameraAngleSubtick(VirtualCameraAngle cameraAngleSubtick) { + return String.format("%s;%s", cameraAngleSubtick.getYaw(), cameraAngleSubtick.getPitch()); + } + protected List serialiseInlineComments(List inlineComments, List> fileCommandsInline) { List out = new ArrayList<>(); @@ -462,6 +490,81 @@ protected List serialiseEndlineComments(List endlineComments, Li return serialiseInlineComments(endlineComments, fileCommandsEndline); } + /** + *

Serialises a list of file commands in an inline comment + *

Uses {@link #serialiseFileCommand(PlaybackFileCommand) serialiseFileCommand} for the actual file command format,
+ * while this method dictates how they are joined together + *

By default, multiple file commands may be serialised like this + *

Example
+ *
+	 * 	// $fileCommandName1(argument1); $fileCommandName2(argument1, argument2);
+	 * 
+ * @param fileCommands The file commands to serialise + * @return A string of serialised file commands or null if fileCommands is null + */ + protected String serialiseFileCommandsInLine(List fileCommands) { + // File commands is null if there are no file commands in the comment. + // Return null if that is the case + if (fileCommands == null) { + return null; + } + List serialisedCommands = new ArrayList<>(); + for (PlaybackFileCommand command : fileCommands) { + serialisedCommands.add(serialiseFileCommand(command)); + } + return String.join(" ", serialisedCommands); + } + + /** + *

Serialises a list of file commands in an endline comment + *

This is added in case a flavor needs a different format for endline and inline commands,
+ * but by default this is the same as {@link #serialiseFileCommandsInLine(List) serialiseFileCommandsInLine} + *

Example
+ *
+	 * 	12|W;w||0;0	// $fileCommandName1(argument1); $fileCommandName2(argument1, argument2);
+	 * 
+ * @param fileCommands The file commands to serialise + * @return A string of serialised file commands or null if fileCommands is null + */ + protected String serialiseFileCommandsEndLine(List fileCommands) { + return serialiseFileCommandsInLine(fileCommands); + } + + /** + *

Serialises a single file command. + *

Used for setting the format of file commands + *

Example
+ *
+	 * 	$fileCommandName(argument1, argument2, argument3);
+	 * 
+ * + *

Has to check if {@link #processExtensions} is false + * @param fileCommand The {@link PlaybackFileCommand} to serialise + * @return The serialised file command, empty if {@link #processExtensions} is false + */ + protected String serialiseFileCommand(PlaybackFileCommand fileCommand) { + if (!processExtensions) + return ""; + return String.format("$%s(%s);", fileCommand.getName(), String.join(", ", fileCommand.getArgs())); + } + + /** + *

Merges lists of keyboard, mouse, camera angle, inline and endline comments together into one string + *

Example
+ *
+	 * // Inline comment
+	 * // $inlineFileCommand(arg);
+	 * 256|W;w|;0,0,0|31.778223;85.11482		// Endline comment
+	 *	1|W,S;s|;0,0,0|34.47822;82.56482	// $endlineFileCommand(arg)
+	 *	2|;||37.02822;79.86482
+	 * 
+ * @param out The list of lines that will be written to file, passed in by reference + * @param serialisedKeyboard The serialised keyboard from {@link #serialiseKeyboard(VirtualKeyboard)} + * @param serialisedMouse The serialised mouse from {@link #serialiseMouse(VirtualMouse)} + * @param serialisedCameraAngle The serialised camera angle from {@link #serialiseCameraAngle(VirtualCameraAngle)} + * @param serialisedInlineComments The inline comments from {@link #serialiseInlineComments(List, List)} + * @param serialisedEndlineComments The endline comments from {@link #serialiseEndlineComments(List, List)} + */ protected void mergeInputs(BigArrayList out, List serialisedKeyboard, List serialisedMouse, List serialisedCameraAngle, List serialisedInlineComments, List serialisedEndlineComments) { /* @@ -521,6 +624,11 @@ protected void mergeInputs(BigArrayList out, List serialisedKeyb currentSubtick = 0; } + /** + * If a string is null, return an empty string + * @param string String to check + * @return The string or empty if null + */ protected String getOrEmpty(String string) { return string == null ? "" : string; } From f785d93345d270089036ac92fa040b4db2ff1f0b Mon Sep 17 00:00:00 2001 From: Scribble Date: Wed, 19 Mar 2025 20:50:20 +0100 Subject: [PATCH 10/16] [Flavor] Finish documentation for serialising --- .../tasfile/flavor/SerialiserFlavorBase.java | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java index a2f4bd29..0fec9b59 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java @@ -401,6 +401,19 @@ protected String serialiseKeyboardSubtick(VirtualKeyboard keyboardSubtick) { return String.format("%s;%s", String.join(",", keyboardSubtick.getCurrentPresses()), charListToString(keyboardSubtick.getCharList())); } + /** + *

Serialises a {@link VirtualMouse} + *

A {@link VirtualMouse} is most often comprised of multiple subticks,
+ * which are each serialised in {@link #serialiseMouseSubtick(VirtualMouse)} + *

Example
+ *
+	 * 	LC;0,15,21
+	 * 	RC;-15,15,21
+	 * 	RC,MC;30,14,20
+	 * 
+ * @param mouse The mouse to serialise + * @return A list of serialised mouse subticks + */ protected List serialiseMouse(VirtualMouse mouse) { List out = new ArrayList<>(); @@ -413,10 +426,34 @@ protected List serialiseMouse(VirtualMouse mouse) { return out; } + /** + *

Serialises a single mouse subtick + *

The mouse subtick is comprised of the following:
+ * mouseKeycodes;scrollWheel,cursorX,cursorY + *

Example
+ *
+	 * 	LC;0,15,21
+	 * 
+ * @param mouseSubtick The mouse subtick to serialise + * @return The serialised mouse subtick + */ protected String serialiseMouseSubtick(VirtualMouse mouseSubtick) { return String.format("%s;%s,%s,%s", String.join(",", mouseSubtick.getCurrentPresses()), mouseSubtick.getScrollWheel(), mouseSubtick.getCursorX(), mouseSubtick.getCursorY()); } + /** + *

Serialises a {@link VirtualCameraAngle} + *

A {@link VirtualCameraAngle} is most often comprised of multiple subticks,
+ * which are each serialised in {@link #serialiseCameraAngleSubtick(VirtualCameraAngle)} + *

Example
+ *
+	 * 	35;26
+	 * 	34;25
+	 * 	140;-130
+	 * 
+ * @param cameraAngle Camera angle to serialise + * @return The serialised list of camera angles + */ protected List serialiseCameraAngle(VirtualCameraAngle cameraAngle) { VirtualCameraAngle previousCamera = null; @@ -432,10 +469,34 @@ protected List serialiseCameraAngle(VirtualCameraAngle cameraAngle) { return out; } + /** + *

Serialises a single camera angle subtick + *

The subtick is comprised of:
+ * The camera angle yaw and the camera angle pitch + *

Example
+ *
+	 * 	140;-130
+	 * 
+ * + * @param cameraAngleSubtick The camera angle subtick to serialise + * @return The serialised camera angle subtick + */ protected String serialiseCameraAngleSubtick(VirtualCameraAngle cameraAngleSubtick) { return String.format("%s;%s", cameraAngleSubtick.getYaw(), cameraAngleSubtick.getPitch()); } + /** + *

Serialise comments that take up an entire line + *

In addition, comments can contain {@link PlaybackFileCommand FileCommands} that are serialised in {@link #serialiseFileCommandsInLine(List)} + *

Example
+ *
+	 * // Inline comment
+	 * 12|W;w||0;0
+	 * 
+ * @param inlineComments The list of inline comments to serialise + * @param fileCommandsInline The list of file commands to serialise + * @return List of comments including file commands + */ protected List serialiseInlineComments(List inlineComments, List> fileCommandsInline) { List out = new ArrayList<>(); @@ -486,6 +547,16 @@ protected List serialiseInlineComments(List inlineComments, List return out; } + /** + *

Serialise comments that are written at the end of the line + *

Example
+ *
+	 *	12|W;w||0;0	// Endline comment
+	 * 
+ * @param endlineComments The list of endline comments to serialise + * @param fileCommandsEndline The list of file commands to serialise + * @return The serialised comments + */ protected List serialiseEndlineComments(List endlineComments, List> fileCommandsEndline) { return serialiseInlineComments(endlineComments, fileCommandsEndline); } From f42123773d51168d8f7d0a708ae65b962e5c87ff Mon Sep 17 00:00:00 2001 From: Scribble Date: Wed, 19 Mar 2025 21:13:18 +0100 Subject: [PATCH 11/16] [Flavor] Start on deserialise documentation --- .../tasfile/flavor/SerialiserFlavorBase.java | 87 +++++++++++++------ 1 file changed, 62 insertions(+), 25 deletions(-) diff --git a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java index 0fec9b59..05bf9d19 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java @@ -750,6 +750,14 @@ protected String joinNotEmpty(String delimiter, String... args) { * */ + /* + _ _ ____ __ ____ ____ ____ + ( )_( )( ___) /__\ ( _ \( ___)( _ \ + ) _ ( )__) /(__)\ )(_) ))__) ) / + (_) (_)(____)(__)(__)(____/(____)(_)\_) + + */ + /** *

Checks if the name of this flavor is present in the header of the TASfile. *

Used to determine the flavor of the file if the flavor is not given @@ -767,11 +775,14 @@ public boolean checkFlavorName(List headerLines) { return false; } - public void deserialiseHeader(List headerLines) { - deserialiseMetadata(headerLines); - deserialiseFileCommandNames(headerLines); - } - + /** + *

Extracts the header from the TASfile + *

Optimization to seperate the header from the actual inputs.
+ * Only reads a maximum of 1000 and until it finds {@link #headerEnd()} + * @param lines The total lines to check + * @return The list of lines containing the header + * @throws PlaybackLoadException If the end of the header is not found after 1000 lines + */ public List extractHeader(BigArrayList lines) { List extracted = new ArrayList<>(); @@ -789,28 +800,26 @@ public List extractHeader(BigArrayList lines) { throw new PlaybackLoadException("Cannot find the end of the header"); } - protected void deserialiseFileCommandNames(List headerLines) { - if (!processExtensions) // Stops FileCommandProcessing - return; - - for (String line : headerLines) { - Matcher matcher = extract("FileCommand-Extensions: ?(.*)", line); - - if (matcher.find()) { - - if (!processExtensions) - return; - - String extensionStrings = matcher.group(1); - String[] extensionNames = extensionStrings.split(", ?"); - - TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.setEnabled(extensionNames); - return; - } - } - throw new PlaybackLoadException("FileCommand-Extensions value was not found in the header"); + /** + *

Deserialise header lines + *

+	 *	deserialiseHeader
+	 *  ├── {@link #deserialiseMetadata(List)}
+	 *  └── {@link #deserialiseFileCommandNames(List)}
+	 * 
+ * @param headerLines The header lines to deserialise + * @see #serialiseHeader() + */ + public void deserialiseHeader(List headerLines) { + deserialiseMetadata(headerLines); + deserialiseFileCommandNames(headerLines); } + /** + *

Deserialises the TASfile metadata + * @param headerLines + * @see #serialiseMetadata(List) + */ protected void deserialiseMetadata(List headerLines) { if (!processExtensions) return; @@ -847,6 +856,34 @@ protected void deserialiseMetadata(List headerLines) { TASmodAPIRegistry.PLAYBACK_METADATA.handleOnLoad(out); } + /** + *

Deserialises file command extension names and enables them + * @param headerLines The header lines to search + * @see #serialiseFileCommandNames(List) + * @throws PlaybackLoadException If the "FileCommand-Extensions" keyword is not found in the header + */ + protected void deserialiseFileCommandNames(List headerLines) { + if (!processExtensions) // Stops FileCommandProcessing + return; + + for (String line : headerLines) { + Matcher matcher = extract("FileCommand-Extensions: ?(.*)", line); + + if (matcher.find()) { + + if (!processExtensions) + return; + + String extensionStrings = matcher.group(1); + String[] extensionNames = extensionStrings.split(", ?"); + + TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.setEnabled(extensionNames); + return; + } + } + throw new PlaybackLoadException("FileCommand-Extensions value was not found in the header"); + } + /** * Deserialises the input part of the TASfile * From d64ca4ff4ca5d0f096a18e6044630f80ec57754d Mon Sep 17 00:00:00 2001 From: Scribble Date: Wed, 19 Mar 2025 21:57:14 +0100 Subject: [PATCH 12/16] [Flavor] Add monitoring serialisation to alpha flavor --- .../tasfile/flavor/SerialiserFlavorBase.java | 102 +++++++++++++++++- .../tasfile/flavor/builtin/AlphaFlavor.java | 25 +++++ 2 files changed, 122 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java index 05bf9d19..5e9afd1c 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java @@ -313,6 +313,8 @@ protected void serialiseMetadataValues(List out, LinkedHashMap * @param inputs The inputs to serialise * @param toTick The tick where to stop, used for partial serialisation by savestates. -1 to serialise all @@ -525,7 +527,7 @@ protected List serialiseInlineComments(List inlineComments, List continue; } - out.add(String.format("// %s", joinNotEmpty(" ", command, comment))); + out.add(serialiseInlineComment(joinNotEmpty(" ", command, comment))); } } @@ -537,7 +539,7 @@ protected List serialiseInlineComments(List inlineComments, List String command = serialiseFileCommandsInLine(fileCommandQueue.poll()); if (command != null) { - out.add(String.format("// %s", command)); + out.add(serialiseInlineComment(command)); } else { out.add(""); // Add an empty line if command is null } @@ -547,6 +549,10 @@ protected List serialiseInlineComments(List inlineComments, List return out; } + protected String serialiseInlineComment(String comment) { + return String.format("// %s", comment); + } + /** *

Serialise comments that are written at the end of the line *

Example
@@ -558,7 +564,57 @@ protected List serialiseInlineComments(List inlineComments, List * @return The serialised comments */ protected List serialiseEndlineComments(List endlineComments, List> fileCommandsEndline) { - return serialiseInlineComments(endlineComments, fileCommandsEndline); + List out = new ArrayList<>(); + + Queue> fileCommandQueue = null; + if (fileCommandsEndline != null) { + fileCommandQueue = new LinkedList<>(fileCommandsEndline); + } + + // Serialise comments and merge them with file commands + if (endlineComments != null) { + + Queue commentQueue = new LinkedList<>(endlineComments); + + // Iterate through comments + while (!commentQueue.isEmpty()) { + String comment = commentQueue.poll(); // Due to commentQueue being a LinkedList, comment can be null at this point! + + String command = null; + if (fileCommandQueue != null) { + command = serialiseFileCommandsEndLine(fileCommandQueue.poll()); // Trying to poll a fileCommand. Command can be null at this point + } + + // Add an empty line if comment and command is null + if (comment == null && command == null) { + out.add(""); + continue; + } + + out.add(serialiseEndlineComment(joinNotEmpty(" ", command, comment))); + } + } + + if (fileCommandQueue != null) { + + // If the fileCommandQueue is not empty or longer than the commentQueue, + // add the rest of the fileCommands to the end + while (!fileCommandQueue.isEmpty()) { + + String command = serialiseFileCommandsEndLine(fileCommandQueue.poll()); + if (command != null) { + out.add(serialiseEndlineComment(command)); + } else { + out.add(""); // Add an empty line if command is null + } + } + } + + return out; + } + + protected String serialiseEndlineComment(String comment) { + return String.format("// %s", comment); } /** @@ -665,7 +721,7 @@ protected void mergeInputs(BigArrayList out, List serialisedKeyb } // Add tick line, not indented - out.add(String.format("%s|%s|%s|%s%s", currentTick, kb, ms, ca, elc)); + out.add(mergeInput(currentTick, kb, ms, ca, elc)); // Add subtick lines, not indented currentSubtick = 0; @@ -679,7 +735,7 @@ protected void mergeInputs(BigArrayList out, List serialisedKeyb elc = "\t\t" + elc; } - out.add(String.format("\t%s|%s|%s|%s%s", currentSubtick, kb, ms, ca, elc)); + out.add(mergeSubtickInput(currentSubtick, kb, ms, ca, elc)); } /* @@ -695,6 +751,42 @@ protected void mergeInputs(BigArrayList out, List serialisedKeyb currentSubtick = 0; } + /** + *

How parent inputs are merged + *

Example
+ *
+	 * 256|W;w|;0,0,0|31.778223;85.11482		// Endline comment
+	 * 
+ * + * @param currentTick The current tick + * @param keyboard The serialised keyboard + * @param mouse The serialised mouse + * @param cameraAngle The serialises camera angle + * @param endLineComment The end line comment + * @return The merged strings + */ + protected String mergeInput(long currentTick, String keyboard, String mouse, String cameraAngle, String endLineComment) { + return String.format("%s|%s|%s|%s%s", currentTick, keyboard, mouse, cameraAngle, endLineComment); + } + + /** + *

How subtick inputs are merged + *

Example
+ *
+	 * 256|W;w|;0,0,0|31.778223;85.11482		// Parent
+	 *	1|W,S;s|;0,0,0|34.47822;82.56482		// Subtick
+	 * 
+ * @param currentSubtick The current subtick in this sequence + * @param keyboard The serialised keyboard + * @param mouse The serialised mouse + * @param cameraAngle The serialised camera angle + * @param endLineComment The serialised end line comment + * @return The merged subticks + */ + protected String mergeSubtickInput(int currentSubtick, String keyboard, String mouse, String cameraAngle, String endLineComment) { + return String.format("\t%s|%s|%s|%s%s", currentSubtick, keyboard, mouse, cameraAngle, endLineComment); + } + /** * If a string is null, return an empty string * @param string String to check diff --git a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java index cc5aff15..c327e74e 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java @@ -15,6 +15,7 @@ import java.util.List; import java.util.regex.Matcher; +import com.minecrafttas.tasmod.playback.filecommands.PlaybackFileCommand; import com.minecrafttas.tasmod.playback.metadata.PlaybackMetadata; import com.minecrafttas.tasmod.playback.tasfile.flavor.SerialiserFlavorBase; import com.minecrafttas.tasmod.registries.TASmodAPIRegistry; @@ -226,6 +227,30 @@ protected List serialiseCameraAngle(VirtualCameraAngle subticks) { return out; } + @Override + protected String serialiseFileCommandsEndLine(List fileCommands) { + if (fileCommands == null) { + return null; + } + List serialisedCommands = new ArrayList<>(); + for (PlaybackFileCommand command : fileCommands) { + if ("desyncMonitor".equals(command.getName())) { + serialisedCommands.add(String.format("Monitoring:%s", String.join(" ", command.getArgs()))); + } + } + return String.join(" ", serialisedCommands); + } + + @Override + protected String serialiseEndlineComment(String comment) { + return String.format("//%s", comment); + } + + @Override + protected String mergeInput(long currentTick, String keyboard, String mouse, String cameraAngle, String endLineComment) { + return String.format("%s|%s|%s|%s~&\t\t\t\t%s", currentTick, keyboard, mouse, cameraAngle, endLineComment); + } + @Override protected void deserialiseMetadata(List headerLines) { String author = "Insert author here"; From 37b3af7438a7ec876cbcd8eaa02af5a20d675e4b Mon Sep 17 00:00:00 2001 From: Scribble Date: Fri, 21 Mar 2025 23:21:45 +0100 Subject: [PATCH 13/16] [Flavor] Finish alpha serialisation and deserialisation - A lot of fixes and changes to get old files to load - Fixed some error messages not displaying the correct values - More tweaks to SerialiserFlavorBase --- .../playback/PlaybackControllerClient.java | 1 + .../DesyncMonitorFileCommandExtension.java | 2 +- .../playback/tasfile/PlaybackSerialiser.java | 2 +- .../tasfile/flavor/SerialiserFlavorBase.java | 61 ++++-- .../tasfile/flavor/builtin/AlphaFlavor.java | 187 +++++++++++++++++- 5 files changed, 229 insertions(+), 24 deletions(-) diff --git a/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java b/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java index da44dcb6..683f5473 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java @@ -846,6 +846,7 @@ public void onClientPacket(PacketID id, ByteBuffer buf, String username) throws if (mc.world != null) mc.ingameGUI.getChatGUI().printChatMessage(new TextComponentString(TextFormatting.RED + "Loading failed, something went very wrong")); LOGGER.catching(e); + return; } if (mc.world != null) diff --git a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/DesyncMonitorFileCommandExtension.java b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/DesyncMonitorFileCommandExtension.java index 5d50a604..ae6c25e7 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/DesyncMonitorFileCommandExtension.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/DesyncMonitorFileCommandExtension.java @@ -135,7 +135,7 @@ public void onPlayback(long tick, TickContainer tickContainer) { private MonitorContainer loadFromFile(long tick, String[] args) throws PlaybackLoadException { if (args.length != 6) - throw new PlaybackLoadException("Tick %s: desyncMonitorArgsLength "); + throw new PlaybackLoadException("Could not load desyncMonitor file command in tick %s. The amount of arguments doesn't match: %s", tick, args.length); double x = 0; double y = 0; diff --git a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiser.java b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiser.java index c1b24116..a110cbad 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiser.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiser.java @@ -177,7 +177,7 @@ public static BigArrayList loadFromFile(Path file, String flavorN // Read the head of the TASfile to check if the flavors match SerialiserFlavorBase flavorInFile = readFlavor(file); if (!flavor.equals(flavorInFile)) { - throw new PlaybackLoadException("Detected flavor %s in the TASfile, which does not match the specified flavor: %s"); + throw new PlaybackLoadException("Detected flavor %s in the TASfile, which does not match the specified flavor: %s", flavorInFile.getExtensionName(), flavor.getExtensionName()); } flavor.setProcessExtensions(processExtensions); diff --git a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java index 5e9afd1c..cd1d48a7 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java @@ -307,10 +307,10 @@ protected void serialiseMetadataValues(List out, LinkedHashMapSerialise comments that take up an entire line - *

In addition, comments can contain {@link PlaybackFileCommand FileCommands} that are serialised in {@link #serialiseFileCommandsInLine(List)} + *

In addition, comments can contain {@link PlaybackFileCommand FileCommands} that are serialised in {@link #serialiseFileCommandsInline(List)} *

Example
*
 	 * // Inline comment
@@ -518,7 +518,7 @@ protected List serialiseInlineComments(List inlineComments, List
 
 				String command = null;
 				if (fileCommandQueue != null) {
-					command = serialiseFileCommandsInLine(fileCommandQueue.poll()); // Trying to poll a fileCommand. Command can be null at this point
+					command = serialiseFileCommandsInline(fileCommandQueue.poll()); // Trying to poll a fileCommand. Command can be null at this point
 				}
 
 				// Add an empty line if comment and command is null
@@ -537,7 +537,7 @@ protected List serialiseInlineComments(List inlineComments, List
 			// add the rest of the fileCommands to the end
 			while (!fileCommandQueue.isEmpty()) {
 
-				String command = serialiseFileCommandsInLine(fileCommandQueue.poll());
+				String command = serialiseFileCommandsInline(fileCommandQueue.poll());
 				if (command != null) {
 					out.add(serialiseInlineComment(command));
 				} else {
@@ -582,7 +582,7 @@ protected List serialiseEndlineComments(List endlineComments, Li
 
 				String command = null;
 				if (fileCommandQueue != null) {
-					command = serialiseFileCommandsEndLine(fileCommandQueue.poll()); // Trying to poll a fileCommand. Command can be null at this point
+					command = serialiseFileCommandsEndline(fileCommandQueue.poll()); // Trying to poll a fileCommand. Command can be null at this point
 				}
 
 				// Add an empty line if comment and command is null
@@ -601,7 +601,7 @@ protected List serialiseEndlineComments(List endlineComments, Li
 			// add the rest of the fileCommands to the end
 			while (!fileCommandQueue.isEmpty()) {
 
-				String command = serialiseFileCommandsEndLine(fileCommandQueue.poll());
+				String command = serialiseFileCommandsEndline(fileCommandQueue.poll());
 				if (command != null) {
 					out.add(serialiseEndlineComment(command));
 				} else {
@@ -629,7 +629,7 @@ protected String serialiseEndlineComment(String comment) {
 	 * @param fileCommands The file commands to serialise
 	 * @return A string of serialised file commands or null if fileCommands is null
 	 */
-	protected String serialiseFileCommandsInLine(List fileCommands) {
+	protected String serialiseFileCommandsInline(List fileCommands) {
 		// File commands is null if there are no file commands in the comment.
 		// Return null if that is the case
 		if (fileCommands == null) {
@@ -645,7 +645,7 @@ protected String serialiseFileCommandsInLine(List fileComma
 	/**
 	 * 

Serialises a list of file commands in an endline comment *

This is added in case a flavor needs a different format for endline and inline commands,
- * but by default this is the same as {@link #serialiseFileCommandsInLine(List) serialiseFileCommandsInLine} + * but by default this is the same as {@link #serialiseFileCommandsInline(List) serialiseFileCommandsInLine} *

Example
*
 	 * 	12|W;w||0;0	// $fileCommandName1(argument1); $fileCommandName2(argument1, argument2);
@@ -653,8 +653,8 @@ protected String serialiseFileCommandsInLine(List fileComma
 	 * @param fileCommands The file commands to serialise
 	 * @return A string of serialised file commands or null if fileCommands is null
 	 */
-	protected String serialiseFileCommandsEndLine(List fileCommands) {
-		return serialiseFileCommandsInLine(fileCommands);
+	protected String serialiseFileCommandsEndline(List fileCommands) {
+		return serialiseFileCommandsInline(fileCommands);
 	}
 
 	/**
@@ -990,7 +990,7 @@ public BigArrayList deserialise(BigArrayList lines, long
 			// Extract the tick and set the index
 			i = extractContainer(container, lines, i);
 			currentLine = i;
-			// Extract container
+			// Deserialise container
 			deserialiseContainer(out, container);
 			currentTick++;
 		}
@@ -1222,7 +1222,7 @@ protected void deserialiseMultipleInlineComments(List inlineComments, Li
 	}
 
 	protected String deserialiseInlineComment(String comment, List deserialisedFileCommands) {
-		comment = deserialiseFileCommands(comment, deserialisedFileCommands);
+		comment = deserialiseFileCommandsInline(comment, deserialisedFileCommands);
 		comment = extract("^// ?(.+)", comment, 1);
 		if (comment != null) {
 			comment = comment.trim();
@@ -1234,11 +1234,36 @@ protected String deserialiseInlineComment(String comment, List deserialisedFileCommands) {
-		return deserialiseInlineComment(comment, deserialisedFileCommands);
+		comment = deserialiseFileCommandsEndline(comment, deserialisedFileCommands);
+		comment = extract("^// ?(.+)", comment, 1);
+		if (comment != null) {
+			comment = comment.trim();
+			if (comment.isEmpty()) {
+				comment = null;
+			}
+		}
+		return comment;
 	}
 
-	protected String deserialiseFileCommands(String comment, List deserialisedFileCommands) {
+	protected String deserialiseFileCommandsInline(String comment, List deserialisedFileCommands) {
+		Matcher matcher = extract("\\$(.+?)\\((.*?)\\);", comment);
+
+		// Iterate through all file commands and add each to the list
+		while (matcher.find()) {
+			String name = matcher.group(1);
+			String[] args = matcher.group(2).split(", ?");
+
+			if (processExtensions)
+				deserialisedFileCommands.add(new PlaybackFileCommand(name, args));
 
+			comment = matcher.replaceFirst("");
+			matcher.reset(comment);
+		}
+
+		return comment;
+	}
+
+	protected String deserialiseFileCommandsEndline(String comment, List deserialisedFileCommands) {
 		Matcher matcher = extract("\\$(.+?)\\((.*?)\\);", comment);
 
 		// Iterate through all file commands and add each to the list
@@ -1467,7 +1492,7 @@ protected void splitInputs(List lines, List serialisedKeyboard,
 		}
 
 		for (String line : lines) {
-			Matcher tickMatcher = extract("^\\t?\\d+\\|(.*?)\\|(.*?)\\|(\\S*)\\s?", line);
+			Matcher tickMatcher = extract(splitInputRegex(), line);
 
 			if (tickMatcher.find()) {
 				if (!tickMatcher.group(1).isEmpty()) {
@@ -1498,6 +1523,10 @@ protected void splitInputs(List lines, List serialisedKeyboard,
 		}
 	}
 
+	protected String splitInputRegex() {
+		return "^\\t?\\d+\\|(.*?)\\|(.*?)\\|(\\S*)\\s?";
+	}
+
 	protected Matcher extract(String regex, String haystack) {
 		Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
 		Matcher matcher = pattern.matcher(haystack);
diff --git a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java
index c327e74e..0392245d 100644
--- a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java
+++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java
@@ -15,8 +15,11 @@
 import java.util.List;
 import java.util.regex.Matcher;
 
+import com.dselent.bigarraylist.BigArrayList;
+import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TickContainer;
 import com.minecrafttas.tasmod.playback.filecommands.PlaybackFileCommand;
 import com.minecrafttas.tasmod.playback.metadata.PlaybackMetadata;
+import com.minecrafttas.tasmod.playback.tasfile.exception.PlaybackLoadException;
 import com.minecrafttas.tasmod.playback.tasfile.flavor.SerialiserFlavorBase;
 import com.minecrafttas.tasmod.registries.TASmodAPIRegistry;
 import com.minecrafttas.tasmod.virtual.VirtualCameraAngle;
@@ -53,13 +56,13 @@ public List serialiseHeader() {
 				+ "#																											#");
 		serialiseMetadata(out);
 		out.add(headerEnd());
+		out.add("#Comments start with \"//\" at the start of the line, comments with # will not be saved");
 		return out;
 	}
 
 	@Override
 	protected String headerEnd() {
-		return "#############################################################################################################\n"
-				+ "#Comments start with \"//\" at the start of the line, comments with # will not be saved";
+		return "#############################################################################################################";
 	}
 
 	@Override
@@ -127,6 +130,8 @@ protected List serialiseKeyboard(VirtualKeyboard keyboard) {
 				if (i == stringy.size() - 1) {
 					seperator = "";
 				}
+				if (stringy.get(i).equals("ZERO"))
+					continue;
 				keyString = keyString.concat(stringy.get(i) + seperator);
 			}
 		}
@@ -160,11 +165,13 @@ protected List serialiseMouse(VirtualMouse mouse) {
 				if (i == stringy.size() - 1) {
 					seperator = "";
 				}
+				if (stringy.get(i).equals("MOUSEMOVED"))
+					continue;
 				keyString = keyString.concat(stringy.get(i) + seperator);
 			}
 		}
 
-		List path = new ArrayList<>(mouse.getSubticks()); // I previously called subticks "paths" as it was mainly used for the mouse...
+		List path = new ArrayList<>(mouse.getAll()); // I previously called subticks "paths" as it was mainly used for the mouse...
 //		pruneListEndEmptySubtickable(path);
 
 		/*
@@ -223,12 +230,14 @@ protected List serialiseCameraAngle(VirtualCameraAngle subticks) {
 		 * To honor this fact, it is also called subticks here, even though
 		 * actual subticks were not supported
 		 */
-		out.add("Camera:" + subticks.getPitch() + ";" + subticks.getYaw());
+		float pitch = subticks.getPitch() == null ? 0f : subticks.getPitch();
+		float yaw = subticks.getYaw() == null ? 0f : subticks.getYaw();
+		out.add("Camera:" + yaw + ";" + pitch);
 		return out;
 	}
 
 	@Override
-	protected String serialiseFileCommandsEndLine(List fileCommands) {
+	protected String serialiseFileCommandsEndline(List fileCommands) {
 		if (fileCommands == null) {
 			return null;
 		}
@@ -248,7 +257,7 @@ protected String serialiseEndlineComment(String comment) {
 
 	@Override
 	protected String mergeInput(long currentTick, String keyboard, String mouse, String cameraAngle, String endLineComment) {
-		return String.format("%s|%s|%s|%s~&\t\t\t\t%s", currentTick, keyboard, mouse, cameraAngle, endLineComment);
+		return String.format("%s|%s|%s|%s~&\t\t%s", currentTick, keyboard, mouse, cameraAngle, endLineComment);
 	}
 
 	@Override
@@ -306,6 +315,172 @@ protected void deserialiseMetadata(List headerLines) {
 		TASmodAPIRegistry.PLAYBACK_METADATA.handleOnLoad(metadataList);
 	}
 
+	@Override
+	protected String splitInputRegex() {
+		return "^\\d+\\|(.*?)\\|(.*?)\\|(\\S*)~&";
+	}
+
+	@Override
+	protected String deserialiseFileCommandsEndline(String comment, List deserialisedFileCommands) {
+		Matcher matcher = extract("Monitoring:(.+)", comment);
+
+		// Iterate through all file commands and add each to the list
+		while (matcher.find()) {
+			String name = "desyncMonitor";
+			String[] args = matcher.group(1).split(" ");
+
+			String[] shortenedArgs = new String[6];
+			for (int i = 0; i < 6; i++) {
+				shortenedArgs[i] = args[i];
+			}
+
+			if (processExtensions)
+				deserialisedFileCommands.add(new PlaybackFileCommand(name, shortenedArgs));
+
+			comment = matcher.replaceFirst("");
+			matcher.reset(comment);
+		}
+
+		return comment;
+	}
+
+	@Override
+	protected VirtualKeyboard deserialiseKeyboard(List keyboardStrings) {
+		VirtualKeyboard out = new VirtualKeyboard();
+
+		currentSubtick = 0;
+		for (String line : keyboardStrings) {
+			Matcher matcher = extract("Keyboard:(.*?);(.*)", line);
+			if (matcher.find()) {
+				String[] keys = matcher.group(1).split(",");
+				char[] chars = matcher.group(2).toCharArray();
+
+				int[] keycodes = deserialiseVirtualKeyboardKey(keys);
+				out.updateFromState(keycodes, chars);
+			} else {
+				throw new PlaybackLoadException(currentLine, currentTick, currentSubtick, "Keyboard could not be read. Probably a missing semicolon: %s", line);
+			}
+			currentSubtick++;
+		}
+		return out;
+	}
+
+	@Override
+	public BigArrayList deserialise(BigArrayList lines, long startPos) {
+		BigArrayList out = new BigArrayList<>();
+		for (long i = startPos; i < lines.size(); i++) {
+
+			if (lines.get(i).startsWith("#")) {
+				continue;
+			}
+			List container = new ArrayList<>();
+			// Extract the tick and set the index
+			i = extractContainer(container, lines, i);
+			currentLine = i;
+			// Deserialise container
+			deserialiseContainer(out, container);
+			currentTick++;
+		}
+		previousTickContainer = null;
+		return out;
+	}
+
+	@Override
+	protected VirtualMouse deserialiseMouse(List mouseStrings) {
+		String section = mouseStrings.get(0);
+		VirtualMouse mouse = new VirtualMouse();
+
+		// Remove the prefix
+		section = section.replace("Mouse:", "");
+
+		//Split into buttons and paths...
+		String buttons = section.split(";")[0];
+		String path = section.split(";")[1];
+
+		//Check whether the button is empty
+		if (!buttons.isEmpty()) {
+
+			//Splitting multiple buttons
+			String[] splitButtons = buttons.split(",");
+			int[] keys = deserialiseVirtualMouseKey(splitButtons);
+			mouse.updateFromState(keys, 0, 0, 0);
+		}
+		readPath(path, mouse);
+
+		return mouse;
+	}
+
+	protected void readPath(String section, VirtualMouse mouse) {
+
+		section = section.replace("[", "").replace("]", "");
+		String[] pathNodes = section.split("->");
+
+		for (String pathNode : pathNodes) {
+			String[] split = pathNode.split(",");
+
+			int length = split.length;
+			int scrollWheel = 0;
+			int cursorX = 0;
+			int cursorY = 0;
+			try {
+				scrollWheel = Integer.parseInt(split[length - 3]);
+				cursorX = Integer.parseInt(split[length - 2]);
+				cursorY = Integer.parseInt(split[length - 1]);
+			} catch (NumberFormatException e) {
+				throw new PlaybackLoadException("'" + pathNode + "' couldn't be read in line " + currentLine + ": Something is not a number");
+			} catch (ArrayIndexOutOfBoundsException e) {
+				throw new PlaybackLoadException("'" + pathNode + "' couldn't be read in line " + currentLine + ": Something is missing or is too much");
+			}
+			List keyList = new ArrayList<>();
+			for (int i = 0; i < length - 3; i++) {
+				String key = split[i];
+				Integer keyCode = VirtualKey.getKeycode(key);
+				if (keyCode == null) {
+					throw new PlaybackLoadException(currentLine, currentTick, currentSubtick, "Could not find keycode");
+				}
+				keyList.add(keyCode);
+			}
+			int[] keyListArray = new int[keyList.size()];
+			for (int i = 0; i < keyList.size(); i++) {
+				keyListArray[i] = keyList.get(i);
+			}
+			mouse.updateFromState(keyListArray, scrollWheel, cursorX, cursorY);
+		}
+	}
+
+	@Override
+	protected VirtualCameraAngle deserialiseCameraAngle(List cameraAngleStrings) {
+		VirtualCameraAngle out = new VirtualCameraAngle();
+
+		currentSubtick = 0;
+		Float previousPitch = previousTickContainer == null ? null : previousTickContainer.getCameraAngle().getPitch();
+		Float previousYaw = previousTickContainer == null ? null : previousTickContainer.getCameraAngle().getYaw();
+
+		for (String line : cameraAngleStrings) {
+			Matcher matcher = extract("Camera:(.+?);(.+)", line);
+
+			if (matcher.find()) {
+				String cameraYawString = matcher.group(1);
+				String cameraPitchString = matcher.group(2);
+
+				Float cameraYaw = null;
+				Float cameraPitch = null;
+
+				if (!"null".equals(cameraYawString))
+					cameraYaw = deserialiseRelativeFloat("camera yaw", cameraYawString, previousYaw);
+
+				if (!"null".equals(cameraPitchString))
+					cameraPitch = deserialiseRelativeFloat("camera pitch", cameraPitchString, previousPitch);
+
+				out.updateFromState(cameraPitch, cameraYaw);
+			} else {
+				throw new PlaybackLoadException(currentLine, currentTick, currentSubtick, "Camera is missing a semicolon");
+			}
+			currentSubtick++;
+		}
+		return out;
+	}
+
 	@Override
 	protected void deserialiseFileCommandNames(List headerLines) {
 		/*

From 0cac623e634a6eb5821b66008a31f4230091e870 Mon Sep 17 00:00:00 2001
From: Scribble 
Date: Fri, 21 Mar 2025 23:43:51 +0100
Subject: [PATCH 14/16] [Flavor] Add inlineFileCommand support for AlphaFlavor

---
 .../builtin/OptionsFileCommandExtension.java  |  6 +++
 .../tasfile/flavor/builtin/AlphaFlavor.java   | 43 +++++++++++++++++++
 .../tasfile/builtin/AlphaFlavorTest.java      |  4 +-
 3 files changed, 51 insertions(+), 2 deletions(-)

diff --git a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/OptionsFileCommandExtension.java b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/OptionsFileCommandExtension.java
index b071afe2..b2d4cd7f 100644
--- a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/OptionsFileCommandExtension.java
+++ b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/OptionsFileCommandExtension.java
@@ -44,6 +44,12 @@ public String[] getFileCommandNames() {
 		return new String[] { "hud" };
 	}
 
+	@Override
+	public PlaybackFileCommandContainer onSerialiseInlineComment(long tick, TickContainer tickContainer) {
+		// TODO Implement
+		return null;
+	}
+
 	@Override
 	public void onDeserialiseInlineComment(long tick, TickContainer container, PlaybackFileCommandContainer fileCommandContainer) {
 		if (fileCommandContainer.containsKey("hud")) {
diff --git a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java
index 0392245d..3894640c 100644
--- a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java
+++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java
@@ -236,6 +236,23 @@ protected List serialiseCameraAngle(VirtualCameraAngle subticks) {
 		return out;
 	}
 
+	@Override
+	protected String serialiseFileCommandsInline(List fileCommands) {
+		if (fileCommands == null) {
+			return null;
+		}
+		List serialisedCommands = new ArrayList<>();
+		for (PlaybackFileCommand command : fileCommands) {
+			if ("hud".equals(command.getName())) {
+				serialisedCommands.add(String.format("$hud %s", command.getArgs()[0].equals("true") ? "on" : "off"));
+			}
+			if ("label".equals(command.getName())) {
+				serialisedCommands.add(String.format("$info %s", command.getArgs().length == 0 ? "off" : String.join(" ", command.getArgs())));
+			}
+		}
+		return String.join(" ", serialisedCommands);
+	}
+
 	@Override
 	protected String serialiseFileCommandsEndline(List fileCommands) {
 		if (fileCommands == null) {
@@ -320,6 +337,32 @@ protected String splitInputRegex() {
 		return "^\\d+\\|(.*?)\\|(.*?)\\|(\\S*)~&";
 	}
 
+	@Override
+	protected String deserialiseFileCommandsInline(String comment, List deserialisedFileCommands) {
+		Matcher matcher = extract("\\$(.+?) (.+?)", comment);
+
+		// Iterate through all file commands and add each to the list
+		while (matcher.find()) {
+			String name = matcher.group(1);
+			String[] args = matcher.group(2).split(" ");
+
+			if ("hud".equals(name)) {
+				args[0] = "on".equals(args[0]) ? "true" : "false";
+			} else if ("info".equals(name)) {
+				name = "label";
+				args[0] = "off".equals(args[0]) ? "" : args[0];
+			}
+
+			if (processExtensions)
+				deserialisedFileCommands.add(new PlaybackFileCommand(name, args));
+
+			comment = matcher.replaceFirst("");
+			matcher.reset(comment);
+		}
+
+		return comment;
+	}
+
 	@Override
 	protected String deserialiseFileCommandsEndline(String comment, List deserialisedFileCommands) {
 		Matcher matcher = extract("Monitoring:(.+)", comment);
diff --git a/src/test/java/tasmod/playback/tasfile/builtin/AlphaFlavorTest.java b/src/test/java/tasmod/playback/tasfile/builtin/AlphaFlavorTest.java
index a8464575..e253a214 100644
--- a/src/test/java/tasmod/playback/tasfile/builtin/AlphaFlavorTest.java
+++ b/src/test/java/tasmod/playback/tasfile/builtin/AlphaFlavorTest.java
@@ -106,8 +106,8 @@ void testSerialiseHeader() {
 				+ "#StartPosition:" + "0.0,0.0,0.0,0.0,0.0" + "\n"
 				+ "#																											#\n"
 				+ "#StartSeed:" + 0);
-		expected.add("#############################################################################################################\n"
-				+ "#Comments start with \"//\" at the start of the line, comments with # will not be saved");
+		expected.add("#############################################################################################################");
+		expected.add("#Comments start with \"//\" at the start of the line, comments with # will not be saved");
 
 		assertIterableEquals(expected, actual);
 	}

From 1bcddf81cda6e3615d07cd782897fdf96da63a06 Mon Sep 17 00:00:00 2001
From: Scribble 
Date: Sat, 22 Mar 2025 14:35:59 +0100
Subject: [PATCH 15/16] [FileCommands] Fixed inline file commands not being
 serialised

- Add serialisation to alpha flavor
---
 .../filecommands/builtin/LabelFileCommandExtension.java  | 9 +++++++++
 .../builtin/OptionsFileCommandExtension.java             | 7 +++++--
 .../playback/tasfile/flavor/builtin/AlphaFlavor.java     | 5 +++++
 3 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/LabelFileCommandExtension.java b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/LabelFileCommandExtension.java
index f0176c1e..5bee6748 100644
--- a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/LabelFileCommandExtension.java
+++ b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/LabelFileCommandExtension.java
@@ -42,6 +42,15 @@ public String[] getFileCommandNames() {
 		return new String[] { "label" };
 	}
 
+	@Override
+	public PlaybackFileCommandContainer onSerialiseInlineComment(long tick, TickContainer tickContainer) {
+		PlaybackFileCommandContainer fileCommandContainer = new PlaybackFileCommandContainer();
+		if (label.get(tick).get("label") != null) {
+			fileCommandContainer = label.get(tick);
+		}
+		return fileCommandContainer;
+	}
+
 	@Override
 	public void onDeserialiseInlineComment(long tick, TickContainer container, PlaybackFileCommandContainer fileCommandContainer) {
 		if (fileCommandContainer.containsKey("label")) {
diff --git a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/OptionsFileCommandExtension.java b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/OptionsFileCommandExtension.java
index b2d4cd7f..10cbefb8 100644
--- a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/OptionsFileCommandExtension.java
+++ b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/OptionsFileCommandExtension.java
@@ -46,8 +46,11 @@ public String[] getFileCommandNames() {
 
 	@Override
 	public PlaybackFileCommandContainer onSerialiseInlineComment(long tick, TickContainer tickContainer) {
-		// TODO Implement
-		return null;
+		PlaybackFileCommandContainer fileCommandContainer = new PlaybackFileCommandContainer();
+		if (hud.get(tick).get("hud") != null) {
+			fileCommandContainer = hud.get(tick);
+		}
+		return fileCommandContainer;
 	}
 
 	@Override
diff --git a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java
index 3894640c..68bdf792 100644
--- a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java
+++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java
@@ -267,6 +267,11 @@ protected String serialiseFileCommandsEndline(List fileComm
 		return String.join(" ", serialisedCommands);
 	}
 
+	@Override
+	protected String serialiseInlineComment(String comment) {
+		return String.format("//%s", comment);
+	}
+
 	@Override
 	protected String serialiseEndlineComment(String comment) {
 		return String.format("//%s", comment);

From 97d5a131b64f1f6eab61cacdf9de27b960d7ef7d Mon Sep 17 00:00:00 2001
From: Scribble 
Date: Sun, 23 Mar 2025 14:28:20 +0100
Subject: [PATCH 16/16] [Flavor] FileCommand fixes and switching yaw and pitch
 in alpha

---
 .../filecommands/builtin/LabelFileCommandExtension.java   | 2 +-
 .../filecommands/builtin/OptionsFileCommandExtension.java | 2 +-
 .../playback/tasfile/flavor/builtin/AlphaFlavor.java      | 8 ++++----
 3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/LabelFileCommandExtension.java b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/LabelFileCommandExtension.java
index 5bee6748..d2f880b5 100644
--- a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/LabelFileCommandExtension.java
+++ b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/LabelFileCommandExtension.java
@@ -45,7 +45,7 @@ public String[] getFileCommandNames() {
 	@Override
 	public PlaybackFileCommandContainer onSerialiseInlineComment(long tick, TickContainer tickContainer) {
 		PlaybackFileCommandContainer fileCommandContainer = new PlaybackFileCommandContainer();
-		if (label.get(tick).get("label") != null) {
+		if (label.size() != 0 && label.get(tick).get("label") != null) {
 			fileCommandContainer = label.get(tick);
 		}
 		return fileCommandContainer;
diff --git a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/OptionsFileCommandExtension.java b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/OptionsFileCommandExtension.java
index 10cbefb8..8923ab2b 100644
--- a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/OptionsFileCommandExtension.java
+++ b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/builtin/OptionsFileCommandExtension.java
@@ -47,7 +47,7 @@ public String[] getFileCommandNames() {
 	@Override
 	public PlaybackFileCommandContainer onSerialiseInlineComment(long tick, TickContainer tickContainer) {
 		PlaybackFileCommandContainer fileCommandContainer = new PlaybackFileCommandContainer();
-		if (hud.get(tick).get("hud") != null) {
+		if (hud.size() != 0 && hud.get(tick).get("hud") != null) {
 			fileCommandContainer = hud.get(tick);
 		}
 		return fileCommandContainer;
diff --git a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java
index 68bdf792..81ced51b 100644
--- a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java
+++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/builtin/AlphaFlavor.java
@@ -232,7 +232,7 @@ protected List serialiseCameraAngle(VirtualCameraAngle subticks) {
 		 */
 		float pitch = subticks.getPitch() == null ? 0f : subticks.getPitch();
 		float yaw = subticks.getYaw() == null ? 0f : subticks.getYaw();
-		out.add("Camera:" + yaw + ";" + pitch);
+		out.add("Camera:" + pitch + ";" + yaw);
 		return out;
 	}
 
@@ -261,7 +261,7 @@ protected String serialiseFileCommandsEndline(List fileComm
 		List serialisedCommands = new ArrayList<>();
 		for (PlaybackFileCommand command : fileCommands) {
 			if ("desyncMonitor".equals(command.getName())) {
-				serialisedCommands.add(String.format("Monitoring:%s", String.join(" ", command.getArgs())));
+				serialisedCommands.add(String.format("Monitoring:%s 0", String.join(" ", command.getArgs())));
 			}
 		}
 		return String.join(" ", serialisedCommands);
@@ -508,8 +508,8 @@ protected VirtualCameraAngle deserialiseCameraAngle(List cameraAngleStri
 			Matcher matcher = extract("Camera:(.+?);(.+)", line);
 
 			if (matcher.find()) {
-				String cameraYawString = matcher.group(1);
-				String cameraPitchString = matcher.group(2);
+				String cameraYawString = matcher.group(2);
+				String cameraPitchString = matcher.group(1);
 
 				Float cameraYaw = null;
 				Float cameraPitch = null;