diff --git a/build.gradle b/build.gradle index 52d14f78..2ad1cb6c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,10 +1,10 @@ plugins { // loom plugin - id 'fabric-loom' version "${loom_version}" + id "fabric-loom" version "${loom_version}" // legacy looming (loom plugin improvements) - id 'legacy-looming' version "${loom_version}" - id 'com.palantir.git-version' version '3.1.0' - id 'com.diffplug.spotless' version '7.0.3' + id "legacy-looming" version "${loom_version}" + id "com.palantir.git-version" version "3.1.0" + id "com.diffplug.spotless" version "7.0.3" } @@ -23,24 +23,23 @@ java { loom { // set access widener - accessWidenerPath = file('src/main/resources/tasmod.accesswidener') + accessWidenerPath = file("src/main/resources/tasmod.accesswidener") // add log4jconfig - log4jConfigs.from(file('src/main/resources/log4j.xml')) + log4jConfigs.from(file("src/main/resources/log4j.xml")) } // dependency repositories repositories { mavenCentral() - maven { url = 'https://raw.githubusercontent.com/BleachDev/cursed-mappings/main/' } + maven { url = "https://maven.minecrafttas.com/main" } + //maven { url = "https://maven.minecrafttas.com/snapshots" } + maven { url = "https://raw.githubusercontent.com/BleachDev/cursed-mappings/main/" } maven { url = "https://jitpack.io" } - maven { url = "https://maven.mgnet.work/main" } - maven { url = 'https://repo.spongepowered.org/maven' } + maven { url = "https://repo.spongepowered.org/maven" } } // dependency configurations configurations { - // non-transitive download mod dependency - downloadMod.transitive = false // embed dependency included in build implementation.extendsFrom(embed) } @@ -48,25 +47,19 @@ configurations { // dependencies dependencies { // tasmod dependencies - embed group: 'com.dselent', name: 'bigarraylist', version: '1.1' - //compileOnly group: 'com.minecrafttas', name: 'killtherng', version: '2.0' - //downloadMod group: 'com.minecrafttas', name: 'killtherng-full', version: '2.0' // for downloadKTRNG task + embed "com.dselent:bigarraylist:1.6" + embed "com.github.KaptainWutax:SeedUtils:b6a383113c" // loom dependencies minecraft "com.mojang:minecraft:${project.minecraft_version}" mappings "net.legacyfabric:yarn:${project.minecraft_version}+build.mcp" modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" - testImplementation 'org.junit.jupiter:junit-jupiter:5.13.3' - testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + + // testing dependencies + testImplementation "org.junit.jupiter:junit-jupiter:5.13.3" + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } -// task for downloading KillTheRng -//task downloadKTRNG(type: Copy) { - //group 'tasmod' - //description 'Download KillTheRNG to the run/mods/ folder of the project' - //from configurations.downloadMod - //into 'run/mods/' -//} compileJava { options.release = 8 @@ -78,7 +71,7 @@ processResources { inputs.property "mcversion", project.minecraft_version filesMatching("fabric.mod.json") { - expand 'mod_url': project.mod_url, 'name': project.mod_name, 'mod_version': project.version, 'mod_description': project.mod_description, 'mod_sources': project.mod_sources, 'mod_email': project.mod_email + expand "mod_url": project.mod_url, "name": project.mod_name, "mod_version": project.version, "mod_description": project.mod_description, "mod_sources": project.mod_sources, "mod_email": project.mod_email } } @@ -95,7 +88,7 @@ jar { } // configure testing -tasks.named('test', Test) { +tasks.named("test", Test) { useJUnitPlatform() testLogging { @@ -104,11 +97,11 @@ tasks.named('test', Test) { } spotless { - encoding 'UTF-8' - lineEndings 'UNIX' - java { - importOrderFile('formatter/TASmodImportorder.txt') - eclipse().configFile('formatter/TASmodFormatter.xml') - } - enforceCheck false + encoding "UTF-8" + lineEndings "UNIX" + java { + importOrderFile("formatter/TASmodImportorder.txt") + eclipse().configFile("formatter/TASmodFormatter.xml") + } + enforceCheck false } \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 7b7f82d9..cdc97026 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,9 +2,9 @@ pluginManagement { // add repositories for plugins repositories { - maven { url = 'https://maven.mgnet.work/main' } - maven { url = 'https://maven.fabricmc.net/' } - maven { url = 'https://repo.legacyfabric.net/repository/legacyfabric/' } + maven { url = "https://maven.minecrafttas.com/main" } + maven { url = "https://maven.fabricmc.net/" } + maven { url = "https://repo.legacyfabric.net/repository/legacyfabric/" } mavenCentral() gradlePluginPortal() } diff --git a/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java b/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java index 07684e67..4c8a9e55 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java @@ -9,8 +9,6 @@ import static com.minecrafttas.tasmod.registries.TASmodPackets.PLAYBACK_SAVE; import static com.minecrafttas.tasmod.registries.TASmodPackets.PLAYBACK_STATE; -import java.io.File; -import java.io.IOException; import java.io.Serializable; import java.nio.ByteBuffer; import java.nio.file.Path; @@ -51,7 +49,6 @@ import com.minecrafttas.tasmod.playback.tasfile.PlaybackSerialiser; import com.minecrafttas.tasmod.playback.tasfile.exception.PlaybackLoadException; import com.minecrafttas.tasmod.playback.tasfile.exception.PlaybackSaveException; -import com.minecrafttas.tasmod.playback.tasfile.flavor.SerialiserFlavorBase; import com.minecrafttas.tasmod.registries.TASmodConfig; import com.minecrafttas.tasmod.registries.TASmodPackets; import com.minecrafttas.tasmod.util.Ducks.GuiScreenDuck; @@ -572,7 +569,7 @@ public void setInputs(BigArrayList inputs) { public void setInputs(BigArrayList inputs, long index) { clearInputList(); - SerialiserFlavorBase.addAll(this.inputs, inputs); + this.inputs.addAll(inputs); setIndex(index); } @@ -616,12 +613,7 @@ public void clear() { } private void clearInputList() { - try { - inputs.clearMemory(); - } catch (IOException e) { - e.printStackTrace(); - } - inputs = new BigArrayList(tasFileDirectory + File.separator + "temp"); + inputs.clear(); } public VirtualKeyboard getNextPlaybackKeyboard() { 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 510473e7..b3d8d45e 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/PlaybackFileCommand.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/PlaybackFileCommand.java @@ -11,19 +11,37 @@ import com.minecrafttas.mctcommon.file.AbstractDataFile; import com.minecrafttas.mctcommon.registry.Registerable; import com.minecrafttas.tasmod.TASmodClient; +import com.minecrafttas.tasmod.commands.CommandFileCommand; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient; import com.minecrafttas.tasmod.playback.PlaybackControllerClient.InputContainer; +import com.minecrafttas.tasmod.playback.tasfile.PlaybackSerialiser; import com.minecrafttas.tasmod.playback.tasfile.flavor.SerialiserFlavorBase; public class PlaybackFileCommand { + /** + * The name of the fileCommand + */ private String name; + /** + * The arguments of the fileCommand + */ private String[] args; + /** + * Creates a new FileCommand with no arguments + * @param name The {@link #name} + */ public PlaybackFileCommand(String name) { this(name, (String[]) null); } + /** + * Creates a new FileCommand + * @param name The {@link #name} + * @param args The {@link #args}r + */ public PlaybackFileCommand(String name, String... args) { if (args == null) { args = new String[] {}; @@ -32,10 +50,16 @@ public PlaybackFileCommand(String name, String... args) { this.args = args; } + /** + * @return {@link #name} + */ public String getName() { return name; } + /** + * @return {@link #args} + */ public String[] getArgs() { return args; } @@ -94,6 +118,12 @@ public PlaybackFileCommandExtension(String tempFolderName) { this(TASmodClient.tasfiledirectory.resolve("temp").resolve(tempFolderName)); } + /** + *

Creates a FileCommandExtension and creates a temp folder with
+ * at the specified path for the {@link BigArrayList} files + * + * @param tempFolderName The name of the temp folder + */ public PlaybackFileCommandExtension(Path tempDirectory) { if (tempDirectory == null) { tempDir = null; @@ -112,33 +142,87 @@ public PlaybackFileCommandExtension(Path tempDirectory) { endlineFileCommandStorage = new BigArrayList<>(tempDir.toString()); } + /** + * Whether this extension is enabled.
+ * Can be changed e.g. via the {@link CommandFileCommand} command + */ protected boolean enabled = false; + /** + *

The names of all file commands that should be handled by this extension + *

Imagine having the following file commands in the playback file: + *

+		 * // $desyncMonitor(13, 0, 1, 1, 1, 1); $hud(true);
+		 * 
+ * And you want to support the hud file command with an extension,
+ * then you must return a string array with the name: + *
+		 * public String[] getFileCommandNames() {
+		 * 	return new String[]{"hud"};
+		 * }
+		 * 
+ * Now, methods like {@link #onDeserialiseEndlineComment(long, InputContainer, SortedFileCommandContainer) onDeserialiseEndlineComment} + * will only have a {@link SortedFileCommandContainer}
+ * with the "hud" {@link FileCommandsInTickList} as a parameter. "desyncMonitor" will be ignored. + *

+ * To also include "desyncMonitor" in the {@link SortedFileCommandContainer}, simply add that name to the array: + *

+		 * public String[] getFileCommandNames() {
+		 * 	return new String[]{"hud", "desyncMonitor"};
+		 * }
+		 * 
+ * + * @return A string array of file command names + */ public abstract String[] getFileCommandNames(); + /** + * Fired when using the {@link CommandFileCommand} and setting this extension to enabled + */ public void onEnable() { }; + /** + * Fired when using the {@link CommandFileCommand} and setting this extension to disabled + */ public void onDisable() { }; + /** + * Fired when {@link PlaybackControllerClient#clear()} is called
+ * Make sure to call super.onClear() as it clears the {@link BigArrayLists} in this extension! + */ public void onClear() { - try { - inlineFileCommandStorage.clearMemory(); - endlineFileCommandStorage.clearMemory(); - } catch (IOException e) { - e.printStackTrace(); - } - inlineFileCommandStorage = new BigArrayList<>(); - endlineFileCommandStorage = new BigArrayList<>(); + inlineFileCommandStorage.clear(); + endlineFileCommandStorage.clear(); }; + /** + * Fired when the {@link PlaybackControllerClient} is recording inputs
+ * Usually used to generate new FileCommands during recording. + * @param tick The current tick in the recording + * @param inputContainer The current inputs in the recording + */ public void onRecord(long tick, InputContainer inputContainer) { }; + /** + * Fired when the {@link PlaybackControllerClient} is playing back inputs.
+ * Usually used to generate new FileCommands during playback. + * @param tick The current tick in the playback + * @param inputContainer The current inputs in the playback + */ public void onPlayback(long tick, InputContainer inputContainer) { }; + /** + * Fired when the {@link PlaybackSerialiser} writes the inputs to a file.
+ * This is used to store your inlineFileCommands into an comment. + * + * @param tick The current tick that is serialised + * @param inputContainer The current inputs that are being serialised + * @return A {@link SortedFileCommandContainer} with your filecommands for one container that you want serialised + */ public SortedFileCommandContainer onSerialiseInlineComment(long tick, InputContainer inputContainer) { SortedFileCommandContainer out = new SortedFileCommandContainer(); if (tick >= inlineFileCommandStorage.size()) @@ -155,6 +239,14 @@ public SortedFileCommandContainer onSerialiseInlineComment(long tick, InputConta return out; } + /** + * Fired when the {@link PlaybackSerialiser} writes the inputs to a file.
+ * This is used to store your endlineFileCommands into an comment. + * + * @param tick The current tick that is serialised + * @param inputContainer The current inputs that are being serialised + * @return A {@link SortedFileCommandContainer} with your filecommands for one container that you want serialised + */ public SortedFileCommandContainer onSerialiseEndlineComment(long tick, InputContainer inputContainer) { SortedFileCommandContainer out = new SortedFileCommandContainer(); if (tick >= endlineFileCommandStorage.size()) @@ -171,24 +263,47 @@ public SortedFileCommandContainer onSerialiseEndlineComment(long tick, InputCont return out; } - public void onDeserialiseInlineComment(long tick, InputContainer container, SortedFileCommandContainer fileCommandContainer) { + /** + * Fired when the {@link PlaybackSerialiser} reads the inputs from a file.
+ * This is used to load your inlineFileCommands from a comment into {@link #inlineFileCommandStorage} to be used in {@link #onPlayback(long, InputContainer)}. + * + * @param tick The current tick that is deserialised + * @param inputContainer The current inputs that are being deserialised + * @param fileCommandContainer The {@link SortedFileCommandContainer} that was deserialised + */ + public void onDeserialiseInlineComment(long tick, InputContainer inputContainer, SortedFileCommandContainer fileCommandContainer) { if (fileCommandContainer == null) return; inlineFileCommandStorage.add(fileCommandContainer); } - public void onDeserialiseEndlineComment(long tick, InputContainer container, SortedFileCommandContainer fileCommandContainer) { + /** + * Fired when the {@link PlaybackSerialiser} reads the inputs from a file.
+ * This is used to load your endlineFileCommands from a comment into {@link #endlineFileCommandStorage} to be used in {@link #onPlayback(long, InputContainer)}. + * + * @param tick The current tick that is deserialised + * @param inputContainer The current inputs that are being deserialised + * @param fileCommandContainer The {@link SortedFileCommandContainer} that was deserialised + */ + public void onDeserialiseEndlineComment(long tick, InputContainer inputContainer, SortedFileCommandContainer fileCommandContainer) { if (fileCommandContainer == null) return; endlineFileCommandStorage.add(fileCommandContainer); } + /** + * @return {@link #enabled} + */ public boolean isEnabled() { return enabled; } + /** + * Set {@link #enabled} and run {@link #onEnable()} and {@link #onDisable()} + * @param enabled Sets {@link #enabled} + */ public void setEnabled(boolean enabled) { if (enabled) onEnable(); @@ -239,8 +354,8 @@ public static class FileCommandsInCommentList extends ArrayListThis would translate into an ArrayList like *
 	 * [
-	 * 	[$desyncMonitor(13, 0, 1, 1, 1, 1);, $hud(true);]	<- One {@link FileCommandsInCommentList}
-	 * 	[$desyncMonitor(16, 3, 1, 1, 1, 1);, $hud(false);]
+	 * 	[$desyncMonitor(13, 0, 1, 1, 1, 1);, $hud(true);],	<- One {@link FileCommandsInCommentList}
+	 * 	[$desyncMonitor(16, 3, 1, 1, 1, 1);, $hud(false);],
 	 * 	[$label(Test);, $hud(false);]
 	 * ]
 	 * 
@@ -259,6 +374,28 @@ public SortedFileCommandContainer sort() { /* * Fill the HashMap in SortedFileCommandContainer with empty FileCommandsInCommentList * for each different FileCommand name found in this UnsortedFileCommandContainer. + * + * We have to do this, since absent FileCommands are set to null. + * + * Example: + * // $desyncMonitor(13, 0, 1, 1, 1, 1); $hud(true); + * // $desyncMonitor(16, 3, 1, 1, 1, 1); $hud(false); + * // $label(Test); $hud(false); + * + * In line 1, the "label" FC is missing + * In line 2, once again, "label is missing + * In line 3, desyncMonitor is missing. + * + * If it's missing, we need to set that spot to null. + * + * So first we create empty Hashmaps for each: + * { + * "desyncMonitor": [], + * "hud": [], + * "label": [] + * } + * + * Then iterate through all filecommands and set null where a FileCommand is absent */ for (FileCommandsInCommentList unsortedFileCommandsList : this) { if (unsortedFileCommandsList != null) { @@ -268,7 +405,7 @@ public SortedFileCommandContainer sort() { } } - /** + /* * Add the FileCommands to the previously created FileCommandsInCommentLists */ for (FileCommandsInCommentList unsortedFileCommandsList : this) { @@ -285,7 +422,7 @@ public SortedFileCommandContainer sort() { boolean valuePresent = false; if (unsortedFileCommandsList != null) { - /** + /* * Iterates through all filecommands in a comment * and adds it to the sorted list if found */ @@ -296,7 +433,7 @@ public SortedFileCommandContainer sort() { } } } - /** + /* * If the value is not found, * add null to indicate that the * file command is missing from this comment @@ -336,9 +473,10 @@ public static class FileCommandsInTickList extends ArrayListA LinkedHashMap for storing {@link FileCommandsInCommentList} sorted by the name of the FileCommand name. + *

A LinkedHashMap for storing {@link FileCommandsInTickList} sorted by the name of the FileCommand name. *

The key represents the FileCommand name, while the elements are the {@link FileCommandsInTickList} *

This stands in contrast to the {@link UnsortedFileCommandContainer}, which can be obtained by calling {@link SortedFileCommandContainer#unsort() unsort()} + *

Used in {@link PlaybackFileCommandExtension PlaybackFileCommandExtensions} as this format makes it easier to distribute the file commands to their respective class extensions *

Example
*
 	 * // $desyncMonitor(13, 0, 1, 1, 1, 1); $hud(true);
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 aa78fc36..1a383e98 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.IOException;
 import java.io.Serializable;
 import java.nio.file.Path;
 import java.text.NumberFormat;
@@ -356,12 +355,7 @@ private double parseDouble(String doublestring) throws ParseException {
 	@Override
 	public void onClear() {
 		currentValues = null;
-		try {
-			monitorContainer.clearMemory();
-		} catch (IOException e) {
-			e.printStackTrace();
-		}
-		monitorContainer = new BigArrayList();
+		monitorContainer.clear();
 		lastStatus = TextFormatting.GRAY + "Empty";
 		lastPos = "";
 		lastMotion = "";
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 06e838d3..876be822 100644
--- a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiser.java
+++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiser.java
@@ -16,6 +16,7 @@
 import com.minecrafttas.tasmod.playback.tasfile.exception.PlaybackSaveException;
 import com.minecrafttas.tasmod.playback.tasfile.flavor.SerialiserFlavorBase;
 import com.minecrafttas.tasmod.registries.TASmodAPIRegistry;
+import com.minecrafttas.tasmod.savestates.SavestateHandlerClient;
 import com.minecrafttas.tasmod.util.FileThread;
 
 /**
@@ -25,6 +26,10 @@
  */
 public class PlaybackSerialiser {
 
+	/**
+	 * The default flavor name used for saving TASfiles when no flavor is specified,
+ * used in {@link #saveToFile(Path, BigArrayList, String, long)} + */ private static String defaultFlavor = "beta1"; /** @@ -112,8 +117,8 @@ public static void saveToFile(Path path, BigArrayList container, } BigArrayList tickLines = flavor.serialise(container, stopIndex); - for (long i = 0; i < tickLines.size(); i++) { - writerThread.addLine(tickLines.get(i)); + for (String tickLine : tickLines) { + writerThread.addLine(tickLine); } writerThread.close(); @@ -132,6 +137,19 @@ public static BigArrayList loadFromFile(Path file) throws Playba return loadFromFile(file, true); } + /** + * Loads a BigArrayList of {@link InputContainer InputContainers} from a file.
+ * Tries to determine the {@link SerialiserFlavorBase flavor} by reading the header of the TASfile + * + *

Has a parameter to stop processing extension data, usually used when + * {@link SavestateHandlerClient#loadstate(String) loading savestates} to prevent loadstates from overwriting data + * + * @param file The file to load from + * @param processExtensions Sets {@link SerialiserFlavorBase#processExtensions} + * @return The loaded BigArrayList of {@link InputContainer InputContainers} + * @throws PlaybackLoadException If the file contains errors + * @throws IOException If the file could not be read + */ public static BigArrayList loadFromFile(Path file, boolean processExtensions) throws PlaybackLoadException, IOException { if (file == null) { throw new PlaybackLoadException("Load from file failed. No file specified"); @@ -160,6 +178,19 @@ public static BigArrayList loadFromFile(Path file, String flavor return loadFromFile(file, flavorName, true); } + /** + * Loads a BigArrayList of {@link InputContainer InputContainers} from a file, with a specific flavor + * + *

Has a parameter to stop processing extension data, usually used when + * {@link SavestateHandlerClient#loadstate(String) loading savestates} to prevent loadstates from overwriting data + * + * @param file The file to load from + * @param flavorName The name of the {@link SerialiserFlavorBase flavor} to use. If the detected flavor in the TASfile mismatches, a {@link PlaybackLoadException} is thrown + * @param processExtensions Sets {@link SerialiserFlavorBase#processExtensions} + * @return The loaded BigArrayList of {@link InputContainer InputContainers} + * @throws PlaybackLoadException If the file contains errors + * @throws IOException If the file could not be read + */ public static BigArrayList loadFromFile(Path file, String flavorName, boolean processExtensions) throws PlaybackLoadException, IOException { // If the flavor is null or empty, try to determine the flavor by reading the header @@ -261,7 +292,7 @@ public static SerialiserFlavorBase readFlavor(Path file) throws PlaybackLoadExce List lines = new ArrayList<>(); String line = null; - // Reads the first 100 lines + // Reads the first 100 lines to search for the flavor for (int i = 0; i < 100; i++) { line = reader.readLine(); 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 937af70d..97253cdb 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 @@ -1,6 +1,5 @@ package com.minecrafttas.tasmod.playback.tasfile.flavor; -import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; @@ -53,7 +52,7 @@ * but also a list of enabled extensions and the name of the flavor that was used to encode the file. * *

  • - * Content
    + * Container
    * Contains the actual inputs per tick, inputs in a subtick (a.k.a in a frame), comments and other extensions. *
  • * @@ -122,10 +121,10 @@ public abstract class SerialiserFlavorBase implements Registerable { ==============================================*/ /* - _ _ ____ __ ____ ____ ____ - ( )_( )( ___) /__\ ( _ \( ___)( _ \ - ) _ ( )__) /(__)\ )(_) ))__) ) / - (_) (_)(____)(__)(__)(____/(____)(_)\_) + _ _ ____ __ ____ ____ ____ + / )( \( __) / _\ ( \( __)( _ \ + ) __ ( ) _) / \ ) D ( ) _) ) / + \_)(_/(____)\_/\_/(____/(____)(__\_) */ @@ -134,6 +133,7 @@ public abstract class SerialiserFlavorBase implements Registerable { *
     	 * ##################### TASfile ####################
     	 * 
    + * * @return The very top of the header */ protected String headerStart() { @@ -146,6 +146,7 @@ protected String headerStart() { *
     	 * ##################################################
     	 * 
    + * * @return The end of the header */ protected String headerEnd() { @@ -186,6 +187,7 @@ protected String headerEnd() { * * ################################################## // {@link #headerEnd()} *
    + * * @return List of lines containing the header */ public List serialiseHeader() { @@ -219,6 +221,7 @@ protected void serialiseFlavorName(List out) { *
     	 * FileCommand-Extensions: tasmod_label@v1, tasmod_desyncMonitor@v1
     	 * 
    + * * @param out The serialised lines, passed by reference */ protected void serialiseEnabledFileCommandNames(List out) { @@ -248,6 +251,7 @@ protected void serialiseEnabledFileCommandNames(List out) { * pitch:29.25007 * yaw:-88.80094 * + * * @param out */ protected void serialiseMetadata(List out) { @@ -285,6 +289,7 @@ protected void serialiseMetadataName(List out, String name) { * Playing Time:00:00.0 * Rerecords:0 * + * * @param out * @param data */ @@ -295,10 +300,10 @@ protected void serialiseMetadataValues(List out, LinkedHashMap 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 * @return The list of lines @@ -379,6 +385,7 @@ protected void serialiseContainer(BigArrayList out, InputContainer conta * A;a * S,D;sd * + * * @param keyboard The keyboard to serialise * @return A list of serialised keyboardSubticks */ @@ -406,6 +413,7 @@ protected List serialiseKeyboard(VirtualKeyboard keyboard) { *
     	 * 	W,S;ws
     	 * 
    + * * @param keyboardSubtick The subtick to serialise * @return The serialised subtick */ @@ -413,6 +421,23 @@ protected String serialiseKeyboardSubtick(VirtualKeyboard keyboardSubtick) { return String.format("%s;%s", String.join(",", keyboardSubtick.getCurrentPresses()), charListToString(keyboardSubtick.getCharList())); } + /** + *

    Utility method for converting a char list to a string + *

    Replaces line break characters with \\n + * + * @param charList The list to use + * @return The created string + */ + 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; + } + /** *

    Serialises a {@link VirtualMouse} *

    A {@link VirtualMouse} is most often comprised of multiple subticks,
    @@ -423,6 +448,7 @@ protected String serialiseKeyboardSubtick(VirtualKeyboard keyboardSubtick) { * RC;-15,15,21 * RC,MC;30,14,20 * + * * @param mouse The mouse to serialise * @return A list of serialised mouse subticks */ @@ -446,6 +472,7 @@ protected List serialiseMouse(VirtualMouse mouse) { *

     	 * 	LC;0,15,21
     	 * 
    + * * @param mouseSubtick The mouse subtick to serialise * @return The serialised mouse subtick */ @@ -463,6 +490,7 @@ protected String serialiseMouseSubtick(VirtualMouse mouseSubtick) { * 34;25 * 140;-130 * + * * @param cameraAngle Camera angle to serialise * @return The serialised list of camera angles */ @@ -505,6 +533,7 @@ protected String serialiseCameraAngleSubtick(VirtualCameraAngle cameraAngleSubti * // 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 @@ -565,6 +594,7 @@ protected List serialiseInlineComments(List inlineComments, Unso *
     	 * // Inline comment
     	 * 
    + * * @param comment Content in the comment * @return The inline comment */ @@ -578,6 +608,7 @@ protected String serialiseInlineComment(String comment) { *
     	 *	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 @@ -638,6 +669,7 @@ protected List serialiseEndlineComments(List endlineComments, Un *
     	 * // Endline comment
     	 * 
    + * * @param comment Content in the comment * @return The endline comment */ @@ -654,6 +686,7 @@ protected String serialiseEndlineComment(String comment) { *
     	 * 	// $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 */ @@ -678,6 +711,7 @@ protected String serialiseFileCommandsInline(FileCommandsInCommentList fileComma *
     	 * 	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 */ @@ -694,6 +728,7 @@ protected String serialiseFileCommandsEndline(FileCommandsInCommentList fileComm * * *

    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 */ @@ -713,6 +748,7 @@ protected String serialiseFileCommand(PlaybackFileCommand fileCommand) { * 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)} @@ -728,7 +764,7 @@ protected void mergeInputs(BigArrayList out, List serialisedKeyb * // This is an inline comment * 1|||0;0 */ - addAll(out, serialisedInlineComments); + out.addAll(serialisedInlineComments); /* * Copy inputs with ticks and subticks into a queue, @@ -804,6 +840,7 @@ protected String mergeInput(long currentTick, String keyboard, String mouse, Str * 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 @@ -817,6 +854,7 @@ protected String mergeSubtickInput(int currentSubtick, String keyboard, String m /** * If a string is null, return an empty string + * * @param string String to check * @return The string or empty if null */ @@ -871,16 +909,17 @@ protected String joinNotEmpty(String delimiter, String... args) { */ /* - _ _ ____ __ ____ ____ ____ - ( )_( )( ___) /__\ ( _ \( ___)( _ \ - ) _ ( )__) /(__)\ )(_) ))__) ) / - (_) (_)(____)(__)(__)(____/(____)(_)\_) + _ _ ____ __ ____ ____ ____ + / )( \( __) / _\ ( \( __)( _ \ + ) __ ( ) _) / \ ) D ( ) _) ) / + \_)(_/(____)\_/\_/(____/(____)(__\_) */ /** *

    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 */ @@ -899,6 +938,7 @@ public boolean checkFlavorName(List 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 @@ -927,6 +967,7 @@ public List extractHeader(BigArrayList lines) { * ├── {@link #deserialiseMetadata(List)} * └── {@link #deserialiseEnabledFileCommandNames(List)} * + * * @param headerLines The header lines to deserialise * @see #serialiseHeader() */ @@ -937,6 +978,7 @@ public void deserialiseHeader(List headerLines) { /** *

    Deserialises the TASfile metadata + * * @param headerLines * @see #serialiseMetadata(List) */ @@ -978,6 +1020,7 @@ protected void deserialiseMetadata(List headerLines) { /** *

    Deserialises file command extension names and enables them + * * @param headerLines The header lines to search * @see #serialiseEnabledFileCommandNames(List) * @throws PlaybackLoadException If the "FileCommand-Extensions" keyword is not found in the header @@ -1004,6 +1047,14 @@ protected void deserialiseEnabledFileCommandNames(List headerLines) { throw new PlaybackLoadException("FileCommand-Extensions value was not found in the header"); } + /* + ___ __ __ _ ____ __ __ __ _ ____ ____ + / __)/ \ ( ( \(_ _)/ _\ ( )( ( \( __)( _ \ + ( (__( O )/ / )( / \ )( / / ) _) ) / + \___)\__/ \_)__) (__)\_/\_/(__)\_)__)(____)(__\_) + + */ + /** *

    Deserialises the input part of the TASfile * @@ -1014,13 +1065,14 @@ protected void deserialiseEnabledFileCommandNames(List headerLines) { * ├── {@link #deserialiseMultipleInlineComments(List, UnsortedFileCommandContainer)} * │ └── {@link #deserialiseInlineComment(String, FileCommandsInCommentList)} * │ └── {@link #deserialiseFileCommandsInline(String, FileCommandsInCommentList)} - * ├── {@link #splitInputs(List, List, List, List, List, List)} + * ├── {@link #splitTickLines(List, List, List, List, List, List)} * │ └── {@link #deserialiseEndlineComment(String, FileCommandsInCommentList)} * │ └── {@link #deserialiseFileCommandsEndline(String, FileCommandsInCommentList)} * ├── {@link #deserialiseKeyboard(List)} * ├── {@link #deserialiseMouse(List)} * └── {@link #deserialiseCameraAngle(List)} * + * * @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 InputContainer InputContainers} @@ -1044,6 +1096,13 @@ public BigArrayList deserialise(BigArrayList lines, long return out; } + /** + *

    Extract phases for a tick container. + *

    A container has a certain order.
    + * This enum contains phases that are updated when extracting and verifying a container in {@link SerialiserFlavorBase#extractContainer(List, BigArrayList, long)} + * + * @author Scribble + */ protected enum ExtractPhases { /** * Inline comment phase. @@ -1092,8 +1151,9 @@ protected enum ExtractPhases { /** *

    * Extracts all the lines corresponding to one tick+subticks a.k.a one - * "container" from the incoming lines.
    - * The extracted ticks are easier to process than using a huge list.
    + * {@link InputContainer "InputcContainer"} from the incoming lines.
    + * The extracted containers are easier to process than using a huge list.
    + * Furthermore, this method ensures the correct formatting of the lines. *

    * A container has multiple parts to it, that are split into * {@link ExtractPhases}
    @@ -1147,6 +1207,7 @@ protected enum ExtractPhases { * @param lines The line list * @param startPos The start position of this tick * @return The updated index for the next tick + * @throws PlaybackLoadException When the order of phases is wrong */ protected long extractContainer(List extracted, BigArrayList lines, long startPos) { ExtractPhases phase = ExtractPhases.NONE; @@ -1206,50 +1267,94 @@ protected long extractContainer(List extracted, BigArrayList lin return startPos + counter - 1; } + /** + * Main deserialising method of a single {@link InputContainer} + * + * In each step, incoming lines are broken down to it's components: + * + *

      + *
    1. Lines are split in inline comments and ticks
    2. + *
    3. Inline comments are processed
    4. + *
    5. Tick lines are split into keyboard, mouse, cameraAngle, endlineComments, endlineFileCommands
    6. + *
    7. All components from the previous step get deserialised into actual objects
    8. + *
    9. Optional if {@link #processExtensions} is true: Update FileCommands
    10. + *
    + * + * @param out The list of {@link InputContainers}, passed in by reference + * @param containerLines The lines to deserialise + */ protected void deserialiseContainer(BigArrayList out, List containerLines) { - + // Split lines into comments and ticks List inlineComments = new ArrayList<>(); List tickLines = new ArrayList<>(); splitContainer(containerLines, inlineComments, tickLines); + // Process inline comments UnsortedFileCommandContainer inlineFileCommands = new UnsortedFileCommandContainer(); deserialiseMultipleInlineComments(inlineComments, inlineFileCommands); + // Split ticks into components List keyboardStrings = new ArrayList<>(); List mouseStrings = new ArrayList<>(); List cameraAngleStrings = new ArrayList<>(); List endlineComments = new ArrayList<>(); UnsortedFileCommandContainer endlineFileCommands = new UnsortedFileCommandContainer(); + splitTickLines(tickLines, keyboardStrings, mouseStrings, cameraAngleStrings, endlineComments, endlineFileCommands); - splitInputs(tickLines, keyboardStrings, mouseStrings, cameraAngleStrings, endlineComments, endlineFileCommands); - + /* + * The previous step splits everything into multiple lists. + * However, the process makes it so every list has the same number of elements. + * While this is true for keyboard, mouse and camera, + * endlineComments will have empty lines. + * + * Ideally we want to remove all empty lines, + * however comments do allow empty lines between ticks. + * Therefore we remove empty lines, but starting from the back of the list + */ pruneListEndNull(endlineComments); + // Deserialise each component VirtualKeyboard keyboard = deserialiseKeyboard(keyboardStrings); VirtualMouse mouse = deserialiseMouse(mouseStrings); VirtualCameraAngle cameraAngle = deserialiseCameraAngle(cameraAngleStrings); - CommentContainer comments = new CommentContainer(inlineComments, endlineComments); + CommentContainer comments = new CommentContainer(inlineComments, endlineComments); // Comments don't need deserialisation InputContainer deserialisedContainer = new InputContainer(keyboard, mouse, cameraAngle, comments); + // Update FileCommands if (processExtensions) { TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.handleOnDeserialiseInline(currentTick, deserialisedContainer, inlineFileCommands); TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.handleOnDeserialiseEndline(currentTick, deserialisedContainer, endlineFileCommands); } + // Set the previous input container, used for relative coordinates previousInputContainer = deserialisedContainer; out.add(deserialisedContainer); } + /** + * @return The regex used for detecting inline comments + */ + protected String inlineComment() { + return "^//"; + } + /** * Splits container into inline comments and ticks. * - * @param lines + *
    +	 * // This is an inline comment
    +	 * 57|W,LCONTROL;w|;0,887,626|17.85;-202.74799 // This is an endline comment, but that is still part of a tick and processed later
    +	 * 
    + * + * @param lines The lines to process + * @param inlineComments The list to add the inline comments + * @param ticks The list to add the ticks to */ protected void splitContainer(List lines, List inlineComments, List ticks) { for (String line : lines) { - if (contains(singleComment(), line)) { + if (contains(inlineComment(), line)) { inlineComments.add(line); } else { ticks.add(line); @@ -1257,6 +1362,12 @@ protected void splitContainer(List lines, List inlineComments, L } } + /** + * Deserialises a list of inline comments into comments and inline file commands + * + * @param inlineComments The comments to deserialise. Will contain comments without filecommands + * @param inlineFileCommands The {@link UnsortedFileCommandContainer} passed in by reference + */ protected void deserialiseMultipleInlineComments(List inlineComments, UnsortedFileCommandContainer inlineFileCommands) { for (int i = 0; i < inlineComments.size(); i++) { FileCommandsInCommentList deserialisedFileCommands = new FileCommandsInCommentList(); @@ -1271,6 +1382,20 @@ protected void deserialiseMultipleInlineComments(List inlineComments, Un } } + /** + * Processes an inline comment + * + * It + *
      + *
    1. Extracts any inlineFileCommands
    2. + *
    3. Removes the leading // from the comment
    4. + *
    5. Trims the whitespaces
    6. + *
    + * + * @param comment The inline comment to process + * @param deserialisedFileCommands The {@link FileCommandsInCommentList}. Passed in by reference + * @return The processed comment. Null if comment is empty + */ protected String deserialiseInlineComment(String comment, FileCommandsInCommentList deserialisedFileCommands) { comment = deserialiseFileCommandsInline(comment, deserialisedFileCommands); comment = extract("^// ?(.+)", comment, 1); @@ -1283,6 +1408,27 @@ protected String deserialiseInlineComment(String comment, FileCommandsInCommentL return comment; } + /** + * @return The regex used for detecting endline comments + */ + protected String endlineComment() { + return "(//.+)"; + } + + /** + * Processes an endline comment + * + * It + *
      + *
    1. Extracts any endlineFileCommands
    2. + *
    3. Removes the leading // from the comment
    4. + *
    5. Trims the whitespaces
    6. + *
    + * + * @param comment The endline comment to process + * @param deserialisedFileCommands The {@link FileCommandsInCommentList}. Passed in by reference + * @return The processed comment. Null if comment is empty + */ protected String deserialiseEndlineComment(String comment, FileCommandsInCommentList deserialisedFileCommands) { comment = deserialiseFileCommandsEndline(comment, deserialisedFileCommands); comment = extract("^// ?(.+)", comment, 1); @@ -1295,6 +1441,13 @@ protected String deserialiseEndlineComment(String comment, FileCommandsInComment return comment; } + /** + * Extracts and deserialises one or more inlineFileCommands + * + * @param comment The comment to process + * @param deserialisedFileCommands The {@link FileCommandsInCommentList}. Passed in by reference + * @return The comment minus the fileCommands + */ protected String deserialiseFileCommandsInline(String comment, FileCommandsInCommentList deserialisedFileCommands) { Matcher matcher = extract("\\$(.+?)\\((.*?)\\);", comment); @@ -1313,6 +1466,13 @@ protected String deserialiseFileCommandsInline(String comment, FileCommandsInCom return comment; } + /** + * Extracts and deserialises one or more endlineFileCommands + * + * @param comment The comment to process + * @param deserialisedFileCommands The {@link FileCommandsInCommentList}. Passed in by reference + * @return The comment minus the fileCommands + */ protected String deserialiseFileCommandsEndline(String comment, FileCommandsInCommentList deserialisedFileCommands) { Matcher matcher = extract("\\$(.+?)\\((.*?)\\);", comment); @@ -1331,12 +1491,77 @@ protected String deserialiseFileCommandsEndline(String comment, FileCommandsInCo return comment; } + /** + * @return The regex used in {@link #splitTickLines(List, List, List, List, List, UnsortedFileCommandContainer)} + */ + protected String splitTickLineRegex() { + return "^\\t?\\d+\\|(.*?)\\|(.*?)\\|(\\S*)\\s?"; + } + + /** + * Splits tick lines into it's components + * + * @param lines The lines to split + * @param serialisedKeyboard The empty keyboard list to add to, passed in by reference + * @param serialisedMouse The empty mouse list to add to, passed in by reference + * @param serialisedCameraAngle The empty camera angle list to add to, passed in by reference + * @param commentsAtEnd The empty comments list to add to, passed in by reference + * @param endlineFileCommands The empty file commands list to add to, passed in by reference + */ + protected void splitTickLines(List lines, List serialisedKeyboard, List serialisedMouse, List serialisedCameraAngle, List commentsAtEnd, UnsortedFileCommandContainer endlineFileCommands) { + + String previousCamera = null; + if (previousInputContainer != null) { + VirtualCameraAngle camera = previousInputContainer.getCameraAngle(); + previousCamera = String.format("%s;%s", camera.getYaw(), camera.getPitch()); + } + + for (String line : lines) { + Matcher tickMatcher = extract(splitTickLineRegex(), line); + + if (tickMatcher.find()) { + if (!tickMatcher.group(1).isEmpty()) { + serialisedKeyboard.add(tickMatcher.group(1)); + } + if (!tickMatcher.group(2).isEmpty()) { + serialisedMouse.add(tickMatcher.group(2)); + } + + if (!tickMatcher.group(3).isEmpty()) { + serialisedCameraAngle.add(tickMatcher.group(3)); + previousCamera = tickMatcher.group(3); + } else { + if (previousCamera != null) + serialisedCameraAngle.add(previousCamera); + } + + FileCommandsInCommentList deserialisedFileCommands = new FileCommandsInCommentList(); + + String endlineComment = line.substring(tickMatcher.group(0).length()); + commentsAtEnd.add(deserialiseEndlineComment(endlineComment, deserialisedFileCommands)); + + if (deserialisedFileCommands.isEmpty()) + deserialisedFileCommands = null; + + endlineFileCommands.add(deserialisedFileCommands); + } + } + } + + /** + * Deserialises a list of keyboard strings in a tick + * + * @param keyboardStrings The list to process + * @return The {@link VirtualKeyboard} of this tick + * @throws PlaybackLoadException When the user made an error in the file + */ protected VirtualKeyboard deserialiseKeyboard(List keyboardStrings) { VirtualKeyboard out = new VirtualKeyboard(); currentSubtick = 0; for (String line : keyboardStrings) { Matcher matcher = extract("(.*?);(.*)", line); + if (matcher.find()) { String[] keys = matcher.group(1).split(","); char[] chars = matcher.group(2).toCharArray(); @@ -1351,6 +1576,13 @@ protected VirtualKeyboard deserialiseKeyboard(List keyboardStrings) { return out; } + /** + * Deserialises a list of mouse strings in a tick + * + * @param mouseStrings The list to process + * @return The {@link VirtualMouse} of this tick + * @throws PlaybackLoadException When the user made an error in the file + */ protected VirtualMouse deserialiseMouse(List mouseStrings) { VirtualMouse out = new VirtualMouse(); @@ -1389,6 +1621,13 @@ protected VirtualMouse deserialiseMouse(List mouseStrings) { return out; } + /** + * Deserialises a list of cameraAngle strings in a tick + * + * @param cameraAngleStrings The list to process + * @return The {@link VirtualCameraAngle} of this tick + * @throws PlaybackLoadException When the user made an error in the file + */ protected VirtualCameraAngle deserialiseCameraAngle(List cameraAngleStrings) { VirtualCameraAngle out = new VirtualCameraAngle(null, null, false); @@ -1426,6 +1665,13 @@ protected VirtualCameraAngle deserialiseCameraAngle(List cameraAngleStri return out; } + /** + * Deserialises keypresses in one subtick + * + * @param keyString The list of keyStrings in the current subtick + * @return A {@link Set} of integer keycodes + * @throws PlaybackLoadException When the user made an error in the file + */ protected Set deserialiseVirtualKeyboardKey(String[] keyString) { Set out = new HashSet<>(); @@ -1446,6 +1692,13 @@ protected Set deserialiseVirtualKeyboardKey(String[] keyString) { return out; } + /** + * Deserialises mousepresses in one subtick + * + * @param keyString The list of keyStrings in the current subtick + * @return A {@link Set} of integer keycodes + * @throws PlaybackLoadException When the user made an error in the file + */ protected Set deserialiseVirtualMouseKey(String[] keyString) { Set out = new HashSet<>(); @@ -1466,6 +1719,19 @@ protected Set deserialiseVirtualMouseKey(String[] keyString) { return out; } + /** + *

    Deserialises a singular VirtualKey. + * This could be either a keyboard or a mouse key + * + *

    All key names supported are listed in {@link VirtualKey}. + * + *

    Also can process literal integer keycode strings like 17. + * + * @param key The key string to check + * @param keyValidator An external validator to check. Used for mouse and keyboard as they have different keycode ranges + * @return The keycode of the string key or null if key is empty + * @throws PlaybackLoadException When a keycode can't be parsed + */ protected Integer deserialiseVirtualKey(String key, WrongKeyCheck keyValidator) { Integer vkey = null; @@ -1490,11 +1756,25 @@ else if (isNumeric(key)) { return vkey; } + /** + * Lambda for checking keycode ranges + * + * @see SerialiserFlavorBase#deserialiseVirtualKey(String, WrongKeyCheck) + * @author Scribble + */ @FunctionalInterface protected interface WrongKeyCheck { public void checkKey(int key) throws PlaybackLoadException; } + /** + * Wrapper around {@link Integer#parseInt(String)} + * + * @param name The name what is currently being parsed, used in error messages + * @param intstring The string to parse + * @return The parsed integer + * @throws PlaybackLoadException If a {@link NumberFormatException} is thrown during parsing + */ protected int parseInt(String name, String intstring) { try { return Integer.parseInt(intstring); @@ -1503,6 +1783,16 @@ protected int parseInt(String name, String intstring) { } } + /** + *

    Deserialises values in the form of "~10". + *

    These values will be compared and added to the value from the previous tick or subtick + * + * @param name The name what is currently being parsed, used in error messages + * @param intstring The string to parse + * @param previous The value from the previous tick + * @return The parsed and adjusted integer + * @throws PlaybackLoadException If a {@link NumberFormatException} is thrown during parsing + */ protected int deserialiseRelativeInt(String name, String intstring, Integer previous) { int out = 0; if (intstring.startsWith("~")) { @@ -1519,6 +1809,14 @@ protected int deserialiseRelativeInt(String name, String intstring, Integer prev return out; } + /** + * Wrapper around {@link Float#parseFloat(String)} + * + * @param name The name what is currently being parsed, used in error messages + * @param floatstring The string to parse + * @return The parsed float + * @throws PlaybackLoadException If a {@link NumberFormatException} is thrown during parsing + */ protected float parseFloat(String name, String floatstring) { try { return Float.parseFloat(floatstring); @@ -1527,6 +1825,16 @@ protected float parseFloat(String name, String floatstring) { } } + /** + *

    Deserialises values in the form of "~10.0". + *

    These values will be compared and added to the value from the previous tick or subtick + * + * @param name The name what is currently being parsed, used in error messages + * @param floatstring The string to parse + * @param previous The value from the previous tick + * @return The parsed and adjusted float + * @throws PlaybackLoadException If a {@link NumberFormatException} is thrown during parsing + */ protected Float deserialiseRelativeFloat(String name, String floatstring, Float previous) { if (floatstring == null) return null; @@ -1546,50 +1854,13 @@ protected Float deserialiseRelativeFloat(String name, String floatstring, Float return out; } - protected void splitInputs(List lines, List serialisedKeyboard, List serialisedMouse, List serialisedCameraAngle, List commentsAtEnd, UnsortedFileCommandContainer endlineFileCommands) { - - String previousCamera = null; - if (previousInputContainer != null) { - VirtualCameraAngle camera = previousInputContainer.getCameraAngle(); - previousCamera = String.format("%s;%s", camera.getYaw(), camera.getPitch()); - } - - for (String line : lines) { - Matcher tickMatcher = extract(splitInputRegex(), line); - - if (tickMatcher.find()) { - if (!tickMatcher.group(1).isEmpty()) { - serialisedKeyboard.add(tickMatcher.group(1)); - } - if (!tickMatcher.group(2).isEmpty()) { - serialisedMouse.add(tickMatcher.group(2)); - } - - if (!tickMatcher.group(3).isEmpty()) { - serialisedCameraAngle.add(tickMatcher.group(3)); - previousCamera = tickMatcher.group(3); - } else { - if (previousCamera != null) - serialisedCameraAngle.add(previousCamera); - } - - FileCommandsInCommentList deserialisedFileCommands = new FileCommandsInCommentList(); - - String endlineComment = line.substring(tickMatcher.group(0).length()); - commentsAtEnd.add(deserialiseEndlineComment(endlineComment, deserialisedFileCommands)); - - if (deserialisedFileCommands.isEmpty()) - deserialisedFileCommands = null; - - endlineFileCommands.add(deserialisedFileCommands); - } - } - } - - protected String splitInputRegex() { - return "^\\t?\\d+\\|(.*?)\\|(.*?)\\|(\\S*)\\s?"; - } - + /** + * Utility method to extract something with regex + * + * @param regex The regex to use + * @param haystack The string to search + * @return The {@link Matcher} for this regex + */ protected Matcher extract(String regex, String haystack) { Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE); Matcher matcher = pattern.matcher(haystack); @@ -1597,6 +1868,14 @@ protected Matcher extract(String regex, String haystack) { return matcher; } + /** + * Utility method to extract something with regex and a group + * + * @param regex The regex to use + * @param haystack The string to search + * @param group The group to extract + * @return The extracted string + */ protected String extract(String regex, String haystack, int group) { Matcher matcher = extract(regex, haystack); if (matcher.find()) { @@ -1605,32 +1884,45 @@ protected String extract(String regex, String haystack, int group) { return null; } + /** + * Utility method to check if a string contains a regex patterns + * + * @param regex The regex to use + * @param haystack The string to search + * @return True if the string contains the regex pattern + */ protected boolean contains(String regex, String haystack) { return extract(regex, haystack).find(); } + /** + * Utility method to check if the string is numeric + * + * @param string The string to search + * @return True if the string is numeric + */ protected boolean isNumeric(String string) { return Pattern.matches("-?\\d+", string); } - protected boolean isFloat(String string) { - return Pattern.matches("-?\\d+(?:\\.\\d+)?", string); - } - /** - * @return {@link #currentTick} + * Utility method to check if the string is a float + * + * @param string The string to search + * @return True if the string is a float */ - public long getCurrentTick() { - return currentTick; + protected boolean isFloat(String string) { + return Pattern.matches("-?\\d+(?:\\.\\d+)?", string); } /** - * @return {@link #currentSubtick} + * Utility method for creating a centered text + * + * @param text The text to center + * @param spacingChar The char which should be used for spacing + * @param headingWidth The total width + * @return The centered text */ - public Integer getCurrentSubtick() { - return currentSubtick; - } - public static String createCenteredHeading(String text, char spacingChar, int headingWidth) { if (text == null || text.isEmpty()) { @@ -1647,6 +1939,13 @@ public static String createCenteredHeading(String text, char spacingChar, int he return String.format("%s%s%s", paddingPre, text, paddingSuf); } + /** + * Utility method for repeating a char a certain amount + * + * @param spacingChar The char to use + * @param width The width to repeat the char + * @return The paddedString + */ private static String createPaddedString(char spacingChar, int width) { char[] spacingLine = new char[width]; for (int i = 0; i < spacingLine.length; i++) { @@ -1655,20 +1954,6 @@ private static String createPaddedString(char spacingChar, int width) { return new String(spacingLine); } - public static void addAll(BigArrayList list, BigArrayList toAdd) { - for (int i = 0; i < toAdd.size(); i++) { - T element = toAdd.get(i); - list.add(element); - } - } - - public static void addAll(BigArrayList list, List toAdd) { - for (int i = 0; i < toAdd.size(); i++) { - T element = toAdd.get(i); - list.add(element); - } - } - /** * Empties the list starting from the back if the values are null * @@ -1717,52 +2002,9 @@ 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(); - - @Override - public boolean equals(Object obj) { - if (obj instanceof SerialiserFlavorBase) { - SerialiserFlavorBase flavor = (SerialiserFlavorBase) obj; - return this.getExtensionName().equals(flavor.getExtensionName()); - } - 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; - } - - 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; - } - /** *

    Clamps the yaw to a value between -180 and 180 + * * @param yaw The yaw to clamp * @return The clamped yaw */ @@ -1781,6 +2023,7 @@ protected Float clampYaw(Float yaw) { *

    Unclamping the yaw from a clamped value *

    Makes it so 170 and a previous value of -170 will return -190,
    * removing the -180 180 clamp. Uses {@link #yawRotations} + * * @param yaw The yaw to unclamp * @param previous The previous yaw to compare against. * @return The unclamped yaw @@ -1799,8 +2042,45 @@ protected Float unclampYaw(Float yaw, Float previous) { return yaw + (360 * yawRotations); } + /** + * 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; + } + + /** + * @return {@link #currentTick} + */ + public long getCurrentTick() { + return currentTick; + } + + /** + * @return {@link #currentSubtick} + */ + public Integer getCurrentSubtick() { + return currentSubtick; + } + @Override public String toString() { return getExtensionName(); } + + @Override + public abstract SerialiserFlavorBase clone(); + + @Override + public boolean equals(Object obj) { + if (obj instanceof SerialiserFlavorBase) { + SerialiserFlavorBase flavor = (SerialiserFlavorBase) obj; + return this.getExtensionName().equals(flavor.getExtensionName()); + } + return super.equals(obj); + } } 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 48c313b6..a81d2057 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 @@ -341,7 +341,7 @@ protected void deserialiseMetadata(List headerLines) { } @Override - protected String splitInputRegex() { + protected String splitTickLineRegex() { return "^\\d+\\|(.*?)\\|(.*?)\\|(\\S*)~&"; } diff --git a/src/test/java/tasmod/playback/tasfile/PlaybackSerialiserTest.java b/src/test/java/tasmod/playback/tasfile/PlaybackSerialiserTest.java index 50a3eeda..3dfd9162 100644 --- a/src/test/java/tasmod/playback/tasfile/PlaybackSerialiserTest.java +++ b/src/test/java/tasmod/playback/tasfile/PlaybackSerialiserTest.java @@ -7,7 +7,6 @@ import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; -import java.io.Serializable; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -200,7 +199,7 @@ void testSerialiser() { try { BigArrayList actual = PlaybackSerialiser.loadFromFile(file, testFlavor); - assertBigArrayList(expected, actual); + assertIterableEquals(expected, actual); assertEquals("testing", testMetadata.actual); } catch (PlaybackLoadException | IOException e) { fail(e); @@ -267,7 +266,7 @@ void testDeserialiser() throws PlaybackLoadException, IOException { expected.add(new InputContainer(new VirtualKeyboard(), mouse3, cameraAngle3)); - assertBigArrayList(expected, actual); + assertIterableEquals(expected, actual); assertEquals("Wat", testMetadata.actual); @@ -278,7 +277,7 @@ void testDeserialiser() throws PlaybackLoadException, IOException { fclist.add(fccontainer); fclist.add(new SortedFileCommandContainer()); fclist.add(new SortedFileCommandContainer()); - assertBigArrayList(fclist, testFileCommand.getInlineStorage()); + assertIterableEquals(fclist, testFileCommand.getInlineStorage()); BigArrayList fclistEnd = new BigArrayList<>(); SortedFileCommandContainer fccontainerEnd = new SortedFileCommandContainer(); @@ -290,7 +289,7 @@ void testDeserialiser() throws PlaybackLoadException, IOException { fclistEnd.add(fccontainerEnd); fclistEnd.add(new SortedFileCommandContainer()); fclistEnd.add(new SortedFileCommandContainer()); - assertBigArrayList(fclistEnd, testFileCommand.getEndlineStorage()); + assertIterableEquals(fclistEnd, testFileCommand.getEndlineStorage()); } @Test @@ -353,7 +352,7 @@ void testDeserialiserNoExtensions() throws PlaybackLoadException, IOException { expected.add(new InputContainer(new VirtualKeyboard(), mouse3, cameraAngle3)); - assertBigArrayList(expected, actual); + assertIterableEquals(expected, actual); assertEquals("e", testMetadata.actual); @@ -389,18 +388,5 @@ void testFlavorIsNull() { }); assertEquals("Flavor name NotAFlavor doesn't exist.", t.getMessage()); - - } - - private void assertBigArrayList(BigArrayList expected, BigArrayList actual) { - assertIterableEquals(convertBigArrayListToArrayList(expected), convertBigArrayListToArrayList(actual)); - } - - private ArrayList convertBigArrayListToArrayList(BigArrayList list) { - ArrayList out = new ArrayList<>(); - for (long i = 0; i < list.size(); i++) { - out.add(list.get(i)); - } - return out; } } diff --git a/src/test/java/tasmod/playback/tasfile/SerialiserFlavorBaseTest.java b/src/test/java/tasmod/playback/tasfile/SerialiserFlavorBaseTest.java index 6481476f..29276d55 100644 --- a/src/test/java/tasmod/playback/tasfile/SerialiserFlavorBaseTest.java +++ b/src/test/java/tasmod/playback/tasfile/SerialiserFlavorBaseTest.java @@ -6,7 +6,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.io.Serializable; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedHashMap; @@ -231,7 +230,7 @@ void testSerialiseContainer() { expected.add("\t3|||4.0;4.0"); // C o m p a r e - assertBigArrayList(expected, actual); + assertIterableEquals(expected, actual); } /** @@ -714,7 +713,7 @@ void testDeserialiseContainer() { expected.add(new InputContainer(keyboard, mouse, cameraAngle)); - assertBigArrayList(expected, actual); + assertIterableEquals(expected, actual); } /** @@ -733,7 +732,7 @@ void testSplitInputs() { List actualComment = new ArrayList<>(); UnsortedFileCommandContainer actualFileCommand = new UnsortedFileCommandContainer(); - splitInputs(tick, actualKeyboard, actualMouse, actualCameraAngle, actualComment, actualFileCommand); + splitTickLines(tick, actualKeyboard, actualMouse, actualCameraAngle, actualComment, actualFileCommand); List expectedKeyboard = new ArrayList<>(); List expectedMouse = new ArrayList<>(); @@ -1078,18 +1077,6 @@ void testYawUnclamping() { assertEquals(-190f, unclampYaw(170f, -170f)); } - private void assertBigArrayList(BigArrayList expected, BigArrayList actual) { - assertIterableEquals(convertBigArrayListToArrayList(expected), convertBigArrayListToArrayList(actual)); - } - - private ArrayList convertBigArrayListToArrayList(BigArrayList list) { - ArrayList out = new ArrayList<>(); - for (long i = 0; i < list.size(); i++) { - out.add(list.get(i)); - } - return out; - } - @Override public SerialiserFlavorBase clone() { return new SerialiserFlavorBaseTest();