From c5faf569f8ddb94dcb266d9d53f9bd13d12e94b5 Mon Sep 17 00:00:00 2001 From: Scribble Date: Wed, 24 Jul 2024 21:13:12 +0200 Subject: [PATCH] [PlaybackSerialiser] Improved error handling --- .../playback/metadata/PlaybackMetadata.java | 19 ++-- .../tasfile/flavor/SerialiserFlavorBase.java | 97 +++++++++++++------ .../tasfile/SerialiserFlavorBaseTest.java | 24 ++--- 3 files changed, 90 insertions(+), 50 deletions(-) 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 3ad74cd8..fe4da30c 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/metadata/PlaybackMetadata.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/metadata/PlaybackMetadata.java @@ -16,7 +16,7 @@ public class PlaybackMetadata { private String extensionName; private LinkedHashMap data; - + private static String SEPERATOR = ":"; public PlaybackMetadata(PlaybackMetadataExtension extension) { @@ -81,7 +81,7 @@ public boolean equals(Object obj) { public static PlaybackMetadata fromStringList(String extensionName, List list) { PlaybackMetadata out = new PlaybackMetadata(extensionName); - final Pattern pattern = Pattern.compile("(\\w+)\\"+SEPERATOR+"(.+)"); + final Pattern pattern = Pattern.compile("(\\w+)\\" + SEPERATOR + "(.+)"); for (String data : list) { Matcher matcher = pattern.matcher(data); @@ -94,19 +94,20 @@ public static PlaybackMetadata fromStringList(String extensionName, List return out; } - + public static PlaybackMetadata fromHashMap(String extensionName, LinkedHashMap data) { return new PlaybackMetadata(extensionName, new LinkedHashMap<>(data)); } - + public static abstract class PlaybackMetadataExtension implements Registerable { - + /** * Currently unused.
* Maybe in the future, TASes have to be created with /create, then you can interactively set the values...
*/ - public void onCreate() {}; - + public void onCreate() { + }; + /** * Runs, when the TASfile is being stored to a file.
* Create a new {@link PlaybackMetadata} with PlaybackMetadata metadata = new PlaybackMetadata(this);.
@@ -115,14 +116,14 @@ public static abstract class PlaybackMetadataExtension implements Registerable { * @return The {@link PlaybackMetadata} to be saved in the TASfile */ public abstract PlaybackMetadata onStore(); - + /** * Runs when the TASfile is being loaded from a file
* * @param metadata The metadata for this extension to read from */ public abstract void onLoad(PlaybackMetadata metadata); - + /** * Runs when the PlaybackController is cleared */ 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 fb2251bc..592bcf69 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 @@ -448,9 +448,9 @@ public BigArrayList deserialise(BigArrayList lines, long // Extract the tick and set the index i = extractContainer(container, lines, i); currentLine = i; - currentTick++; // Extract container deserialiseContainer(out, container); + currentTick++; } previousTickContainer = null; return out; @@ -711,8 +711,10 @@ protected VirtualKeyboard deserialiseKeyboard(List keyboardStrings) { String[] keys = matcher.group(1).split(","); char[] chars = matcher.group(2).toCharArray(); - int[] keycodes = deserialiseVirtualKey(keys, VirtualKey.ZERO); + 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++; } @@ -732,7 +734,7 @@ protected VirtualMouse deserialiseMouse(List mouseStrings) { String[] buttons = matcher.group(1).split(","); String[] functions = matcher.group(2).split(","); - int[] keycodes = deserialiseVirtualKey(buttons, VirtualKey.MOUSEMOVED); + int[] keycodes = deserialiseVirtualMouseKey(buttons); int scrollwheel; Integer cursorX; Integer cursorY; @@ -742,13 +744,15 @@ protected VirtualMouse deserialiseMouse(List mouseStrings) { cursorX = deserialiseRelativeInt("cursorX", functions[1], previousCursorX); cursorY = deserialiseRelativeInt("cursorY", functions[2], previousCursorY); } else { - throw new PlaybackLoadException(currentLine, currentTick, currentSubtick, "Mouse functions do not have the correct length"); + throw new PlaybackLoadException(currentLine, currentTick, currentSubtick, "Mouse can't be read. Probably a missing comma: %s", line); } out.updateFromState(keycodes, scrollwheel, cursorX, cursorY); previousCursorX = cursorX; previousCursorY = cursorY; + } else { + throw new PlaybackLoadException(currentLine, currentTick, currentSubtick, "Mouse is missing a semicolon"); } currentSubtick++; } @@ -766,57 +770,92 @@ protected VirtualCameraAngle deserialiseCameraAngle(List cameraAngleStri Matcher matcher = extract("(.+?);(.+)", line); if (matcher.find()) { - String cameraPitchString = matcher.group(1); - String cameraYawString = matcher.group(2); + String cameraYawString = matcher.group(1); + String cameraPitchString = matcher.group(2); - Float cameraPitch = null; Float cameraYaw = null; - - if (!"null".equals(cameraPitchString)) - cameraPitch = deserialiseRelativeFloat("camera pitch", cameraPitchString, previousPitch); + 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; } - protected int[] deserialiseVirtualKey(String[] keyString, VirtualKey defaultKey) { + protected int[] deserialiseVirtualKeyboardKey(String[] keyString) { int[] out = new int[keyString.length]; for (int i = 0; i < keyString.length; i++) { String key = keyString[i]; + out[i] = deserialiseVirtualKey(key, VirtualKey.ZERO, (vkey) -> { + if (vkey < 0) { + throw new PlaybackLoadException(currentLine, currentTick, currentSubtick, "Keyboard section contains a mouse key: %s", VirtualKey.get(vkey)); + } + }); + } + return out; + } - /* If no key is pressed, then a zero key will be used for the state. - * This zero key is either VirtualKey.ZERO on a keyboard or VirtualKey.MOUSEMOVED on a mouse, - * hence the parameter */ - if (key.isEmpty()) { - out[i] = defaultKey.getKeycode(); - continue; - } - - /* Instead of keynames such as W, A, S, KEY_1, NUMPAD3 you can also write the numerical keycodes - * into the tasfile, e.g. 17, 30, 31, 2, 81. This enables TASmod to support every current and future - * keycodes, even if no name was given to the key in VirtualKey.*/ - if (isNumeric(key)) { - out[i] = Integer.parseInt(key); - continue; - } + protected int[] deserialiseVirtualMouseKey(String[] keyString) { + int[] out = new int[keyString.length]; - out[i] = VirtualKey.getKeycode(key); + for (int i = 0; i < keyString.length; i++) { + String key = keyString[i]; + out[i] = deserialiseVirtualKey(key, VirtualKey.MOUSEMOVED, (vkey) -> { + if (vkey >= 0) { + throw new PlaybackLoadException(currentLine, currentTick, currentSubtick, "Mouse section contains a keyboard key: %s", VirtualKey.get(vkey)); + } + }); } return out; } + protected int deserialiseVirtualKey(String key, VirtualKey defaultKey, WrongKeyCheck keyValidator) { + + Integer vkey = null; + /* If no key is pressed, then a zero key will be used for the state. + * This zero key is either VirtualKey.ZERO on a keyboard or VirtualKey.MOUSEMOVED on a mouse, + * hence the parameter */ + if (key.isEmpty()) { + vkey = defaultKey.getKeycode(); + } + /* Instead of keynames such as W, A, S, KEY_1, NUMPAD3 you can also write the numerical keycodes + * into the tasfile, e.g. 17, 30, 31, 2, 81. This enables TASmod to support every current and future + * keycodes, even if no name was given to the key in VirtualKey.*/ + else if (isNumeric(key)) { + vkey = Integer.parseInt(key); + } else { + vkey = VirtualKey.getKeycode(key); + } + + if (vkey == null) { + throw new PlaybackLoadException(currentLine, currentTick, currentSubtick, "The keycode %s does not exist", key); + } + + keyValidator.checkKey(vkey); + + return vkey; + } + + @FunctionalInterface + protected interface WrongKeyCheck { + public void checkKey(int key) throws PlaybackLoadException; + } + protected int parseInt(String name, String intstring) { try { return Integer.parseInt(intstring); } catch (NumberFormatException e) { - throw new PlaybackLoadException(currentLine, currentTick, currentSubtick, e, "Can't parse integer in %s", name); + throw new PlaybackLoadException(currentLine, currentTick, currentSubtick, e, "The %s could not be processed. This should be a number: %s", name, intstring); } } @@ -840,7 +879,7 @@ protected float parseFloat(String name, String floatstring) { try { return Float.parseFloat(floatstring); } catch (NumberFormatException e) { - throw new PlaybackLoadException(currentLine, currentTick, currentSubtick, e, "Can't parse float in %s", name); + throw new PlaybackLoadException(currentLine, currentTick, currentSubtick, e, "The %s could not be processed. This should be a decimal number: %s", name, floatstring); } } diff --git a/src/test/java/tasmod/playback/tasfile/SerialiserFlavorBaseTest.java b/src/test/java/tasmod/playback/tasfile/SerialiserFlavorBaseTest.java index fc8fcf93..c5e2c610 100644 --- a/src/test/java/tasmod/playback/tasfile/SerialiserFlavorBaseTest.java +++ b/src/test/java/tasmod/playback/tasfile/SerialiserFlavorBaseTest.java @@ -858,30 +858,30 @@ void testDeserialiseMouse() { expected.updateFromEvent(VirtualKey.MC, true, 15, 25, 34); assertEquals(expected, actual); - - currentTick=29; + + currentTick = 29; List tick2 = new ArrayList<>(); tick2.add(";0,0,0"); tick2.add("LC;0,12,35"); tick2.add("LC,MC;15,25"); - - Throwable t = assertThrows(PlaybackLoadException.class, ()->{ + + Throwable t = assertThrows(PlaybackLoadException.class, () -> { deserialiseMouse(tick2); }); - - assertEquals("Line 1, Tick 29, Subtick 2: Mouse functions do not have the correct length", t.getMessage()); - - currentTick=30; + + assertEquals("Line 1, Tick 29, Subtick 2: Mouse can't be read. Probably a missing comma: LC,MC;15,25", t.getMessage()); + + currentTick = 30; List tick3 = new ArrayList<>(); tick3.add(";0,0,0"); tick3.add("LC;0,12,35,12"); tick3.add("LC,MC;15,25,15"); - - Throwable t1 = assertThrows(PlaybackLoadException.class, ()->{ + + Throwable t1 = assertThrows(PlaybackLoadException.class, () -> { deserialiseMouse(tick3); }); - - assertEquals("Line 1, Tick 30, Subtick 1: Mouse functions do not have the correct length", t1.getMessage()); + + assertEquals("Line 1, Tick 30, Subtick 1: Mouse can't be read. Probably a missing comma: LC;0,12,35,12", t1.getMessage()); } /**