diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..c3d5a3da --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +*.java diff=java +*.md diff=markdown \ No newline at end of file diff --git a/src/main/java/com/minecrafttas/mctcommon/Configuration.java b/src/main/java/com/minecrafttas/mctcommon/Configuration.java index 5d461f89..e8ad5c83 100644 --- a/src/main/java/com/minecrafttas/mctcommon/Configuration.java +++ b/src/main/java/com/minecrafttas/mctcommon/Configuration.java @@ -5,40 +5,76 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; -import java.util.HashMap; +import java.util.ArrayList; import java.util.InvalidPropertiesFormatException; -import java.util.Map; +import java.util.LinkedHashMap; +import java.util.List; import java.util.Properties; +import com.minecrafttas.mctcommon.Configuration.ConfigOptions; +import com.minecrafttas.mctcommon.registry.AbstractRegistry; +import com.minecrafttas.mctcommon.registry.Registerable; /** * A very simple configuration class + * * @author Scribble - * */ -public class Configuration { - +public class Configuration extends AbstractRegistry { + private File file; - + private Properties properties; private String comment; - + public Configuration(String comment, File configFile) { + super("Configuration", new LinkedHashMap<>()); + file = configFile; this.comment = comment; + } + + protected final List configRegistry = new ArrayList<>(); + + @Override + public void register(ConfigOptions registryObject) { + if(registryObject == null) { + return; + } + + if(configRegistry.contains(registryObject)) { + return; + } + + configRegistry.add(registryObject); + } + + @Override + public void unregister(ConfigOptions registryObject) { + if (registryObject == null) { + return; + } + + if (!configRegistry.contains(registryObject)) { + return; + } - if(file.exists()) { - properties = load(); + configRegistry.remove(registryObject); + } + + public void load() { + if (file.exists()) { + properties = loadInner(); } - if(properties == null || !file.exists()) { + if (properties == null || !file.exists()) { properties = generateDefault(); save(); - } + } } - public Properties load() { + private Properties loadInner() { FileInputStream fis; Properties newProp = new Properties(); try { @@ -57,11 +93,11 @@ public Properties load() { } return newProp; } - + public void save() { save(file); } - + public void save(File file) { try { FileOutputStream fos = new FileOutputStream(file); @@ -71,73 +107,62 @@ public void save(File file) { e.printStackTrace(); } } - + public Properties generateDefault() { Properties newProperties = new Properties(); - newProperties.putAll(ConfigOptions.getDefaultValues()); + configRegistry.forEach((configOption)->{ + newProperties.put(configOption.getConfigKey(), configOption.getDefaultValue()); + }); return newProperties; } - + public String get(ConfigOptions configOption) { - return properties.getProperty(configOption.configKey); + return properties.getProperty(configOption.getConfigKey(), configOption.getDefaultValue()); } - + public int getInt(ConfigOptions configOption) { return Integer.parseInt(get(configOption)); } - + public boolean getBoolean(ConfigOptions configOption) { return Boolean.parseBoolean(get(configOption)); } - + public boolean has(ConfigOptions configOption) { - return properties.contains(configOption.configKey); + return properties.contains(configOption.getConfigKey()); } - + public void set(ConfigOptions configOption, String value) { - properties.setProperty(configOption.configKey, value); + if(properties == null) { + throw new NullPointerException("Config needs to be loaded first, before trying to set a value"); + } + properties.setProperty(configOption.getConfigKey(), value); save(); } - + public void set(ConfigOptions configOption, int value) { String val = Integer.toString(value); set(configOption, val); } - + public void set(ConfigOptions configOption, boolean value) { String val = Boolean.toString(value); set(configOption, val); } - + public void reset(ConfigOptions configOption) { - set(configOption, configOption.defaultValue); + set(configOption, configOption.getDefaultValue()); } - + public void delete(ConfigOptions configOption) { properties.remove(configOption); save(); } - - public static enum ConfigOptions{ - FileToOpen("fileToOpen", ""), - ServerConnection("serverConnection", ""); - - private String configKey; - private String defaultValue; - - private ConfigOptions(String configKey, String defaultValue) { - this.configKey = configKey; - this.defaultValue = defaultValue; - } - - public static Map getDefaultValues() { - Map out = new HashMap<>(); - for (ConfigOptions configthing : values()) { - if(configthing.defaultValue!=null) { - out.put(configthing.configKey, configthing.defaultValue); - } - } - return out; - } + + public interface ConfigOptions extends Registerable { + + public String getDefaultValue(); + + public String getConfigKey(); } } diff --git a/src/main/java/com/minecrafttas/mctcommon/KeybindManager.java b/src/main/java/com/minecrafttas/mctcommon/KeybindManager.java index d1d70caa..fcfdf5a2 100644 --- a/src/main/java/com/minecrafttas/mctcommon/KeybindManager.java +++ b/src/main/java/com/minecrafttas/mctcommon/KeybindManager.java @@ -23,10 +23,10 @@ public class KeybindManager implements EventClientGameLoop { public static class Keybind { - private KeyBinding keyBinding; - private String category; - private Runnable onKeyDown; - private IsKeyDownFunc isKeyDownFunc; + public final KeyBinding vanillaKeyBinding; + private final String category; + private final Runnable onKeyDown; + private final IsKeyDownFunc isKeyDownFunc; /** * Initialize keybind @@ -49,12 +49,16 @@ public Keybind(String name, String category, int defaultKey, Runnable onKeyDown) * @param onKeyDown Will be run when the keybind is pressed */ public Keybind(String name, String category, int defaultKey, Runnable onKeyDown, IsKeyDownFunc func) { - this.keyBinding = new KeyBinding(name, defaultKey, category); + this.vanillaKeyBinding = new KeyBinding(name, defaultKey, category); this.category = category; this.onKeyDown = onKeyDown; this.isKeyDownFunc = func; } + @Override + public String toString() { + return this.vanillaKeyBinding.getKeyDescription(); + } } private List keybindings; @@ -77,7 +81,7 @@ public KeybindManager(IsKeyDownFunc defaultFunction) { public void onRunClientGameLoop(Minecraft mc) { for (Keybind keybind : this.keybindings){ IsKeyDownFunc keyDown = keybind.isKeyDownFunc != null ? keybind.isKeyDownFunc : defaultFunction; - if(keyDown.isKeyDown(keybind.keyBinding)){ + if(keyDown.isKeyDown(keybind.vanillaKeyBinding)){ keybind.onKeyDown.run(); } } @@ -89,9 +93,9 @@ public void onRunClientGameLoop(Minecraft mc) { * * @param keybind Keybind to register */ - public KeyBinding registerKeybind(Keybind keybind) { + public void registerKeybind(Keybind keybind) { this.keybindings.add(keybind); - KeyBinding keyBinding = keybind.keyBinding; + KeyBinding keyBinding = keybind.vanillaKeyBinding; // add category GameSettings options = Minecraft.getMinecraft().gameSettings; @@ -100,9 +104,8 @@ public KeyBinding registerKeybind(Keybind keybind) { // add keybinding options.keyBindings = ArrayUtils.add(options.keyBindings, keyBinding); - return keyBinding; } - + @FunctionalInterface public static interface IsKeyDownFunc { diff --git a/src/main/java/com/minecrafttas/mctcommon/LanguageManager.java b/src/main/java/com/minecrafttas/mctcommon/LanguageManager.java index da3a4bce..acb7defe 100644 --- a/src/main/java/com/minecrafttas/mctcommon/LanguageManager.java +++ b/src/main/java/com/minecrafttas/mctcommon/LanguageManager.java @@ -1,19 +1,26 @@ package com.minecrafttas.mctcommon; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; + +import org.apache.commons.io.IOUtils; + import com.google.common.base.Splitter; import com.google.common.collect.Iterables; import com.google.gson.Gson; + import net.minecraft.client.resources.IResource; import net.minecraft.client.resources.IResourceManager; import net.minecraft.util.ResourceLocation; -import org.apache.commons.io.IOUtils; - -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.regex.Pattern; public class LanguageManager { diff --git a/src/main/java/com/minecrafttas/mctcommon/events/EventClient.java b/src/main/java/com/minecrafttas/mctcommon/events/EventClient.java index 78159c02..14a58991 100644 --- a/src/main/java/com/minecrafttas/mctcommon/events/EventClient.java +++ b/src/main/java/com/minecrafttas/mctcommon/events/EventClient.java @@ -1,7 +1,7 @@ package com.minecrafttas.mctcommon.events; import com.minecrafttas.mctcommon.events.EventListenerRegistry.EventBase; -import com.minecrafttas.mctcommon.server.Client; +import com.minecrafttas.mctcommon.networking.Client; import com.mojang.authlib.GameProfile; import net.minecraft.client.Minecraft; diff --git a/src/main/java/com/minecrafttas/mctcommon/events/EventServer.java b/src/main/java/com/minecrafttas/mctcommon/events/EventServer.java index 471baa00..0f8e6af2 100644 --- a/src/main/java/com/minecrafttas/mctcommon/events/EventServer.java +++ b/src/main/java/com/minecrafttas/mctcommon/events/EventServer.java @@ -1,7 +1,7 @@ package com.minecrafttas.mctcommon.events; import com.minecrafttas.mctcommon.events.EventListenerRegistry.EventBase; -import com.minecrafttas.mctcommon.server.Client; +import com.minecrafttas.mctcommon.networking.Client; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.server.MinecraftServer; diff --git a/src/main/java/com/minecrafttas/mctcommon/server/ByteBufferBuilder.java b/src/main/java/com/minecrafttas/mctcommon/networking/ByteBufferBuilder.java similarity index 97% rename from src/main/java/com/minecrafttas/mctcommon/server/ByteBufferBuilder.java rename to src/main/java/com/minecrafttas/mctcommon/networking/ByteBufferBuilder.java index 17d7b47a..8558439b 100644 --- a/src/main/java/com/minecrafttas/mctcommon/server/ByteBufferBuilder.java +++ b/src/main/java/com/minecrafttas/mctcommon/networking/ByteBufferBuilder.java @@ -1,10 +1,10 @@ -package com.minecrafttas.mctcommon.server; +package com.minecrafttas.mctcommon.networking; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.UUID; -import com.minecrafttas.mctcommon.server.interfaces.PacketID; +import com.minecrafttas.mctcommon.networking.interfaces.PacketID; /** * Helper method for creating byte buffers which get pooled from a diff --git a/src/main/java/com/minecrafttas/mctcommon/server/Client.java b/src/main/java/com/minecrafttas/mctcommon/networking/Client.java similarity index 95% rename from src/main/java/com/minecrafttas/mctcommon/server/Client.java rename to src/main/java/com/minecrafttas/mctcommon/networking/Client.java index 4858fbdd..c8989a1f 100644 --- a/src/main/java/com/minecrafttas/mctcommon/server/Client.java +++ b/src/main/java/com/minecrafttas/mctcommon/networking/Client.java @@ -1,9 +1,9 @@ -package com.minecrafttas.mctcommon.server; +package com.minecrafttas.mctcommon.networking; import static com.minecrafttas.mctcommon.MCTCommon.Client; import static com.minecrafttas.mctcommon.MCTCommon.LOGGER; import static com.minecrafttas.mctcommon.MCTCommon.Server; -import static com.minecrafttas.mctcommon.server.SecureList.BUFFER_SIZE; +import static com.minecrafttas.mctcommon.networking.SecureList.BUFFER_SIZE; import java.io.IOException; import java.net.InetSocketAddress; @@ -22,10 +22,10 @@ import com.minecrafttas.mctcommon.events.EventClient.EventDisconnectClient; import com.minecrafttas.mctcommon.events.EventListenerRegistry; import com.minecrafttas.mctcommon.events.EventServer.EventClientCompleteAuthentication; -import com.minecrafttas.mctcommon.server.exception.InvalidPacketException; -import com.minecrafttas.mctcommon.server.exception.PacketNotImplementedException; -import com.minecrafttas.mctcommon.server.exception.WrongSideException; -import com.minecrafttas.mctcommon.server.interfaces.PacketID; +import com.minecrafttas.mctcommon.networking.exception.InvalidPacketException; +import com.minecrafttas.mctcommon.networking.exception.PacketNotImplementedException; +import com.minecrafttas.mctcommon.networking.exception.WrongSideException; +import com.minecrafttas.mctcommon.networking.interfaces.PacketID; /** * A custom asynchronous client diff --git a/src/main/java/com/minecrafttas/mctcommon/server/CompactPacketHandler.java b/src/main/java/com/minecrafttas/mctcommon/networking/CompactPacketHandler.java similarity index 58% rename from src/main/java/com/minecrafttas/mctcommon/server/CompactPacketHandler.java rename to src/main/java/com/minecrafttas/mctcommon/networking/CompactPacketHandler.java index 8cd5c2b1..93bc10f1 100644 --- a/src/main/java/com/minecrafttas/mctcommon/server/CompactPacketHandler.java +++ b/src/main/java/com/minecrafttas/mctcommon/networking/CompactPacketHandler.java @@ -1,8 +1,8 @@ -package com.minecrafttas.mctcommon.server; +package com.minecrafttas.mctcommon.networking; import java.nio.ByteBuffer; -import com.minecrafttas.mctcommon.server.exception.PacketNotImplementedException; +import com.minecrafttas.mctcommon.networking.exception.PacketNotImplementedException; @FunctionalInterface public interface CompactPacketHandler { diff --git a/src/main/java/com/minecrafttas/mctcommon/server/PacketHandlerRegistry.java b/src/main/java/com/minecrafttas/mctcommon/networking/PacketHandlerRegistry.java similarity index 81% rename from src/main/java/com/minecrafttas/mctcommon/server/PacketHandlerRegistry.java rename to src/main/java/com/minecrafttas/mctcommon/networking/PacketHandlerRegistry.java index 25b29058..b073e0d0 100644 --- a/src/main/java/com/minecrafttas/mctcommon/server/PacketHandlerRegistry.java +++ b/src/main/java/com/minecrafttas/mctcommon/networking/PacketHandlerRegistry.java @@ -1,4 +1,4 @@ -package com.minecrafttas.mctcommon.server; +package com.minecrafttas.mctcommon.networking; import java.nio.ByteBuffer; import java.util.ArrayList; @@ -6,13 +6,13 @@ import java.util.List; import com.minecrafttas.mctcommon.MCTCommon; -import com.minecrafttas.mctcommon.server.Client.Side; -import com.minecrafttas.mctcommon.server.exception.PacketNotImplementedException; -import com.minecrafttas.mctcommon.server.exception.WrongSideException; -import com.minecrafttas.mctcommon.server.interfaces.ClientPacketHandler; -import com.minecrafttas.mctcommon.server.interfaces.PacketHandlerBase; -import com.minecrafttas.mctcommon.server.interfaces.PacketID; -import com.minecrafttas.mctcommon.server.interfaces.ServerPacketHandler; +import com.minecrafttas.mctcommon.networking.Client.Side; +import com.minecrafttas.mctcommon.networking.exception.PacketNotImplementedException; +import com.minecrafttas.mctcommon.networking.exception.WrongSideException; +import com.minecrafttas.mctcommon.networking.interfaces.ClientPacketHandler; +import com.minecrafttas.mctcommon.networking.interfaces.PacketHandlerBase; +import com.minecrafttas.mctcommon.networking.interfaces.PacketID; +import com.minecrafttas.mctcommon.networking.interfaces.ServerPacketHandler; public class PacketHandlerRegistry { private static final List REGISTRY = new ArrayList<>(); diff --git a/src/main/java/com/minecrafttas/mctcommon/server/SecureList.java b/src/main/java/com/minecrafttas/mctcommon/networking/SecureList.java similarity index 96% rename from src/main/java/com/minecrafttas/mctcommon/server/SecureList.java rename to src/main/java/com/minecrafttas/mctcommon/networking/SecureList.java index ce82af9e..372efaec 100644 --- a/src/main/java/com/minecrafttas/mctcommon/server/SecureList.java +++ b/src/main/java/com/minecrafttas/mctcommon/networking/SecureList.java @@ -1,4 +1,4 @@ -package com.minecrafttas.mctcommon.server; +package com.minecrafttas.mctcommon.networking; import java.nio.ByteBuffer; diff --git a/src/main/java/com/minecrafttas/mctcommon/server/Server.java b/src/main/java/com/minecrafttas/mctcommon/networking/Server.java similarity index 96% rename from src/main/java/com/minecrafttas/mctcommon/server/Server.java rename to src/main/java/com/minecrafttas/mctcommon/networking/Server.java index d10ff67a..da578d64 100644 --- a/src/main/java/com/minecrafttas/mctcommon/server/Server.java +++ b/src/main/java/com/minecrafttas/mctcommon/networking/Server.java @@ -1,4 +1,4 @@ -package com.minecrafttas.mctcommon.server; +package com.minecrafttas.mctcommon.networking; import static com.minecrafttas.mctcommon.MCTCommon.LOGGER; import static com.minecrafttas.mctcommon.MCTCommon.Server; @@ -15,8 +15,8 @@ import com.minecrafttas.mctcommon.MCTCommon; import com.minecrafttas.mctcommon.events.EventListenerRegistry; import com.minecrafttas.mctcommon.events.EventServer.EventDisconnectServer; -import com.minecrafttas.mctcommon.server.Client.ClientCallback; -import com.minecrafttas.mctcommon.server.interfaces.PacketID; +import com.minecrafttas.mctcommon.networking.Client.ClientCallback; +import com.minecrafttas.mctcommon.networking.interfaces.PacketID; import net.minecraft.entity.player.EntityPlayer; diff --git a/src/main/java/com/minecrafttas/mctcommon/server/exception/InvalidPacketException.java b/src/main/java/com/minecrafttas/mctcommon/networking/exception/InvalidPacketException.java similarity index 75% rename from src/main/java/com/minecrafttas/mctcommon/server/exception/InvalidPacketException.java rename to src/main/java/com/minecrafttas/mctcommon/networking/exception/InvalidPacketException.java index fa8e389b..ef184e1c 100644 --- a/src/main/java/com/minecrafttas/mctcommon/server/exception/InvalidPacketException.java +++ b/src/main/java/com/minecrafttas/mctcommon/networking/exception/InvalidPacketException.java @@ -1,4 +1,4 @@ -package com.minecrafttas.mctcommon.server.exception; +package com.minecrafttas.mctcommon.networking.exception; public class InvalidPacketException extends Exception { diff --git a/src/main/java/com/minecrafttas/mctcommon/server/exception/PacketNotImplementedException.java b/src/main/java/com/minecrafttas/mctcommon/networking/exception/PacketNotImplementedException.java similarity index 70% rename from src/main/java/com/minecrafttas/mctcommon/server/exception/PacketNotImplementedException.java rename to src/main/java/com/minecrafttas/mctcommon/networking/exception/PacketNotImplementedException.java index 95365017..37994139 100644 --- a/src/main/java/com/minecrafttas/mctcommon/server/exception/PacketNotImplementedException.java +++ b/src/main/java/com/minecrafttas/mctcommon/networking/exception/PacketNotImplementedException.java @@ -1,8 +1,8 @@ -package com.minecrafttas.mctcommon.server.exception; +package com.minecrafttas.mctcommon.networking.exception; -import com.minecrafttas.mctcommon.server.Client.Side; -import com.minecrafttas.mctcommon.server.interfaces.PacketHandlerBase; -import com.minecrafttas.mctcommon.server.interfaces.PacketID; +import com.minecrafttas.mctcommon.networking.Client.Side; +import com.minecrafttas.mctcommon.networking.interfaces.PacketHandlerBase; +import com.minecrafttas.mctcommon.networking.interfaces.PacketID; public class PacketNotImplementedException extends Exception { diff --git a/src/main/java/com/minecrafttas/mctcommon/server/exception/WrongSideException.java b/src/main/java/com/minecrafttas/mctcommon/networking/exception/WrongSideException.java similarity index 67% rename from src/main/java/com/minecrafttas/mctcommon/server/exception/WrongSideException.java rename to src/main/java/com/minecrafttas/mctcommon/networking/exception/WrongSideException.java index 97c33878..f19c8922 100644 --- a/src/main/java/com/minecrafttas/mctcommon/server/exception/WrongSideException.java +++ b/src/main/java/com/minecrafttas/mctcommon/networking/exception/WrongSideException.java @@ -1,7 +1,7 @@ -package com.minecrafttas.mctcommon.server.exception; +package com.minecrafttas.mctcommon.networking.exception; -import com.minecrafttas.mctcommon.server.Client; -import com.minecrafttas.mctcommon.server.interfaces.PacketID; +import com.minecrafttas.mctcommon.networking.Client; +import com.minecrafttas.mctcommon.networking.interfaces.PacketID; public class WrongSideException extends Exception { diff --git a/src/main/java/com/minecrafttas/mctcommon/server/interfaces/ClientPacketHandler.java b/src/main/java/com/minecrafttas/mctcommon/networking/interfaces/ClientPacketHandler.java similarity index 52% rename from src/main/java/com/minecrafttas/mctcommon/server/interfaces/ClientPacketHandler.java rename to src/main/java/com/minecrafttas/mctcommon/networking/interfaces/ClientPacketHandler.java index 9ce21f45..2abbaad6 100644 --- a/src/main/java/com/minecrafttas/mctcommon/server/interfaces/ClientPacketHandler.java +++ b/src/main/java/com/minecrafttas/mctcommon/networking/interfaces/ClientPacketHandler.java @@ -1,9 +1,9 @@ -package com.minecrafttas.mctcommon.server.interfaces; +package com.minecrafttas.mctcommon.networking.interfaces; import java.nio.ByteBuffer; -import com.minecrafttas.mctcommon.server.exception.PacketNotImplementedException; -import com.minecrafttas.mctcommon.server.exception.WrongSideException; +import com.minecrafttas.mctcommon.networking.exception.PacketNotImplementedException; +import com.minecrafttas.mctcommon.networking.exception.WrongSideException; public interface ClientPacketHandler extends PacketHandlerBase{ diff --git a/src/main/java/com/minecrafttas/mctcommon/server/interfaces/PacketHandlerBase.java b/src/main/java/com/minecrafttas/mctcommon/networking/interfaces/PacketHandlerBase.java similarity index 86% rename from src/main/java/com/minecrafttas/mctcommon/server/interfaces/PacketHandlerBase.java rename to src/main/java/com/minecrafttas/mctcommon/networking/interfaces/PacketHandlerBase.java index b3cdc182..3651b45f 100644 --- a/src/main/java/com/minecrafttas/mctcommon/server/interfaces/PacketHandlerBase.java +++ b/src/main/java/com/minecrafttas/mctcommon/networking/interfaces/PacketHandlerBase.java @@ -1,4 +1,4 @@ -package com.minecrafttas.mctcommon.server.interfaces; +package com.minecrafttas.mctcommon.networking.interfaces; public interface PacketHandlerBase { /** diff --git a/src/main/java/com/minecrafttas/mctcommon/server/interfaces/PacketID.java b/src/main/java/com/minecrafttas/mctcommon/networking/interfaces/PacketID.java similarity index 65% rename from src/main/java/com/minecrafttas/mctcommon/server/interfaces/PacketID.java rename to src/main/java/com/minecrafttas/mctcommon/networking/interfaces/PacketID.java index 462ab012..99506c07 100644 --- a/src/main/java/com/minecrafttas/mctcommon/server/interfaces/PacketID.java +++ b/src/main/java/com/minecrafttas/mctcommon/networking/interfaces/PacketID.java @@ -1,9 +1,10 @@ -package com.minecrafttas.mctcommon.server.interfaces; +package com.minecrafttas.mctcommon.networking.interfaces; -import com.minecrafttas.mctcommon.server.CompactPacketHandler; -import com.minecrafttas.mctcommon.server.Client.Side; +import com.minecrafttas.mctcommon.networking.CompactPacketHandler; +import com.minecrafttas.mctcommon.registry.Registerable; +import com.minecrafttas.mctcommon.networking.Client.Side; -public interface PacketID { +public interface PacketID extends Registerable{ /** * @return The numerical ID of the packet */ diff --git a/src/main/java/com/minecrafttas/mctcommon/server/interfaces/ServerPacketHandler.java b/src/main/java/com/minecrafttas/mctcommon/networking/interfaces/ServerPacketHandler.java similarity index 52% rename from src/main/java/com/minecrafttas/mctcommon/server/interfaces/ServerPacketHandler.java rename to src/main/java/com/minecrafttas/mctcommon/networking/interfaces/ServerPacketHandler.java index 9c380a59..ba66fb29 100644 --- a/src/main/java/com/minecrafttas/mctcommon/server/interfaces/ServerPacketHandler.java +++ b/src/main/java/com/minecrafttas/mctcommon/networking/interfaces/ServerPacketHandler.java @@ -1,9 +1,9 @@ -package com.minecrafttas.mctcommon.server.interfaces; +package com.minecrafttas.mctcommon.networking.interfaces; import java.nio.ByteBuffer; -import com.minecrafttas.mctcommon.server.exception.PacketNotImplementedException; -import com.minecrafttas.mctcommon.server.exception.WrongSideException; +import com.minecrafttas.mctcommon.networking.exception.PacketNotImplementedException; +import com.minecrafttas.mctcommon.networking.exception.WrongSideException; public interface ServerPacketHandler extends PacketHandlerBase{ diff --git a/src/main/java/com/minecrafttas/mctcommon/registry/AbstractRegistry.java b/src/main/java/com/minecrafttas/mctcommon/registry/AbstractRegistry.java new file mode 100644 index 00000000..292cef65 --- /dev/null +++ b/src/main/java/com/minecrafttas/mctcommon/registry/AbstractRegistry.java @@ -0,0 +1,84 @@ +package com.minecrafttas.mctcommon.registry; + +import java.util.Arrays; +import java.util.Map; + +import com.minecrafttas.tasmod.TASmod; + +public abstract class AbstractRegistry { + protected final Map REGISTRY; + protected final String name; + + public AbstractRegistry(String name, Map map) { + this.REGISTRY = map; + this.name = name; + } + + public void register(V registryObject) { + if (registryObject == null) { + throw new NullPointerException("Tried to register an object to "+name+" with value null"); + } + + if (containsClass(registryObject)) { + TASmod.LOGGER.warn("Trying to register an object in {}, but another instance of this class is already registered: {}", name, registryObject.getClass().getName()); + return; + } + + if(REGISTRY.containsKey(registryObject.getExtensionName())) { + TASmod.LOGGER.warn("Trying to register the an object in {}, but an extension with the same name is already registered: {}", registryObject.getExtensionName()); + return; + } + + REGISTRY.put(registryObject.getExtensionName(), registryObject); + } + + @SafeVarargs + public final void register(V... registryObjects) { + this.register(Arrays.asList(registryObjects)); + } + + public final void register(Iterable registryObjects) { + for (V registryObject : registryObjects) { + this.register(registryObject); + } + } + + public void unregister(V registryObject) { + if (registryObject == null) { + throw new NullPointerException("Tried to unregister an object from "+name+" with value null"); + } + if (REGISTRY.containsKey(registryObject.getExtensionName())) { + REGISTRY.remove(registryObject.getExtensionName()); + } else { + TASmod.LOGGER.warn("Trying to unregister an object from {}, but it was not registered: {}", name, registryObject.getClass().getName()); + } + } + + @SafeVarargs + public final void unregister(V... registryObjects) { + this.unregister(Arrays.asList(registryObjects)); + } + + public final void unregister(Iterable registryObjects) { + for (V registryObject : registryObjects) { + this.unregister(registryObject); + } + } + + public void clear() { + REGISTRY.clear(); + } + + protected boolean containsClass(V newExtension) { + return containsClazz(newExtension, REGISTRY.values()); + } + + public static boolean containsClazz(W newExtension, Iterable iterable) { + for (W extension : iterable) { + if (extension.getClass().equals(newExtension.getClass())) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/com/minecrafttas/mctcommon/registry/Registerable.java b/src/main/java/com/minecrafttas/mctcommon/registry/Registerable.java new file mode 100644 index 00000000..226179a6 --- /dev/null +++ b/src/main/java/com/minecrafttas/mctcommon/registry/Registerable.java @@ -0,0 +1,5 @@ +package com.minecrafttas.mctcommon.registry; + +public interface Registerable { + public String getExtensionName(); +} \ No newline at end of file diff --git a/src/main/java/com/minecrafttas/tasmod/TASmod.java b/src/main/java/com/minecrafttas/tasmod/TASmod.java index cf503604..57899423 100644 --- a/src/main/java/com/minecrafttas/tasmod/TASmod.java +++ b/src/main/java/com/minecrafttas/tasmod/TASmod.java @@ -10,9 +10,10 @@ import com.minecrafttas.mctcommon.events.EventListenerRegistry; import com.minecrafttas.mctcommon.events.EventServer.EventServerInit; import com.minecrafttas.mctcommon.events.EventServer.EventServerStop; -import com.minecrafttas.mctcommon.server.PacketHandlerRegistry; -import com.minecrafttas.mctcommon.server.Server; +import com.minecrafttas.mctcommon.networking.PacketHandlerRegistry; +import com.minecrafttas.mctcommon.networking.Server; import com.minecrafttas.tasmod.commands.CommandClearInputs; +import com.minecrafttas.tasmod.commands.CommandFileCommand; import com.minecrafttas.tasmod.commands.CommandFolder; import com.minecrafttas.tasmod.commands.CommandFullPlay; import com.minecrafttas.tasmod.commands.CommandFullRecord; @@ -24,10 +25,11 @@ import com.minecrafttas.tasmod.commands.CommandSaveTAS; import com.minecrafttas.tasmod.commands.CommandSavestate; import com.minecrafttas.tasmod.commands.CommandTickrate; +import com.minecrafttas.tasmod.commands.TabCompletionUtils; import com.minecrafttas.tasmod.ktrng.KillTheRNGHandler; -import com.minecrafttas.tasmod.networking.TASmodPackets; import com.minecrafttas.tasmod.playback.PlaybackControllerServer; import com.minecrafttas.tasmod.playback.metadata.integrated.StartpositionMetadataExtension; +import com.minecrafttas.tasmod.registries.TASmodPackets; import com.minecrafttas.tasmod.savestates.SavestateHandlerServer; import com.minecrafttas.tasmod.savestates.files.SavestateTrackerFile; import com.minecrafttas.tasmod.tickratechanger.TickrateChangerServer; @@ -43,56 +45,58 @@ * ModContainer for TASmod * * @author Scribble - * */ -public class TASmod implements ModInitializer, EventServerInit, EventServerStop{ +public class TASmod implements ModInitializer, EventServerInit, EventServerStop { private static MinecraftServer serverInstance; - + public static final Logger LOGGER = LogManager.getLogger("TASmod"); - - public static PlaybackControllerServer playbackControllerServer=new PlaybackControllerServer();; - + + public static PlaybackControllerServer playbackControllerServer = new PlaybackControllerServer();; + public static SavestateHandlerServer savestateHandlerServer; - + public static KillTheRNGHandler ktrngHandler; - + public static TickrateChangerServer tickratechanger; - + public static TickSyncServer ticksyncServer; - + public static final Scheduler tickSchedulerServer = new Scheduler(); - + public static Server server; public static final int networkingport = 8999; public static final boolean isDevEnvironment = FabricLoaderImpl.INSTANCE.isDevelopmentEnvironment(); - + public static final StartpositionMetadataExtension startPositionMetadataExtension = new StartpositionMetadataExtension(); - + + public static final TabCompletionUtils tabCompletionUtils = new TabCompletionUtils(); + + public static final CommandFileCommand commandFileCommand = new CommandFileCommand(); + @Override public void onInitialize() { - + LOGGER.info("Initializing TASmod"); - - + // Start ticksync ticksyncServer = new TickSyncServer(); - + // Initilize KillTheRNG LOGGER.info("Testing connection with KillTheRNG"); - ktrngHandler=new KillTheRNGHandler(FabricLoaderImpl.INSTANCE.isModLoaded("killtherng")); - + ktrngHandler = new KillTheRNGHandler(FabricLoaderImpl.INSTANCE.isModLoaded("killtherng")); + // Initialize TickrateChanger tickratechanger = new TickrateChangerServer(LOGGER); - + // Register event listeners EventListenerRegistry.register(this); EventListenerRegistry.register(ticksyncServer); EventListenerRegistry.register(tickratechanger); EventListenerRegistry.register(ktrngHandler); - + // Register packet handlers LOGGER.info(LoggerMarkers.Networking, "Registering network handlers"); PacketHandlerRegistry.register(ticksyncServer); @@ -100,15 +104,17 @@ public void onInitialize() { PacketHandlerRegistry.register(ktrngHandler); PacketHandlerRegistry.register(playbackControllerServer); PacketHandlerRegistry.register(startPositionMetadataExtension); + PacketHandlerRegistry.register(tabCompletionUtils); + PacketHandlerRegistry.register(commandFileCommand); } - + @Override public void onServerInit(MinecraftServer server) { LOGGER.info("Initializing server"); serverInstance = server; - + // Command handling - + CommandRegistry.registerServerCommand(new CommandTickrate(), server); CommandRegistry.registerServerCommand(new CommandRecord(), server); CommandRegistry.registerServerCommand(new CommandPlay(), server); @@ -121,6 +127,7 @@ public void onServerInit(MinecraftServer server) { CommandRegistry.registerServerCommand(new CommandFullPlay(), server); CommandRegistry.registerServerCommand(new CommandRestartAndPlay(), server); CommandRegistry.registerServerCommand(new CommandPlayUntil(), server); + CommandRegistry.registerServerCommand(commandFileCommand, server); // Save Loadstate Count File savestateDirectory = new File(server.getDataDirectory() + File.separator + "saves" + File.separator + "savestates" + File.separator); @@ -132,10 +139,10 @@ public void onServerInit(MinecraftServer server) { savestateHandlerServer = new SavestateHandlerServer(server, LOGGER); PacketHandlerRegistry.register(savestateHandlerServer); - - if(!server.isDedicatedServer()) { - TASmod.tickratechanger.ticksPerSecond=0F; - TASmod.tickratechanger.tickrateSaved=20F; + + if (!server.isDedicatedServer()) { + TASmod.tickratechanger.ticksPerSecond = 0F; + TASmod.tickratechanger.tickrateSaved = 20F; } else { // Starting custom server instance try { @@ -145,26 +152,27 @@ public void onServerInit(MinecraftServer server) { } } } - + @Override public void onServerStop(MinecraftServer mcserver) { - serverInstance=null; - - if(mcserver.isDedicatedServer()) { + serverInstance = null; + + if (mcserver.isDedicatedServer()) { try { - if (server != null) server.close(); + if (server != null) + server.close(); } catch (IOException e) { LOGGER.error("Unable to close TASmod server: {}", e); e.printStackTrace(); } } - - if(savestateHandlerServer != null) { - PacketHandlerRegistry.unregister(savestateHandlerServer); // Unregistering the savestatehandler, as a new instance is registered in onServerStart() + + if (savestateHandlerServer != null) { + PacketHandlerRegistry.unregister(savestateHandlerServer); // Unregistering the savestatehandler, as a new instance is registered in onServerStart() savestateHandlerServer = null; } } - + public static MinecraftServer getServerInstance() { return serverInstance; } diff --git a/src/main/java/com/minecrafttas/tasmod/TASmodClient.java b/src/main/java/com/minecrafttas/tasmod/TASmodClient.java index aab41277..b8aaa122 100644 --- a/src/main/java/com/minecrafttas/tasmod/TASmodClient.java +++ b/src/main/java/com/minecrafttas/tasmod/TASmodClient.java @@ -4,34 +4,34 @@ import java.io.File; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; +import java.util.Arrays; import org.apache.logging.log4j.Level; -import org.lwjgl.input.Keyboard; import com.minecrafttas.mctcommon.Configuration; -import com.minecrafttas.mctcommon.Configuration.ConfigOptions; import com.minecrafttas.mctcommon.KeybindManager; -import com.minecrafttas.mctcommon.KeybindManager.Keybind; import com.minecrafttas.mctcommon.LanguageManager; import com.minecrafttas.mctcommon.events.EventClient.EventClientInit; import com.minecrafttas.mctcommon.events.EventClient.EventOpenGui; import com.minecrafttas.mctcommon.events.EventClient.EventPlayerJoinedClientSide; import com.minecrafttas.mctcommon.events.EventListenerRegistry; -import com.minecrafttas.mctcommon.server.Client; -import com.minecrafttas.mctcommon.server.PacketHandlerRegistry; -import com.minecrafttas.mctcommon.server.Server; +import com.minecrafttas.mctcommon.networking.Client; +import com.minecrafttas.mctcommon.networking.PacketHandlerRegistry; +import com.minecrafttas.mctcommon.networking.Server; import com.minecrafttas.tasmod.gui.InfoHud; import com.minecrafttas.tasmod.handlers.LoadingScreenHandler; -import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; -import com.minecrafttas.tasmod.networking.TASmodPackets; import com.minecrafttas.tasmod.playback.PlaybackControllerClient; import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; -import com.minecrafttas.tasmod.playback.metadata.PlaybackMetadataRegistry; +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.PlaybackSerialiser; +import com.minecrafttas.tasmod.playback.tasfile.flavor.integrated.Beta1Flavor; +import com.minecrafttas.tasmod.registries.TASmodAPIRegistry; +import com.minecrafttas.tasmod.registries.TASmodConfig; +import com.minecrafttas.tasmod.registries.TASmodKeybinds; +import com.minecrafttas.tasmod.registries.TASmodPackets; import com.minecrafttas.tasmod.savestates.SavestateHandlerClient; import com.minecrafttas.tasmod.tickratechanger.TickrateChangerClient; import com.minecrafttas.tasmod.ticksync.TickSyncClient; @@ -48,11 +48,7 @@ import net.minecraft.client.gui.GuiMainMenu; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.multiplayer.ServerData; -import net.minecraft.client.settings.KeyBinding; import net.minecraft.server.MinecraftServer; -import net.minecraft.util.text.ChatType; -import net.minecraft.util.text.TextComponentString; -import net.minecraft.util.text.TextFormatting; public class TASmodClient implements ClientModInitializer, EventClientInit, EventPlayerJoinedClientSide, EventOpenGui{ @@ -61,8 +57,6 @@ public class TASmodClient implements ClientModInitializer, EventClientInit, Even public static TickSyncClient ticksyncClient; - public static PlaybackSerialiser serialiser = new PlaybackSerialiser(); - public static final String tasdirectory = Minecraft.getMinecraft().mcDataDir.getAbsolutePath() + File.separator + "saves" + File.separator + "tasfiles"; public static final String savestatedirectory = Minecraft.getMinecraft().mcDataDir.getAbsolutePath() + File.separator + "saves" + File.separator + "savestates"; @@ -115,25 +109,11 @@ public static void createSavestatesDir() { @Override public void onInitializeClient() { - // Load config - Minecraft mc = Minecraft.getMinecraft(); - - File configDir = new File(mc.mcDataDir, "config"); - if(!configDir.exists()) { - configDir.mkdir(); - } - config = new Configuration("TASmod configuration", new File(configDir, "tasmod.cfg")); - LanguageManager.registerMod("tasmod"); - // Execute /restartandplay. Load the file to start from the config. If it exists load the playback file on start. - String fileOnStart = config.get(ConfigOptions.FileToOpen); - if (fileOnStart.isEmpty()) { - fileOnStart = null; - } else { - config.reset(ConfigOptions.FileToOpen); - } - virtual=new VirtualInput(LOGGER); //TODO Move fileOnStart to PlaybackController + loadConfig(Minecraft.getMinecraft()); + + virtual=new VirtualInput(LOGGER); // Initialize InfoHud hud = new InfoHud(); @@ -146,9 +126,30 @@ public void onInitializeClient() { // Initialize keybind manager keybindManager = new KeybindManager(VirtualKeybindings::isKeyDownExceptTextfield); - // Register event listeners + registerEventListeners(); + + registerNetworkPacketHandlers(); + + // Starting local server instance + try { + TASmod.server = new Server(TASmod.networkingport-1, TASmodPackets.values()); + } catch (Exception e) { + LOGGER.error("Unable to launch TASmod server: {}", e.getMessage()); + } + + } + + private void registerNetworkPacketHandlers() { + // Register packet handlers + LOGGER.info(LoggerMarkers.Networking, "Registering network handlers on client"); + PacketHandlerRegistry.register(controller); + PacketHandlerRegistry.register(ticksyncClient); + PacketHandlerRegistry.register(tickratechanger); + PacketHandlerRegistry.register(savestateHandlerClient); + } + + private void registerEventListeners() { EventListenerRegistry.register(this); -// EventListenerRegistry.register(virtual); TODO Remove if unnecessary EventListenerRegistry.register(hud); EventListenerRegistry.register(shieldDownloader); EventListenerRegistry.register(loadingScreenHandler); @@ -161,64 +162,21 @@ public void onInitializeClient() { return gui; })); EventListenerRegistry.register(controller); - PlaybackMetadataRegistry.register(creditsMetadataExtension); EventListenerRegistry.register(creditsMetadataExtension); - - PlaybackMetadataRegistry.register(startpositionMetadataExtension); EventListenerRegistry.register(startpositionMetadataExtension); - // Register packet handlers - LOGGER.info(LoggerMarkers.Networking, "Registering network handlers on client"); - PacketHandlerRegistry.register(controller); - PacketHandlerRegistry.register(ticksyncClient); - PacketHandlerRegistry.register(tickratechanger); - PacketHandlerRegistry.register(savestateHandlerClient); - - // Starting local server instance - try { - TASmod.server = new Server(TASmod.networkingport-1, TASmodPackets.values()); - } catch (Exception e) { - LOGGER.error("Unable to launch TASmod server: {}", e.getMessage()); - } + EventListenerRegistry.register(desyncMonitorFileCommandExtension); + EventListenerRegistry.register(TASmodAPIRegistry.PLAYBACK_METADATA); + EventListenerRegistry.register(TASmodAPIRegistry.PLAYBACK_FILE_COMMAND); } - + @Override public void onClientInit(Minecraft mc) { - // initialize keybindings - List blockedKeybindings = new ArrayList<>(); - blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Tickrate 0 Key", "TASmod", Keyboard.KEY_F8, () -> TASmodClient.tickratechanger.togglePause(), VirtualKeybindings::isKeyDown))); - blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Advance Tick", "TASmod", Keyboard.KEY_F9, () -> TASmodClient.tickratechanger.advanceTick(), VirtualKeybindings::isKeyDown))); - blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Recording/Playback Stop", "TASmod", Keyboard.KEY_F10, () -> TASmodClient.controller.setTASState(TASstate.NONE), VirtualKeybindings::isKeyDown))); - blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Create Savestate", "TASmod", Keyboard.KEY_J, () -> { - Minecraft.getMinecraft().ingameGUI.addChatMessage(ChatType.CHAT, new TextComponentString("Savestates might not work correctly at the moment... rewriting a lot of core features, which might break this...")); - try { - TASmodClient.client.send(new TASmodBufferBuilder(TASmodPackets.SAVESTATE_SAVE).writeInt(-1)); - } catch (Exception e) { - e.printStackTrace(); - } - }))); - blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Load Latest Savestate", "TASmod", Keyboard.KEY_K, () -> { - Minecraft.getMinecraft().ingameGUI.addChatMessage(ChatType.CHAT, new TextComponentString(TextFormatting.RED+"Savestates might not work correctly at the moment... rewriting a lot of core features, which might break this...")); - try { - TASmodClient.client.send(new TASmodBufferBuilder(TASmodPackets.SAVESTATE_LOAD).writeInt(-1)); - } catch (Exception e) { - e.printStackTrace(); - } - }))); - blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Open InfoGui Editor", "TASmod", Keyboard.KEY_F6, () -> Minecraft.getMinecraft().displayGuiScreen(TASmodClient.hud)))); - blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Various Testing", "TASmod", Keyboard.KEY_F12, () -> { - controller.setTASState(TASstate.RECORDING); - }, VirtualKeybindings::isKeyDown))); - blockedKeybindings.add(keybindManager.registerKeybind(new Keybind("Various Testing2", "TASmod", Keyboard.KEY_F7, () -> { -// try { -// TASmodClient.client = new Client("localhost", TASmod.networkingport-1, TASmodPackets.values(), mc.getSession().getProfile().getName(), true); -// } catch (Exception e) { -// e.printStackTrace(); -// } - controller.setTASState(TASstate.PLAYBACK); - }, VirtualKeybindings::isKeyDown))); - blockedKeybindings.forEach(VirtualKeybindings::registerBlockedKeyBinding); + registerKeybindings(mc); + registerPlaybackMetadata(mc); + registerSerialiserFlavors(mc); + registerFileCommands(); createTASDir(); createSavestatesDir(); @@ -255,7 +213,7 @@ public void onPlayerJoinedClientSide(EntityPlayerSP player) { } - if(!(ip+":"+port).equals(connectedIP)) { + if(!(ip+":"+port).equals(connectedIP)) { // TODO Clean this up. Make TASmodNetworkHandler out of this... Maybe with Permission system? try { LOGGER.info("Closing client connection: {}", client.getRemote()); client.disconnect(); @@ -280,34 +238,7 @@ public void onPlayerJoinedClientSide(EntityPlayerSP player) { @Override public GuiScreen onOpenGui(GuiScreen gui) { if (gui instanceof GuiMainMenu) { - if (client == null) { - Minecraft mc = Minecraft.getMinecraft(); - - String IP = "localhost"; - int PORT = TASmod.networkingport - 1; - - // Get the connection on startup from config - String configAddress = config.get(ConfigOptions.ServerConnection); - if(configAddress != null && !configAddress.isEmpty()) { - String[] ipSplit = configAddress.split(":"); - IP = ipSplit[0]; - try { - PORT = Integer.parseInt(ipSplit[1]); - } catch (Exception e) { - LOGGER.catching(Level.ERROR, e); - IP = "localhost"; - PORT = TASmod.networkingport - 1; - } - } - - try { - // connect to server and authenticate - client = new Client(IP, PORT, TASmodPackets.values(), mc.getSession().getUsername(), true); - } catch (Exception e) { - LOGGER.error("Unable to connect TASmod client: {}", e); - } - ticksyncClient.setEnabled(true); - } + initializeCustomPacketHandler(); } else if (gui instanceof GuiControls) { TASmodClient.controller.setTASState(TASstate.NONE); // Set the TASState to nothing to avoid collisions if (TASmodClient.tickratechanger.ticksPerSecond == 0) { @@ -322,4 +253,74 @@ public GuiScreen onOpenGui(GuiScreen gui) { } return gui; } + + private void initializeCustomPacketHandler() { + if (client == null) { + Minecraft mc = Minecraft.getMinecraft(); + + String IP = "localhost"; + int PORT = TASmod.networkingport - 1; + + // Get the connection on startup from config + String configAddress = config.get(TASmodConfig.ServerConnection); + if(configAddress != null && !configAddress.isEmpty()) { + String[] ipSplit = configAddress.split(":"); + IP = ipSplit[0]; + try { + PORT = Integer.parseInt(ipSplit[1]); + } catch (Exception e) { + LOGGER.catching(Level.ERROR, e); + IP = "localhost"; + PORT = TASmod.networkingport - 1; + } + } + + try { + // connect to server and authenticate + client = new Client(IP, PORT, TASmodPackets.values(), mc.getSession().getUsername(), true); + } catch (Exception e) { + LOGGER.error("Unable to connect TASmod client: {}", e); + } + ticksyncClient.setEnabled(true); + } + } + + private void registerKeybindings(Minecraft mc) { + Arrays.stream(TASmodKeybinds.valuesKeybind()).forEach(keybindManager::registerKeybind); + Arrays.stream(TASmodKeybinds.valuesVanillaKeybind()).forEach(VirtualKeybindings::registerBlockedKeyBinding); + } + + private void registerPlaybackMetadata(Minecraft mc) { + TASmodAPIRegistry.PLAYBACK_METADATA.register(creditsMetadataExtension); + TASmodAPIRegistry.PLAYBACK_METADATA.register(startpositionMetadataExtension); + } + + public static Beta1Flavor betaFlavor = new Beta1Flavor(); + + private void registerSerialiserFlavors(Minecraft mc) { + TASmodAPIRegistry.SERIALISER_FLAVOR.register(betaFlavor); + } + + public static DesyncMonitorFileCommandExtension desyncMonitorFileCommandExtension = new DesyncMonitorFileCommandExtension(); + public static OptionsFileCommandExtension optionsFileCommandExtension = new OptionsFileCommandExtension(); + public static LabelFileCommandExtension labelFileCommandExtension = new LabelFileCommandExtension(); + + private void registerFileCommands() { + TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.register(desyncMonitorFileCommandExtension); + TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.register(optionsFileCommandExtension); + TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.register(labelFileCommandExtension); + + TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.setConfig(config); + } + + private void loadConfig(Minecraft mc) { + File configDir = new File(mc.mcDataDir, "config"); + if(!configDir.exists()) { + configDir.mkdir(); + } + config = new Configuration("TASmod configuration", new File(configDir, "tasmod.cfg")); + config.register(TASmodConfig.values()); + config.load(); + config.save(); + } } diff --git a/src/main/java/com/minecrafttas/tasmod/commands/CommandClearInputs.java b/src/main/java/com/minecrafttas/tasmod/commands/CommandClearInputs.java index b93ac489..38f7337f 100644 --- a/src/main/java/com/minecrafttas/tasmod/commands/CommandClearInputs.java +++ b/src/main/java/com/minecrafttas/tasmod/commands/CommandClearInputs.java @@ -2,7 +2,7 @@ import com.minecrafttas.tasmod.TASmod; import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; -import com.minecrafttas.tasmod.networking.TASmodPackets; +import com.minecrafttas.tasmod.registries.TASmodPackets; import net.minecraft.command.CommandBase; import net.minecraft.command.CommandException; diff --git a/src/main/java/com/minecrafttas/tasmod/commands/CommandFileCommand.java b/src/main/java/com/minecrafttas/tasmod/commands/CommandFileCommand.java new file mode 100644 index 00000000..07c649f1 --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/commands/CommandFileCommand.java @@ -0,0 +1,204 @@ +package com.minecrafttas.tasmod.commands; + +import static com.minecrafttas.tasmod.registries.TASmodPackets.COMMAND_FILECOMMANDLIST; +import static com.minecrafttas.tasmod.registries.TASmodPackets.PLAYBACK_FILECOMMAND_ENABLE; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.minecrafttas.mctcommon.networking.Client.Side; +import com.minecrafttas.mctcommon.networking.exception.PacketNotImplementedException; +import com.minecrafttas.mctcommon.networking.exception.WrongSideException; +import com.minecrafttas.mctcommon.networking.interfaces.ClientPacketHandler; +import com.minecrafttas.mctcommon.networking.interfaces.PacketID; +import com.minecrafttas.mctcommon.networking.interfaces.ServerPacketHandler; +import com.minecrafttas.tasmod.TASmod; +import com.minecrafttas.tasmod.TASmodClient; +import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; +import com.minecrafttas.tasmod.playback.filecommands.PlaybackFileCommand.PlaybackFileCommandExtension; +import com.minecrafttas.tasmod.registries.TASmodAPIRegistry; +import com.minecrafttas.tasmod.registries.TASmodPackets; + +import net.minecraft.client.Minecraft; +import net.minecraft.command.CommandBase; +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.command.PlayerNotFoundException; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.ChatType; +import net.minecraft.util.text.TextComponentString; +import net.minecraft.util.text.TextFormatting; + +public class CommandFileCommand extends CommandBase implements ClientPacketHandler, ServerPacketHandler { + + CompletableFuture> fileCommandList = null; + + @Override + public String getName() { + return "filecommand"; + } + + @Override + public String getUsage(ICommandSender iCommandSender) { + return "/filecommand [enable|disable]"; + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + if (sender instanceof EntityPlayer) { + if (sender.canUseCommand(2, "fileCommand")) { + + String senderName = null; + + // Get the list of file commands from the server + Map fileCommandNames; + try { + senderName = getCommandSenderAsPlayer(sender).getName(); + fileCommandNames = getExtensions(senderName); + } catch (PlayerNotFoundException | InterruptedException | ExecutionException | TimeoutException e) { + sender.sendMessage(new TextComponentString(e.getMessage())); + return; + } + + if (args.length == 0) { // Displays all enabled and disabled filecommands + sender.sendMessage(new TextComponentString(String.join(" ", getColoredNames(fileCommandNames)))); + } else if (args.length == 1) { // Toggles the filecommand + + String name = args[0]; + Boolean enable = fileCommandNames.get(name); + + if (enable == null) { + throw new CommandException("The file command was not found: %s", name); + } + + try { + TASmod.server.sendTo(senderName, new TASmodBufferBuilder(PLAYBACK_FILECOMMAND_ENABLE).writeString(name).writeBoolean(!enable)); + } catch (Exception e) { + e.printStackTrace(); + } + } + } else { + sender.sendMessage(new TextComponentString(TextFormatting.RED + "You have no permission to use this command")); + } + } + } + + @Override + public List getTabCompletions(MinecraftServer minecraftServer, ICommandSender iCommandSender, String[] args, BlockPos blockPos) { + if (args.length == 1) { + List names = null; + try { + names = new ArrayList<>(getExtensions(getCommandSenderAsPlayer(iCommandSender).getName()).keySet()); + } catch (PlayerNotFoundException | InterruptedException | ExecutionException | TimeoutException e) { + e.printStackTrace(); + return super.getTabCompletions(minecraftServer, iCommandSender, args, blockPos); + } + return getListOfStringsMatchingLastWord(args, names); + } + return super.getTabCompletions(minecraftServer, iCommandSender, args, blockPos); + } + + private Map getExtensions(String playername) throws InterruptedException, ExecutionException, TimeoutException { + Map out = new LinkedHashMap<>(); + fileCommandList = new CompletableFuture<>(); + + try { + TASmod.server.sendTo(playername, new TASmodBufferBuilder(COMMAND_FILECOMMANDLIST)); + } catch (Exception e) { + e.printStackTrace(); + } + + List commands = fileCommandList.get(2, TimeUnit.SECONDS); + + commands.forEach(element -> { + + Pattern pattern = Pattern.compile("^E_"); + Matcher matcher = pattern.matcher(element); + if (matcher.find()) { + element = matcher.replaceFirst(""); + out.put(element, true); + return; + } + + pattern = Pattern.compile("^D_"); + matcher = pattern.matcher(element); + if (matcher.find()) { + element = matcher.replaceFirst(""); + out.put(element, false); + return; + } + }); + + return out; + } + + private List getColoredNames(Map list) { + List out = new ArrayList<>(); + list.forEach((name, enabled) -> { + out.add(String.format("%s%s%s", enabled ? TextFormatting.GREEN : TextFormatting.RED, name, TextFormatting.RESET)); + }); + return out; + } + + @Override + public PacketID[] getAcceptedPacketIDs() { + return new PacketID[] { COMMAND_FILECOMMANDLIST, PLAYBACK_FILECOMMAND_ENABLE }; + } + + @Override + public void onServerPacket(PacketID id, ByteBuffer buf, String username) throws PacketNotImplementedException, WrongSideException, Exception { + TASmodPackets packet = (TASmodPackets) id; + switch (packet) { + case COMMAND_FILECOMMANDLIST: + String filecommandnames = TASmodBufferBuilder.readString(buf); + fileCommandList.complete(Arrays.asList(filecommandnames.split("\\|"))); + break; + default: + throw new WrongSideException(packet, Side.SERVER); + } + } + + // ========== Client + + @Override + public void onClientPacket(PacketID id, ByteBuffer buf, String username) throws PacketNotImplementedException, WrongSideException, Exception { + TASmodPackets packet = (TASmodPackets) id; + switch (packet) { + case COMMAND_FILECOMMANDLIST: + String filecommandnames = String.join("|", getFileCommandNames(TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.getAll())); + TASmodClient.client.send(new TASmodBufferBuilder(COMMAND_FILECOMMANDLIST).writeString(filecommandnames)); + break; + case PLAYBACK_FILECOMMAND_ENABLE: + String filecommand = TASmodBufferBuilder.readString(buf); + boolean enable = TASmodBufferBuilder.readBoolean(buf); + boolean success = TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.setEnabled(filecommand, enable); + + String msg = success ? String.format("%s%s file command: %s", TextFormatting.GREEN, enable ? "Enabled" : "Disabled", filecommand) : String.format("%sFailed to %s file command: %s", TextFormatting.RED, enable ? "enable" : "disable", filecommand); + Minecraft.getMinecraft().ingameGUI.addChatMessage(ChatType.CHAT, new TextComponentString(msg)); + break; + default: + break; + } + } + + private List getFileCommandNames(List fileCommands) { + List out = new ArrayList<>(); + fileCommands.forEach(element -> { + out.add(String.format("%s_%s", element.isEnabled() ? "E" : "D", element.toString())); + }); + return out; + } + +} diff --git a/src/main/java/com/minecrafttas/tasmod/commands/CommandFolder.java b/src/main/java/com/minecrafttas/tasmod/commands/CommandFolder.java index 3642126f..eae277ed 100644 --- a/src/main/java/com/minecrafttas/tasmod/commands/CommandFolder.java +++ b/src/main/java/com/minecrafttas/tasmod/commands/CommandFolder.java @@ -11,7 +11,7 @@ import com.minecrafttas.tasmod.TASmod; import com.minecrafttas.tasmod.TASmodClient; import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; -import com.minecrafttas.tasmod.networking.TASmodPackets; +import com.minecrafttas.tasmod.registries.TASmodPackets; import net.minecraft.command.CommandBase; import net.minecraft.command.CommandException; diff --git a/src/main/java/com/minecrafttas/tasmod/commands/CommandFullPlay.java b/src/main/java/com/minecrafttas/tasmod/commands/CommandFullPlay.java index 1dc02367..c8626e4d 100644 --- a/src/main/java/com/minecrafttas/tasmod/commands/CommandFullPlay.java +++ b/src/main/java/com/minecrafttas/tasmod/commands/CommandFullPlay.java @@ -2,8 +2,8 @@ import com.minecrafttas.tasmod.TASmod; import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; -import com.minecrafttas.tasmod.networking.TASmodPackets; import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; +import com.minecrafttas.tasmod.registries.TASmodPackets; import com.minecrafttas.tasmod.savestates.SavestateHandlerServer.SavestateState; import com.minecrafttas.tasmod.savestates.exceptions.LoadstateException; diff --git a/src/main/java/com/minecrafttas/tasmod/commands/CommandFullRecord.java b/src/main/java/com/minecrafttas/tasmod/commands/CommandFullRecord.java index 09a1cfe5..9ab51422 100644 --- a/src/main/java/com/minecrafttas/tasmod/commands/CommandFullRecord.java +++ b/src/main/java/com/minecrafttas/tasmod/commands/CommandFullRecord.java @@ -2,8 +2,8 @@ import com.minecrafttas.tasmod.TASmod; import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; -import com.minecrafttas.tasmod.networking.TASmodPackets; import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; +import com.minecrafttas.tasmod.registries.TASmodPackets; import com.minecrafttas.tasmod.savestates.SavestateHandlerServer.SavestateState; import com.minecrafttas.tasmod.savestates.exceptions.SavestateException; @@ -28,7 +28,6 @@ public String getUsage(ICommandSender sender) { @Override public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { - sender.sendMessage(new TextComponentString(TextFormatting.RED+"This feature doesn't work at the moment!")); try { TASmod.savestateHandlerServer.saveState(0, false); } catch (SavestateException e) { diff --git a/src/main/java/com/minecrafttas/tasmod/commands/CommandLoadTAS.java b/src/main/java/com/minecrafttas/tasmod/commands/CommandLoadTAS.java index 0e2ef1d2..cbd8e71e 100644 --- a/src/main/java/com/minecrafttas/tasmod/commands/CommandLoadTAS.java +++ b/src/main/java/com/minecrafttas/tasmod/commands/CommandLoadTAS.java @@ -1,15 +1,12 @@ package com.minecrafttas.tasmod.commands; -import java.io.File; -import java.io.FileFilter; -import java.util.ArrayList; import java.util.List; +import java.util.concurrent.TimeoutException; import com.minecrafttas.tasmod.TASmod; import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; -import com.minecrafttas.tasmod.networking.TASmodPackets; +import com.minecrafttas.tasmod.registries.TASmodPackets; -import net.minecraft.client.Minecraft; import net.minecraft.command.CommandBase; import net.minecraft.command.CommandException; import net.minecraft.command.ICommandSender; @@ -28,27 +25,27 @@ public String getName() { @Override public String getUsage(ICommandSender sender) { - return "/load "; + return "/load [flavor]"; } @Override public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { - sender.sendMessage(new TextComponentString(TextFormatting.RED + "This feature does not work at the moment!")); if (sender instanceof EntityPlayer) { if (sender.canUseCommand(2, "load")) { if (args.length < 1) { sender.sendMessage(new TextComponentString(TextFormatting.RED + "Please add a filename, " + getUsage(sender))); - } else { - String name = ""; - String spacer = " "; - for (int i = 0; i < args.length; i++) { - if (i == args.length - 1) { - spacer=""; - } - name=name.concat(args[i]+spacer); + } else if(args.length == 1) { + String filename = args[0]; + try { + TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.PLAYBACK_LOAD).writeString(filename).writeString("")); + } catch (Exception e) { + e.printStackTrace(); } + } else if (args.length == 2) { + String filename = args[0]; + String flavorname = args[1]; try { - TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.PLAYBACK_LOAD).writeString(name)); + TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.PLAYBACK_LOAD).writeString(filename).writeString(flavorname)); } catch (Exception e) { e.printStackTrace(); } @@ -63,28 +60,39 @@ public void execute(MinecraftServer server, ICommandSender sender, String[] args public List getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, BlockPos targetPos) { List tab; if (args.length == 1) { - tab = getFilenames(); + try { + tab = TASmod.tabCompletionUtils.getTASfileList(getCommandSenderAsPlayer(sender).getName()); + } catch (TimeoutException e) { + sender.sendMessage(new TextComponentString(TextFormatting.RED + "Failed to fetch the file list after 2 seconds, something went wrong")); + TASmod.LOGGER.catching(e); + return super.getTabCompletions(server, sender, args, targetPos); + } catch (Exception e) { + sender.sendMessage(new TextComponentString(TextFormatting.RED + "Something went wrong with Tab Completions")); + TASmod.LOGGER.catching(e); + return super.getTabCompletions(server, sender, args, targetPos); + } + if (tab.isEmpty()) { sender.sendMessage(new TextComponentString(TextFormatting.RED + "No files in directory")); return super.getTabCompletions(server, sender, args, targetPos); } return getListOfStringsMatchingLastWord(args, tab); + + } else if (args.length == 2) { + try { + tab = TASmod.tabCompletionUtils.getFlavorList(getCommandSenderAsPlayer(sender).getName()); + } catch (TimeoutException e) { + sender.sendMessage(new TextComponentString(TextFormatting.RED + "Failed to fetch the flavor list after 2 seconds, something went wrong")); + TASmod.LOGGER.catching(e); + return super.getTabCompletions(server, sender, args, targetPos); + } catch (Exception e) { + sender.sendMessage(new TextComponentString(TextFormatting.RED + "Something went wrong with Tab Completions")); + TASmod.LOGGER.catching(e); + return super.getTabCompletions(server, sender, args, targetPos); + } + return getListOfStringsMatchingLastWord(args, tab); + } else return super.getTabCompletions(server, sender, args, targetPos); } - - public List getFilenames() { - List tab = new ArrayList(); - File folder = new File(Minecraft.getMinecraft().mcDataDir, "saves" + File.separator + "tasfiles"); - File[] listOfFiles = folder.listFiles(new FileFilter() { - @Override - public boolean accept(File pathname) { - return pathname.getName().endsWith(".mctas"); - } - }); - for (int i = 0; i < listOfFiles.length; i++) { - tab.add(listOfFiles[i].getName().replaceAll("\\.mctas", "")); - } - return tab; - } } diff --git a/src/main/java/com/minecrafttas/tasmod/commands/CommandPlayUntil.java b/src/main/java/com/minecrafttas/tasmod/commands/CommandPlayUntil.java index d9f284af..21d93cd5 100644 --- a/src/main/java/com/minecrafttas/tasmod/commands/CommandPlayUntil.java +++ b/src/main/java/com/minecrafttas/tasmod/commands/CommandPlayUntil.java @@ -2,7 +2,7 @@ import com.minecrafttas.tasmod.TASmod; import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; -import com.minecrafttas.tasmod.networking.TASmodPackets; +import com.minecrafttas.tasmod.registries.TASmodPackets; import net.minecraft.command.CommandBase; import net.minecraft.command.CommandException; diff --git a/src/main/java/com/minecrafttas/tasmod/commands/CommandRestartAndPlay.java b/src/main/java/com/minecrafttas/tasmod/commands/CommandRestartAndPlay.java index 60bd5f6d..deb96bc6 100644 --- a/src/main/java/com/minecrafttas/tasmod/commands/CommandRestartAndPlay.java +++ b/src/main/java/com/minecrafttas/tasmod/commands/CommandRestartAndPlay.java @@ -8,8 +8,8 @@ import com.minecrafttas.tasmod.TASmod; import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; -import com.minecrafttas.tasmod.networking.TASmodPackets; import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; +import com.minecrafttas.tasmod.registries.TASmodPackets; import com.minecrafttas.tasmod.savestates.exceptions.LoadstateException; import net.minecraft.client.Minecraft; diff --git a/src/main/java/com/minecrafttas/tasmod/commands/CommandSaveTAS.java b/src/main/java/com/minecrafttas/tasmod/commands/CommandSaveTAS.java index 93063f98..1c4162af 100644 --- a/src/main/java/com/minecrafttas/tasmod/commands/CommandSaveTAS.java +++ b/src/main/java/com/minecrafttas/tasmod/commands/CommandSaveTAS.java @@ -1,16 +1,13 @@ package com.minecrafttas.tasmod.commands; -import java.io.File; -import java.io.FileFilter; -import java.util.ArrayList; import java.util.List; +import java.util.concurrent.TimeoutException; import com.google.common.collect.ImmutableList; import com.minecrafttas.tasmod.TASmod; import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; -import com.minecrafttas.tasmod.networking.TASmodPackets; +import com.minecrafttas.tasmod.registries.TASmodPackets; -import net.minecraft.client.Minecraft; import net.minecraft.command.CommandBase; import net.minecraft.command.CommandException; import net.minecraft.command.ICommandSender; @@ -31,7 +28,7 @@ public String getName() { @Override public String getUsage(ICommandSender sender) { - return "/save "; + return "/save [flavor]"; } @Override @@ -41,22 +38,23 @@ public List getAliases() { @Override public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { - sender.sendMessage(new TextComponentString(TextFormatting.RED + "This feature does not work at the moment!")); + if (sender instanceof EntityPlayer) { if (sender.canUseCommand(2, "save")) { if (args.length < 1) { - sender.sendMessage(new TextComponentString(TextFormatting.RED + "Please add a filename, " + getUsage(sender))); - } else { - String name = ""; - String spacer = " "; - for (int i = 0; i < args.length; i++) { - if (i == args.length - 1) { - spacer = ""; - } - name = name.concat(args[i] + spacer); + sender.sendMessage(new TextComponentString(TextFormatting.RED + "Command must contain a filename " + getUsage(sender))); + } else if (args.length == 1) { + String filename = args[0]; + try { + TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.PLAYBACK_SAVE).writeString(filename).writeString("")); + } catch (Exception e) { + e.printStackTrace(); } + } else if (args.length == 2) { + String filename = args[0]; + String flavorname = args[1]; try { - TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.PLAYBACK_SAVE).writeString(name)); + TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.PLAYBACK_SAVE).writeString(filename).writeString(flavorname)); } catch (Exception e) { e.printStackTrace(); } @@ -69,38 +67,48 @@ public void execute(MinecraftServer server, ICommandSender sender, String[] args @Override public List getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, BlockPos targetPos) { - List tab; + List tab = null; if (args.length == 1) { if (!check) { sender.sendMessage(new TextComponentString(TextFormatting.BOLD + "" + TextFormatting.RED + "WARNING!" + TextFormatting.RESET + TextFormatting.RED + " Existing filenames will be overwritten! /fail to abort the recording if you accidentally started one")); check = true; } - tab = getFilenames(); + try { + tab = TASmod.tabCompletionUtils.getTASfileList(getCommandSenderAsPlayer(sender).getName()); + } catch (TimeoutException e) { + sender.sendMessage(new TextComponentString(TextFormatting.RED + "Failed to fetch the file list after 2 seconds, something went wrong")); + TASmod.LOGGER.catching(e); + return super.getTabCompletions(server, sender, args, targetPos); + } catch (Exception e) { + sender.sendMessage(new TextComponentString(TextFormatting.RED + "Something went wrong with Tab Completions")); + TASmod.LOGGER.catching(e); + return super.getTabCompletions(server, sender, args, targetPos); + } + if (tab.isEmpty()) { sender.sendMessage(new TextComponentString(TextFormatting.RED + "No files in directory")); return super.getTabCompletions(server, sender, args, targetPos); } return getListOfStringsMatchingLastWord(args, tab); + + } else if (args.length == 2) { + try { + tab = TASmod.tabCompletionUtils.getFlavorList(getCommandSenderAsPlayer(sender).getName()); + } catch (TimeoutException e) { + sender.sendMessage(new TextComponentString(TextFormatting.RED + "Failed to fetch the flavor list after 2 seconds, something went wrong")); + TASmod.LOGGER.catching(e); + return super.getTabCompletions(server, sender, args, targetPos); + } catch (Exception e) { + sender.sendMessage(new TextComponentString(TextFormatting.RED + "Something went wrong with Tab Completions")); + TASmod.LOGGER.catching(e); + return super.getTabCompletions(server, sender, args, targetPos); + } + return getListOfStringsMatchingLastWord(args, tab); + } else return super.getTabCompletions(server, sender, args, targetPos); } - public List getFilenames() { - List tab = new ArrayList(); - File folder = new File(Minecraft.getMinecraft().mcDataDir, "saves" + File.separator + "tasfiles"); - - File[] listOfFiles = folder.listFiles(new FileFilter() { - @Override - public boolean accept(File pathname) { - return pathname.getName().endsWith(".mctas"); - } - }); - for (int i = 0; i < listOfFiles.length; i++) { - tab.add(listOfFiles[i].getName().replaceAll("\\.mctas", "")); - } - return tab; - } - @Override public int getRequiredPermissionLevel() { return 2; diff --git a/src/main/java/com/minecrafttas/tasmod/commands/TabCompletionUtils.java b/src/main/java/com/minecrafttas/tasmod/commands/TabCompletionUtils.java new file mode 100644 index 00000000..69eae109 --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/commands/TabCompletionUtils.java @@ -0,0 +1,115 @@ +package com.minecrafttas.tasmod.commands; + +import static com.minecrafttas.tasmod.registries.TASmodPackets.COMMAND_FLAVORLIST; +import static com.minecrafttas.tasmod.registries.TASmodPackets.COMMAND_TASFILELIST; + +import java.io.File; +import java.io.FileFilter; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import com.minecrafttas.mctcommon.networking.exception.PacketNotImplementedException; +import com.minecrafttas.mctcommon.networking.exception.WrongSideException; +import com.minecrafttas.mctcommon.networking.interfaces.ClientPacketHandler; +import com.minecrafttas.mctcommon.networking.interfaces.PacketID; +import com.minecrafttas.mctcommon.networking.interfaces.ServerPacketHandler; +import com.minecrafttas.tasmod.TASmod; +import com.minecrafttas.tasmod.TASmodClient; +import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; +import com.minecrafttas.tasmod.registries.TASmodAPIRegistry; +import com.minecrafttas.tasmod.registries.TASmodPackets; + +import net.minecraft.client.Minecraft; + +public class TabCompletionUtils implements ServerPacketHandler, ClientPacketHandler { + + private volatile CompletableFuture> fileList = null; + private volatile CompletableFuture> flavorList = null; + + @Override + public PacketID[] getAcceptedPacketIDs() { + return new PacketID[] { COMMAND_TASFILELIST, COMMAND_FLAVORLIST }; + } + + //======== SERVER SIDE + + @Override + public void onServerPacket(PacketID id, ByteBuffer buf, String username) throws PacketNotImplementedException, WrongSideException, Exception { + TASmodPackets packet = (TASmodPackets) id; + switch (packet) { + case COMMAND_TASFILELIST: + String filenames = TASmodBufferBuilder.readString(buf); + fileList.complete(Arrays.asList(filenames.split("\\|"))); + break; + case COMMAND_FLAVORLIST: + String flavornames = TASmodBufferBuilder.readString(buf); + flavorList.complete(Arrays.asList(flavornames.split("\\|"))); + break; + default: + break; + } + } + + public List getTASfileList(String playername) throws InterruptedException, ExecutionException, TimeoutException { + fileList = new CompletableFuture<>(); + try { + TASmod.server.sendTo(playername, new TASmodBufferBuilder(COMMAND_TASFILELIST)); + } catch (Exception e) { + TASmod.LOGGER.catching(e); + } + return fileList.get(2, TimeUnit.SECONDS); + } + + public List getFlavorList(String playername) throws InterruptedException, ExecutionException, TimeoutException { + flavorList = new CompletableFuture<>(); + try { + TASmod.server.sendTo(playername, new TASmodBufferBuilder(COMMAND_FLAVORLIST)); + } catch (Exception e) { + TASmod.LOGGER.catching(e); + } + return flavorList.get(2, TimeUnit.SECONDS); + } + + //======== CLIENT SIDE + + @Override + public void onClientPacket(PacketID id, ByteBuffer buf, String username) throws PacketNotImplementedException, WrongSideException, Exception { + TASmodPackets packet = (TASmodPackets) id; + switch (packet) { + case COMMAND_TASFILELIST: + String filenames = String.join("|", getFilenames()); + TASmodClient.client.send(new TASmodBufferBuilder(COMMAND_TASFILELIST).writeString(filenames)); + break; + + case COMMAND_FLAVORLIST: + String flavornames = String.join("|", TASmodAPIRegistry.SERIALISER_FLAVOR.getFlavorNames()); + TASmodClient.client.send(new TASmodBufferBuilder(COMMAND_FLAVORLIST).writeString(flavornames)); + break; + + default: + break; + } + } + + private List getFilenames() { + List tab = new ArrayList(); + File folder = new File(Minecraft.getMinecraft().mcDataDir, "saves" + File.separator + "tasfiles"); + + File[] listOfFiles = folder.listFiles(new FileFilter() { + @Override + public boolean accept(File pathname) { + return pathname.getName().endsWith(".mctas"); + } + }); + for (int i = 0; i < listOfFiles.length; i++) { + tab.add(listOfFiles[i].getName().replaceFirst("\\.mctas$", "")); + } + return tab; + } +} diff --git a/src/main/java/com/minecrafttas/tasmod/events/EventClient.java b/src/main/java/com/minecrafttas/tasmod/events/EventClient.java index 4314ce50..8cc771e4 100644 --- a/src/main/java/com/minecrafttas/tasmod/events/EventClient.java +++ b/src/main/java/com/minecrafttas/tasmod/events/EventClient.java @@ -1,12 +1,6 @@ package com.minecrafttas.tasmod.events; import com.minecrafttas.mctcommon.events.EventListenerRegistry.EventBase; -import com.minecrafttas.tasmod.virtual.VirtualCameraAngle; -import com.minecrafttas.tasmod.virtual.VirtualInput.VirtualCameraAngleInput; -import com.minecrafttas.tasmod.virtual.VirtualInput.VirtualKeyboardInput; -import com.minecrafttas.tasmod.virtual.VirtualInput.VirtualMouseInput; -import com.minecrafttas.tasmod.virtual.VirtualKeyboard; -import com.minecrafttas.tasmod.virtual.VirtualMouse; import net.minecraft.client.Minecraft; @@ -39,71 +33,4 @@ public static interface EventClientTickPost extends EventBase { */ public void onClientTickPost(Minecraft mc); } - - /** - * Fired when the tickrate changes on the client side - */ - @FunctionalInterface - public static interface EventClientTickrateChange extends EventBase { - - /** - * Fired at the end of a client tick - */ - public void onClientTickrateChange(float tickrate); - } - - - /** - * Fired when the {@link VirtualKeyboardInput#currentKeyboard} is updated - * - * @see VirtualKeyboardInput#nextKeyboardTick() - */ - @FunctionalInterface - public static interface EventVirtualKeyboardTick extends EventBase { - - /** - * Fired when the {@link VirtualKeyboard} ticks - * - * @param vkeyboard The {@link VirtualKeyboardInput#nextKeyboard} that is supposed to be pressed - * @returns The redirected keyboard - * @see VirtualKeyboardInput#nextKeyboardTick() - */ - public VirtualKeyboard onVirtualKeyboardTick(VirtualKeyboard vkeyboard); - } - - /** - * Fired when the {@link VirtualMouseInput#currentMouse} is updated - * - * @see VirtualMouseInput#nextMouseTick() - */ - @FunctionalInterface - public static interface EventVirtualMouseTick extends EventBase { - - /** - * Fired when the {@link VirtualMouseInput#currentMouse} is updated - * - * @param vmouse The {@link VirtualMouseInput#nextMouse} that is supposed to be pressed - * @returns The redirected mouse - * @see VirtualMouseInput#nextMouseTick() - */ - public VirtualMouse onVirtualMouseTick(VirtualMouse vmouse); - } - - /** - * Fired when the {@link VirtualCameraAngleInput#currentCameraAngle} is updated - * - * @see VirtualCameraAngleInput#nextCameraTick() - */ - @FunctionalInterface - public static interface EventVirtualCameraAngleTick extends EventBase { - - /** - * Fired when the {@link VirtualCameraAngleInput#currentCameraAngle} is updated - * - * @param vcamera The {@link VirtualCameraAngleInput#nextCameraAngle} - * @returns The redirected cameraAngle - * @see VirtualCameraAngleInput#nextCameraTick() - */ - public VirtualCameraAngle onVirtualCameraTick(VirtualCameraAngle vcamera); - } } diff --git a/src/main/java/com/minecrafttas/tasmod/events/EventPlaybackClient.java b/src/main/java/com/minecrafttas/tasmod/events/EventPlaybackClient.java index df7e3c83..8608d9d1 100644 --- a/src/main/java/com/minecrafttas/tasmod/events/EventPlaybackClient.java +++ b/src/main/java/com/minecrafttas/tasmod/events/EventPlaybackClient.java @@ -3,21 +3,22 @@ import com.minecrafttas.mctcommon.events.EventListenerRegistry.EventBase; import com.minecrafttas.tasmod.playback.PlaybackControllerClient; import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TickContainer; public interface EventPlaybackClient { /** * Fired when - * {@link PlaybackControllerClient#setTASStateClient(com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate, boolean)} - * is called + * {@link PlaybackControllerClient#setTASStateClient(com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate, boolean) + * PlaybackControllerClient#setTASStateClient} is called */ @FunctionalInterface - public static interface EventControllerStateChange extends EventBase { + public interface EventControllerStateChange extends EventBase { /** * Fired when - * {@link PlaybackControllerClient#setTASStateClient(com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate, boolean)} - * is called + * {@link PlaybackControllerClient#setTASStateClient(com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate, boolean) + * PlaybackControllerClient#setTASStateClient} is called * * @param newstate The new state that the playback controller is about to be set * to @@ -30,8 +31,57 @@ public static interface EventControllerStateChange extends EventBase { * Fired after a player joined the world with a playback/recording running */ @FunctionalInterface - public static interface EventPlaybackJoinedWorld extends EventBase { - + public interface EventPlaybackJoinedWorld extends EventBase { + + /** + * Fired after a player joined the world with a playback/recording running + * + * @param state The {@link PlaybackControllerClient#state state} of the + * {@link PlaybackControllerClient} when the player joined the + * world + */ public void onPlaybackJoinedWorld(TASstate state); } + + /** + * Fired when a tick is being recorded + */ + @FunctionalInterface + public interface EventRecordTick extends EventBase { + + /** + * Fired when a tick is being recorded + * + * @param index The index of the tick that is being recorded + * @param container The {@link TickContainer} that is being recorded + */ + public void onRecordTick(long index, TickContainer container); + } + + /** + * Fired when a tick is being played back + */ + @FunctionalInterface + public interface EventPlaybackTick extends EventBase { + + /** + * Fired when a tick is being recorded + * + * @param index The index of the tick that is being recorded + * @param container The {@link TickContainer} that is being recorded + */ + public void onPlaybackTick(long index, TickContainer container); + } + + /** + * Fired when a recording is cleared + */ + @FunctionalInterface + public interface EventRecordClear extends EventBase { + + /** + * Fired when a recording is cleared + */ + public void onClear(); + } } diff --git a/src/main/java/com/minecrafttas/tasmod/events/EventSavestate.java b/src/main/java/com/minecrafttas/tasmod/events/EventSavestate.java new file mode 100644 index 00000000..58791881 --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/events/EventSavestate.java @@ -0,0 +1,58 @@ +package com.minecrafttas.tasmod.events; + +import java.io.File; + +import com.minecrafttas.mctcommon.events.EventListenerRegistry.EventBase; + +public interface EventSavestate { + + /** + * Fired when saving a savestate, before the savestate folder is copied + */ + @FunctionalInterface + interface EventServerSavestate extends EventBase { + + /** + * Fired when saving a savestate, before the savestate folder is copied + * + * @param index The savestate index for this savestate + * @param target Target folder, where the savestate is copied to + * @param current The current folder that will be copied from + */ + public void onServerSavestate(int index, File target, File current); + } + + /** + * Fired when loading a savestate, before the savestate folder is copied + */ + @FunctionalInterface + interface EventServerLoadstate extends EventBase { + + /** + * Fired when loading a savestate, before the savestate folder is copied + * + * @param index The savestate index for this loadstate + * @param target Target folder, where the savestate is copied to + * @param current The current folder that will be copied from + */ + public void onServerLoadstate(int index, File target, File current); + } + + /** + * Fired one tick after a loadstate was carried out + */ + @FunctionalInterface + interface EventServerCompleteLoadstate extends EventBase { + + /** + * Fired one tick after a loadstate was carried out + */ + public void onServerLoadstateComplete(); + } + + @FunctionalInterface + interface EventClientSavestate extends EventBase { + + public void onClientSavestate(); + } +} diff --git a/src/main/java/com/minecrafttas/tasmod/events/EventServer.java b/src/main/java/com/minecrafttas/tasmod/events/EventServer.java index a3c15f63..7ddf8fad 100644 --- a/src/main/java/com/minecrafttas/tasmod/events/EventServer.java +++ b/src/main/java/com/minecrafttas/tasmod/events/EventServer.java @@ -3,80 +3,22 @@ import com.minecrafttas.mctcommon.events.EventListenerRegistry.EventBase; import net.minecraft.server.MinecraftServer; -import java.io.File; - /** * TASmod specific events fired on the server side * * @author Scribble */ public interface EventServer { - - /** - * Fired when saving a savestate, before the savestate folder is copied - */ - @FunctionalInterface - public static interface EventSavestate extends EventBase { - - /** - * Fired when saving a savestate, before the savestate folder is copied - * @param index The savestate index for this savestate - * @param target Target folder, where the savestate is copied to - * @param current The current folder that will be copied from - */ - public void onSavestateEvent(int index, File target, File current); - } - - /** - * Fired when loading a savestate, before the savestate folder is copied - */ - @FunctionalInterface - public static interface EventLoadstate extends EventBase { - - /** - * Fired when loading a savestate, before the savestate folder is copied - * @param index The savestate index for this loadstate - * @param target Target folder, where the savestate is copied to - * @param current The current folder that will be copied from - */ - public void onLoadstateEvent(int index, File target, File current); - } - - /** - * Fired one tick after a loadstate was carried out - */ - @FunctionalInterface - public static interface EventCompleteLoadstate extends EventBase{ - - /** - * Fired one tick after a loadstate was carried out - */ - public void onLoadstateComplete(); - } - /** * Fired at the end of a server tick */ @FunctionalInterface - public static interface EventServerTickPost extends EventBase{ - + public static interface EventServerTickPost extends EventBase { + /** * Fired at the end of a server tick */ public void onServerTickPost(MinecraftServer minecraftServer); } - - - /** - * Fired when the tickrate changes on the server side - */ - @FunctionalInterface - public static interface EventServerTickrateChange extends EventBase{ - - /** - * Fired at the end of a client tick - */ - public void onServerTickrateChange(float tickrate); - } } diff --git a/src/main/java/com/minecrafttas/tasmod/events/EventTickratechanger.java b/src/main/java/com/minecrafttas/tasmod/events/EventTickratechanger.java new file mode 100644 index 00000000..bae1cba4 --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/events/EventTickratechanger.java @@ -0,0 +1,31 @@ +package com.minecrafttas.tasmod.events; + +import com.minecrafttas.mctcommon.events.EventListenerRegistry.EventBase; + +public interface EventTickratechanger { + + /** + * Fired when the tickrate changes on the client side + */ + @FunctionalInterface + interface EventClientTickrateChange extends EventBase { + + /** + * Fired at the end of a client tick + */ + public void onClientTickrateChange(float tickrate); + } + + /** + * Fired when the tickrate changes on the server side + */ + @FunctionalInterface + interface EventServerTickrateChange extends EventBase { + + /** + * Fired at the end of a client tick + */ + public void onServerTickrateChange(float tickrate); + } + +} diff --git a/src/main/java/com/minecrafttas/tasmod/events/EventVirtualInput.java b/src/main/java/com/minecrafttas/tasmod/events/EventVirtualInput.java new file mode 100644 index 00000000..0aa52aa5 --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/events/EventVirtualInput.java @@ -0,0 +1,68 @@ +package com.minecrafttas.tasmod.events; + +import com.minecrafttas.mctcommon.events.EventListenerRegistry.EventBase; +import com.minecrafttas.tasmod.virtual.VirtualCameraAngle; +import com.minecrafttas.tasmod.virtual.VirtualInput.VirtualCameraAngleInput; +import com.minecrafttas.tasmod.virtual.VirtualInput.VirtualKeyboardInput; +import com.minecrafttas.tasmod.virtual.VirtualInput.VirtualMouseInput; +import com.minecrafttas.tasmod.virtual.VirtualKeyboard; +import com.minecrafttas.tasmod.virtual.VirtualMouse; + +public interface EventVirtualInput { + + /** + * Fired when the {@link VirtualKeyboardInput#currentKeyboard} is updated + * + * @see VirtualKeyboardInput#nextKeyboardTick() + */ + @FunctionalInterface + interface EventVirtualKeyboardTick extends EventBase { + + /** + * Fired when the {@link VirtualKeyboard} ticks + * + * @param vkeyboard The {@link VirtualKeyboardInput#nextKeyboard} that is + * supposed to be pressed + * @returns The redirected keyboard + * @see VirtualKeyboardInput#nextKeyboardTick() + */ + public VirtualKeyboard onVirtualKeyboardTick(VirtualKeyboard vkeyboard); + } + + /** + * Fired when the {@link VirtualMouseInput#currentMouse} is updated + * + * @see VirtualMouseInput#nextMouseTick() + */ + @FunctionalInterface + interface EventVirtualMouseTick extends EventBase { + + /** + * Fired when the {@link VirtualMouseInput#currentMouse} is updated + * + * @param vmouse The {@link VirtualMouseInput#nextMouse} that is supposed to be + * pressed + * @returns The redirected mouse + * @see VirtualMouseInput#nextMouseTick() + */ + public VirtualMouse onVirtualMouseTick(VirtualMouse vmouse); + } + + /** + * Fired when the {@link VirtualCameraAngleInput#currentCameraAngle} is updated + * + * @see VirtualCameraAngleInput#nextCameraTick() + */ + @FunctionalInterface + interface EventVirtualCameraAngleTick extends EventBase { + + /** + * Fired when the {@link VirtualCameraAngleInput#currentCameraAngle} is updated + * + * @param vcamera The {@link VirtualCameraAngleInput#nextCameraAngle} + * @returns The redirected cameraAngle + * @see VirtualCameraAngleInput#nextCameraTick() + */ + public VirtualCameraAngle onVirtualCameraTick(VirtualCameraAngle vcamera); + } +} diff --git a/src/main/java/com/minecrafttas/tasmod/events/TASmodEventListener.java b/src/main/java/com/minecrafttas/tasmod/events/TASmodEventListener.java deleted file mode 100644 index e1dfea63..00000000 --- a/src/main/java/com/minecrafttas/tasmod/events/TASmodEventListener.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.minecrafttas.tasmod.events; - -import com.minecrafttas.mctcommon.events.EventListenerRegistry; - -public class TASmodEventListener extends EventListenerRegistry{ -} diff --git a/src/main/java/com/minecrafttas/tasmod/gui/InfoHud.java b/src/main/java/com/minecrafttas/tasmod/gui/InfoHud.java index 13cbda8c..20cb8f5c 100644 --- a/src/main/java/com/minecrafttas/tasmod/gui/InfoHud.java +++ b/src/main/java/com/minecrafttas/tasmod/gui/InfoHud.java @@ -17,9 +17,8 @@ import com.minecrafttas.tasmod.TASmod; import com.minecrafttas.tasmod.TASmodClient; import com.minecrafttas.tasmod.events.EventClient.EventDrawHotbar; -import com.minecrafttas.tasmod.monitoring.DesyncMonitoring; -import com.minecrafttas.tasmod.playback.ControlByteHandler; import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; +import com.minecrafttas.tasmod.playback.filecommands.integrated.DesyncMonitorFileCommandExtension; import com.mojang.realmsclient.gui.ChatFormatting; import net.minecraft.client.Minecraft; @@ -373,7 +372,7 @@ public boolean checkInit() { if (configuration.getProperty(title + "_x", "err").equals("err")) setDefaults(title, y); lists.add(new InfoLabel(title, Integer.parseInt(configuration.getProperty(title + "_x")), Integer.parseInt(configuration.getProperty(title + "_y")), Boolean.parseBoolean(configuration.getProperty(title + "_visible")), Boolean.parseBoolean(configuration.getProperty(title + "_rect")), () -> { if (Minecraft.getMinecraft().currentScreen == this) return "Desync"; - DesyncMonitoring dMonitor=TASmodClient.controller.desyncMonitor; + DesyncMonitorFileCommandExtension dMonitor=TASmodClient.desyncMonitorFileCommandExtension; return dMonitor.getStatus(Minecraft.getMinecraft().player); })); @@ -382,7 +381,7 @@ public boolean checkInit() { if (configuration.getProperty(title + "_x", "err").equals("err")) setDefaults(title, y); lists.add(new InfoLabel(title, Integer.parseInt(configuration.getProperty(title + "_x")), Integer.parseInt(configuration.getProperty(title + "_y")), Boolean.parseBoolean(configuration.getProperty(title + "_visible")), Boolean.parseBoolean(configuration.getProperty(title + "_rect")), () -> { if (Minecraft.getMinecraft().currentScreen == this) return "Desync Motion"; - DesyncMonitoring dMonitor=TASmodClient.controller.desyncMonitor; + DesyncMonitorFileCommandExtension dMonitor=TASmodClient.desyncMonitorFileCommandExtension; return dMonitor.getMotion(); })); @@ -391,26 +390,16 @@ public boolean checkInit() { if (configuration.getProperty(title + "_x", "err").equals("err")) setDefaults(title, y); lists.add(new InfoLabel(title, Integer.parseInt(configuration.getProperty(title + "_x")), Integer.parseInt(configuration.getProperty(title + "_y")), Boolean.parseBoolean(configuration.getProperty(title + "_visible")), Boolean.parseBoolean(configuration.getProperty(title + "_rect")), () -> { if (Minecraft.getMinecraft().currentScreen == this) return "Desync Position"; - DesyncMonitoring dMonitor=TASmodClient.controller.desyncMonitor; + DesyncMonitorFileCommandExtension dMonitor=TASmodClient.desyncMonitorFileCommandExtension; return dMonitor.getPos(); })); - y += 14; - title = "ktrng_desync"; - if (configuration.getProperty(title + "_x", "err").equals("err")) setDefaults(title, y); - lists.add(new InfoLabel(title, Integer.parseInt(configuration.getProperty(title + "_x")), Integer.parseInt(configuration.getProperty(title + "_y")), Boolean.parseBoolean(configuration.getProperty(title + "_visible")), Boolean.parseBoolean(configuration.getProperty(title + "_rect")), () -> { - if (Minecraft.getMinecraft().currentScreen == this) return "Desync KTRNG"; - DesyncMonitoring dMonitor=TASmodClient.controller.desyncMonitor; - return dMonitor.getSeed(); - })); - - y = height - 28; title = "playback_index"; if (configuration.getProperty(title + "_x", "err").equals("err")) setDefaults(title, y, true); lists.add(new InfoLabel(title, Integer.parseInt(configuration.getProperty(title + "_x")), Integer.parseInt(configuration.getProperty(title + "_y")), Boolean.parseBoolean(configuration.getProperty(title + "_visible")), Boolean.parseBoolean(configuration.getProperty(title + "_rect")), () -> { if (Minecraft.getMinecraft().currentScreen == this) return "PlaybackIndex"; - return Integer.toString(TASmodClient.controller.index()); + return Long.toString(TASmodClient.controller.index()); })); y = height - 14; @@ -433,10 +422,11 @@ public boolean checkInit() { @Override public void onDrawHotbar() { // render custom info box if control byte is set - if (!ControlByteHandler.hideInfoBox && TASmodClient.controller.isPlayingback()) - drawRectWithText(ControlByteHandler.text, 10, 10, true); + String text = TASmodClient.labelFileCommandExtension.getLabelText(); + if (!text.isEmpty() && TASmodClient.controller.isPlayingback()) + drawRectWithText(text, 10, 10, true); // skip rendering of control byte is set - if (!ControlByteHandler.shouldRenderHud && TASmodClient.controller.isPlayingback()) + if (!TASmodClient.optionsFileCommandExtension.shouldRenderHud() && TASmodClient.controller.isPlayingback()) return; int xpos=40; int ypos=190; diff --git a/src/main/java/com/minecrafttas/tasmod/handlers/InterpolationHandler.java b/src/main/java/com/minecrafttas/tasmod/handlers/InterpolationHandler.java index 0a904cab..cafe1ed5 100644 --- a/src/main/java/com/minecrafttas/tasmod/handlers/InterpolationHandler.java +++ b/src/main/java/com/minecrafttas/tasmod/handlers/InterpolationHandler.java @@ -2,8 +2,7 @@ import com.minecrafttas.mctcommon.events.EventClient.EventCamera; import com.minecrafttas.tasmod.TASmodClient; -import com.minecrafttas.tasmod.playback.ControlByteHandler; -import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TickInputContainer; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TickContainer; import net.minecraft.client.Minecraft; import net.minecraft.util.math.MathHelper; @@ -22,8 +21,8 @@ public class InterpolationHandler implements EventCamera { @Override public CameraData onCameraEvent(CameraData dataIn) { - if (TASmodClient.controller.isPlayingback() && ControlByteHandler.shouldInterpolate) { - TickInputContainer input = TASmodClient.controller.get(); + if (TASmodClient.controller.isPlayingback() /*&& ControlByteHandler.shouldInterpolate*/) { + TickContainer input = TASmodClient.controller.get(); if (input == null) return dataIn; float nextPitch = input.getCameraAngle().getPitch(); diff --git a/src/main/java/com/minecrafttas/tasmod/handlers/LoadingScreenHandler.java b/src/main/java/com/minecrafttas/tasmod/handlers/LoadingScreenHandler.java index 92bcb00b..7d164842 100644 --- a/src/main/java/com/minecrafttas/tasmod/handlers/LoadingScreenHandler.java +++ b/src/main/java/com/minecrafttas/tasmod/handlers/LoadingScreenHandler.java @@ -16,7 +16,6 @@ * Handles logic during a loading screen to transition between states. * * @author Scribble - * */ public class LoadingScreenHandler implements EventLaunchIntegratedServer, EventClientGameLoop, EventDoneLoadingWorld { diff --git a/src/main/java/com/minecrafttas/tasmod/ktrng/KTRNGMonitor.java b/src/main/java/com/minecrafttas/tasmod/ktrng/KTRNGMonitor.java index 0a16d9e9..baa32a5c 100644 --- a/src/main/java/com/minecrafttas/tasmod/ktrng/KTRNGMonitor.java +++ b/src/main/java/com/minecrafttas/tasmod/ktrng/KTRNGMonitor.java @@ -1,6 +1,6 @@ package com.minecrafttas.tasmod.ktrng; - +@Deprecated public class KTRNGMonitor { // @CaptureRandomness(name = "jukeboxRecordDropPosition") diff --git a/src/main/java/com/minecrafttas/tasmod/ktrng/KillTheRNGHandler.java b/src/main/java/com/minecrafttas/tasmod/ktrng/KillTheRNGHandler.java index f6643fc8..b2669838 100644 --- a/src/main/java/com/minecrafttas/tasmod/ktrng/KillTheRNGHandler.java +++ b/src/main/java/com/minecrafttas/tasmod/ktrng/KillTheRNGHandler.java @@ -6,17 +6,17 @@ import com.minecrafttas.mctcommon.events.EventClient.EventPlayerJoinedClientSide; import com.minecrafttas.mctcommon.events.EventServer.EventServerTick; -import com.minecrafttas.mctcommon.server.Client.Side; -import com.minecrafttas.mctcommon.server.exception.PacketNotImplementedException; -import com.minecrafttas.mctcommon.server.exception.WrongSideException; -import com.minecrafttas.mctcommon.server.interfaces.ClientPacketHandler; -import com.minecrafttas.mctcommon.server.interfaces.PacketID; -import com.minecrafttas.mctcommon.server.interfaces.ServerPacketHandler; +import com.minecrafttas.mctcommon.networking.Client.Side; +import com.minecrafttas.mctcommon.networking.exception.PacketNotImplementedException; +import com.minecrafttas.mctcommon.networking.exception.WrongSideException; +import com.minecrafttas.mctcommon.networking.interfaces.ClientPacketHandler; +import com.minecrafttas.mctcommon.networking.interfaces.PacketID; +import com.minecrafttas.mctcommon.networking.interfaces.ServerPacketHandler; import com.minecrafttas.tasmod.TASmod; import com.minecrafttas.tasmod.TASmodClient; import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; -import com.minecrafttas.tasmod.networking.TASmodPackets; import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; +import com.minecrafttas.tasmod.registries.TASmodPackets; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; @@ -29,6 +29,7 @@ * @author Scribble * */ +@Deprecated public class KillTheRNGHandler implements EventServerTick, EventPlayerJoinedClientSide, ClientPacketHandler, ServerPacketHandler { private boolean isLoaded; diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinEntityRenderer.java b/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinEntityRenderer.java index c736c7f0..761dbff6 100644 --- a/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinEntityRenderer.java +++ b/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinEntityRenderer.java @@ -47,7 +47,7 @@ public class MixinEntityRenderer implements SubtickDuck { /** * Injects into the vanilla camera updating cycle, runs every frame. - * Updates {@link com.minecrafttas.tasmod.virtual.VirtualInput.VirtualCameraAngleInput#nextCameraAngle} + * Updates {@link com.minecrafttas.tasmod.virtual.VirtualInput.VirtualCameraAngleInput#nextCameraAngle VirtualCameraAngleInput#nextCameraAngle} * @param partialTicks The partial ticks of the timer, unused * @param nanoTime The nanoTime, unused * @param ci CBI diff --git a/src/main/java/com/minecrafttas/tasmod/monitoring/DesyncMonitoring.java b/src/main/java/com/minecrafttas/tasmod/monitoring/DesyncMonitoring.java deleted file mode 100644 index 4cf26ba6..00000000 --- a/src/main/java/com/minecrafttas/tasmod/monitoring/DesyncMonitoring.java +++ /dev/null @@ -1,335 +0,0 @@ -package com.minecrafttas.tasmod.monitoring; - -import java.io.File; -import java.io.IOException; -import java.io.Serializable; -import java.util.List; - -import com.dselent.bigarraylist.BigArrayList; -import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.playback.PlaybackControllerClient; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.entity.EntityPlayerSP; -import net.minecraft.util.text.TextFormatting; - -/** - * Stores the players position during recording and compares it with the position during playback - * @author Scribble - * - */ -public class DesyncMonitoring { - - private File tempDir = new File(Minecraft.getMinecraft().mcDataDir.getAbsolutePath() + File.separator + "saves" + File.separator + "tasfiles" + File.separator + "temp" + File.separator + "monitoring"); - - private BigArrayList container = new BigArrayList(tempDir.toString()); - - private MonitorContainer currentValues; - - private PlaybackControllerClient controller; - - /** - * Creates an empty desync monitor - * @param playbackController - */ - public DesyncMonitoring(PlaybackControllerClient playbackController) { - controller = playbackController; - } - - /** - * Parses lines and fills the desync monitor - * @param playbackController - * @param monitorLines - */ - public DesyncMonitoring(PlaybackControllerClient playbackController, List monitorLines) throws IOException{ - this(playbackController); - container = loadFromFile(monitorLines); - } - - public void recordNull(int index) { - if(container.size()<=index) { - container.add(new MonitorContainer(index)); - } else { - container.set(index, new MonitorContainer(index)); - } - } - - public void recordMonitor(int index) { - EntityPlayerSP player = Minecraft.getMinecraft().player; - MonitorContainer values = null; - if(player != null) { - values = new MonitorContainer(index, player.posX, player.posY, player.posZ, player.motionX, player.motionY, player.motionZ, TASmod.ktrngHandler.getGlobalSeedClient()); - } else { - values = new MonitorContainer(index, 0D, 0D, 0D, 0D, 0D, 0D, 0L); - } - - if(container.size()<=index) { - container.add(values); - } else { - container.set(index, values); - } - } - - public void playMonitor(int index) { - currentValues = get(index-1); - } - - private BigArrayList loadFromFile(List monitorLines) throws IOException { - BigArrayList out = new BigArrayList(tempDir.toString()); - int linenumber = 0; - for(String line : monitorLines) { - linenumber++; - String[] split = line.split(" "); - double x = 0; - double y = 0; - double z = 0; - double mx = 0; - double my = 0; - double mz = 0; - long seed = 0; - try { - x = Double.parseDouble(split[0]); - y = Double.parseDouble(split[1]); - z = Double.parseDouble(split[2]); - mx = Double.parseDouble(split[3]); - my = Double.parseDouble(split[4]); - mz = Double.parseDouble(split[5]); - seed = Long.parseLong(split[6]); - } catch (Exception e) { - e.printStackTrace(); - throw new IOException("Error in monitoring section in line "+ linenumber + ". Some value is not a number"); - } - out.add(new MonitorContainer(linenumber, x, y, z, mx, my, mz, seed)); - } - return out; - } - - public MonitorContainer get(int index) { - try { - return container.get(index); - } catch (IndexOutOfBoundsException e) { - return null; - } - } - - private String lastStatus = TextFormatting.GRAY + "Empty"; - - public String getStatus(EntityPlayerSP player) { - if (!controller.isNothingPlaying()) { - if (currentValues != null) { - double[] playervalues = new double[6]; - playervalues[0] = player.posX; - playervalues[1] = player.posY; - playervalues[2] = player.posZ; - playervalues[3] = player.motionX; - playervalues[4] = player.motionY; - playervalues[5] = player.motionZ; - DesyncStatus status = currentValues.getSeverity(controller.index(), playervalues, TASmod.ktrngHandler.getGlobalSeedClient()); - lastStatus = status.getFormat() + status.getText(); - } else { - lastStatus = TextFormatting.GRAY + "Empty"; - } - } - return lastStatus; - } - - private String lastPos = ""; - - public String getPos() { - if(currentValues!=null && !controller.isNothingPlaying()) { - EntityPlayerSP player = Minecraft.getMinecraft().player; - String[] values = new String[3]; - values[0]=getFormattedString(player.posX-currentValues.values[0]); - values[1]=getFormattedString(player.posY-currentValues.values[1]); - values[2]=getFormattedString(player.posZ-currentValues.values[2]); - - String out = ""; - for (String val : values) { - if(val !=null) { - out+=val+" "; - } - } - lastPos=out; - } - return lastPos; - } - - private String lastMotion = ""; - - public String getMotion() { - if(currentValues!=null && !controller.isNothingPlaying()) { - EntityPlayerSP player = Minecraft.getMinecraft().player; - String[] values = new String[3]; - values[0] = getFormattedString(player.motionX - currentValues.values[3]); - values[1] = getFormattedString(player.motionY - currentValues.values[4]); - values[2] = getFormattedString(player.motionZ - currentValues.values[5]); - - String out = ""; - for (String val : values) { - if (val != null) { - out+=val+" "; - } - } - lastMotion = out; - } - return lastMotion; - } - - private String lastSeed = ""; - - public String getSeed() { - if(currentValues!=null && !controller.isNothingPlaying()) { - if(currentValues.seed == TASmod.ktrngHandler.getGlobalSeedClient()) { - lastSeed = ""; - } else { - if(TASmod.ktrngHandler.isLoaded()) { - long distance = 0; //CustomRandom.distance(currentValues.seed, TASmod.ktrngHandler.getGlobalSeedClient()); - if(distance == 0L) { - lastSeed = ""; - } else { - lastSeed = DesyncStatus.SEED.format+Long.toString(distance); - } - } else { - lastSeed = DesyncStatus.SEED.format+"TAS was recorded with KillTheRNG"; - } - } - } - return lastSeed; - } - - private String getFormattedString(double delta) { - String out = ""; - if(delta != 0D) { - DesyncStatus status = DesyncStatus.fromDelta(delta); - if(status == DesyncStatus.EQUAL) { - return ""; - } - out = status.getFormat() + Double.toString(delta); - } - return out; - } - - public class MonitorContainer implements Serializable{ - private static final long serialVersionUID = -3138791930493647885L; - - int index; - - double[] values = new double[6]; - - long seed; - - - public MonitorContainer(int index, double posx, double posy, double posz, double velx, double vely, double velz, long seed) { - this.index = index; - this.values[0] = posx; - this.values[1] = posy; - this.values[2] = posz; - this.values[3] = velx; - this.values[4] = vely; - this.values[5] = velz; - this.seed = seed; - } - - public MonitorContainer(int index) { - this(index, 0, 0, 0, 0, 0, 0, 0); - } - - @Override - public String toString() { - return String.format("%s %s %s %s %s %s %s", values[0], values[1], values[2], values[3], values[4], values[5], seed); - } - - public DesyncStatus getSeverity(int index, double[] playerValues, long seed) { - - if(this.seed != seed) { - if(TASmod.ktrngHandler.isLoaded()) { -// if(CustomRandom.distance(this.seed, seed)!=1) { - return DesyncStatus.SEED; -// } - } else { - return DesyncStatus.SEED; - } - } - - DesyncStatus out = null; - - for (int i = 0; i < values.length; i++) { - double delta = 0; - try { - delta = playerValues[i] - values[i]; - } catch (Exception e) { - return DesyncStatus.ERROR; - } - DesyncStatus status = DesyncStatus.fromDelta(delta); - if(out==null || status.getSeverity() > out.getSeverity()) { - out = status; - } - } - - return out; - } - } - - public enum DesyncStatus { - EQUAL(0, TextFormatting.GREEN, "In sync", 0D), - WARNING(1, TextFormatting.YELLOW, "Slight desync", 0.00001D), - MODERATE(2, TextFormatting.RED, "Moderate desync", 0.01D), - TOTAL(3, TextFormatting.DARK_RED, "Total desync"), - SEED(3, TextFormatting.DARK_PURPLE, "RNG Seed desync"), - ERROR(3, TextFormatting.DARK_PURPLE, "ERROR"); - - private Double tolerance; - private int severity; - private String text; - private TextFormatting format; - - private DesyncStatus(int severity, TextFormatting color, String text) { - this.severity = severity; - this.format = color; - this.text = text; - tolerance = null; - } - - private DesyncStatus(int severity, TextFormatting color, String text, double tolerance) { - this(severity, color, text); - this.tolerance=tolerance; - } - - public static DesyncStatus fromDelta(double delta) { - DesyncStatus out = TOTAL; - for(DesyncStatus status : values()) { - if(status.tolerance == null) { - return status; - } - if(Math.abs(delta)=status.tolerance) { - out = status; - } - } - return out; - } - - public TextFormatting getFormat() { - return format; - } - - public int getSeverity() { - return severity; - } - - public String getText() { - return text; - } - } - - public void clear() { - currentValues=null; - container = new BigArrayList(tempDir.toString()); - lastStatus = TextFormatting.GRAY+"Empty"; - lastPos = ""; - lastMotion = ""; - lastSeed = ""; - } -} diff --git a/src/main/java/com/minecrafttas/tasmod/networking/TASmodBufferBuilder.java b/src/main/java/com/minecrafttas/tasmod/networking/TASmodBufferBuilder.java index c553ea05..053fd8ff 100644 --- a/src/main/java/com/minecrafttas/tasmod/networking/TASmodBufferBuilder.java +++ b/src/main/java/com/minecrafttas/tasmod/networking/TASmodBufferBuilder.java @@ -7,8 +7,8 @@ import java.io.IOException; import java.nio.ByteBuffer; -import com.minecrafttas.mctcommon.server.ByteBufferBuilder; -import com.minecrafttas.mctcommon.server.interfaces.PacketID; +import com.minecrafttas.mctcommon.networking.ByteBufferBuilder; +import com.minecrafttas.mctcommon.networking.interfaces.PacketID; import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; import com.minecrafttas.tasmod.savestates.SavestateHandlerServer.PlayerHandler.MotionData; import com.minecrafttas.tasmod.tickratechanger.TickrateChangerServer.TickratePauseState; diff --git a/src/main/java/com/minecrafttas/tasmod/playback/ControlByteHandler.java b/src/main/java/com/minecrafttas/tasmod/playback/ControlByteHandler.java deleted file mode 100644 index c3bfaed2..00000000 --- a/src/main/java/com/minecrafttas/tasmod/playback/ControlByteHandler.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.minecrafttas.tasmod.playback; - -import java.util.List; - -import com.mojang.realmsclient.util.Pair; - -/** - * Handles playback control bytes - * @author Pancake - */ -public class ControlByteHandler { - - /** - * Resets all control-byte-controlled settings - */ - public static void reset() { - ControlByteHandler.hideInfoBox = true; - ControlByteHandler.text = ""; - ControlByteHandler.shouldInterpolate = false; - ControlByteHandler.shouldRenderHud = true; - } - - public static void readCotrolByte(List> list) { - if (list != null) - for (Pair pair : list) - ControlByteHandler.onControlByte(pair.first(), pair.second()); - } - - public static String toString(List> cbytes) { - String out=""; - // Iterating over the list of cbytes - for(Pair pair : cbytes) { - - //Add the command byte - out=out.concat("$"+pair.first()+" "); - - //Add the arguments - String[] args=pair.second(); - for (int i = 0; i < args.length; i++) { - String limiter=" "; - if(args.length-1==i) { - limiter=""; - } - out=out.concat(args[i]+limiter); - } - //Add a new line for a new controlbyte - out=out.concat("\n"); - } - - return out; - } - - /** - * Reacts to control bytes - * @param command Control Command - * @param args Arguments - */ - public static void onControlByte(String command, String[] args) { - switch (command.toLowerCase()) { - case "interpolation": - interpolation(args); - break; - case "hud": - hud(args); - break; - case "info": - info(args); - default: - break; - } - } - - private static void info(String[] args) { - ControlByteHandler.hideInfoBox = "off".equals(args[0].trim()) || "false".equals(args[0].trim()) || "no".equals(args[0].trim()) || "0".equals(args[0].trim()); - // Parse array as text - ControlByteHandler.text = ""; - for (String string : args) { - ControlByteHandler.text += " " + string; - } - ControlByteHandler.text = ControlByteHandler.text.trim(); - } - - public static void interpolation(String[] args) { - ControlByteHandler.shouldInterpolate = "on".equals(args[0].trim()) || "true".equals(args[0].trim()) || "yes".equals(args[0].trim()) || "1".equals(args[0].trim()); - } - - public static void hud(String[] args) { - ControlByteHandler.shouldRenderHud = "on".equals(args[0].trim()) || "true".equals(args[0].trim()) || "yes".equals(args[0].trim()) || "1".equals(args[0].trim()); - } - - public static boolean hideInfoBox = true; - public static String text = ""; - public static boolean shouldInterpolate = false; - public static boolean shouldRenderHud = true; - -} diff --git a/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java b/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java index 758c7a1b..ca6aa0f6 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerClient.java @@ -1,54 +1,56 @@ package com.minecrafttas.tasmod.playback; import static com.minecrafttas.tasmod.TASmod.LOGGER; -import static com.minecrafttas.tasmod.networking.TASmodPackets.PLAYBACK_CLEAR_INPUTS; -import static com.minecrafttas.tasmod.networking.TASmodPackets.PLAYBACK_FULLPLAY; -import static com.minecrafttas.tasmod.networking.TASmodPackets.PLAYBACK_FULLRECORD; -import static com.minecrafttas.tasmod.networking.TASmodPackets.PLAYBACK_LOAD; -import static com.minecrafttas.tasmod.networking.TASmodPackets.PLAYBACK_PLAYUNTIL; -import static com.minecrafttas.tasmod.networking.TASmodPackets.PLAYBACK_RESTARTANDPLAY; -import static com.minecrafttas.tasmod.networking.TASmodPackets.PLAYBACK_SAVE; -import static com.minecrafttas.tasmod.networking.TASmodPackets.PLAYBACK_STATE; +import static com.minecrafttas.tasmod.registries.TASmodPackets.PLAYBACK_CLEAR_INPUTS; +import static com.minecrafttas.tasmod.registries.TASmodPackets.PLAYBACK_FULLPLAY; +import static com.minecrafttas.tasmod.registries.TASmodPackets.PLAYBACK_FULLRECORD; +import static com.minecrafttas.tasmod.registries.TASmodPackets.PLAYBACK_LOAD; +import static com.minecrafttas.tasmod.registries.TASmodPackets.PLAYBACK_PLAYUNTIL; +import static com.minecrafttas.tasmod.registries.TASmodPackets.PLAYBACK_RESTARTANDPLAY; +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.util.HashMap; +import java.util.ArrayList; import java.util.List; -import java.util.Map; import org.lwjgl.opengl.Display; import com.dselent.bigarraylist.BigArrayList; -import com.minecrafttas.mctcommon.Configuration.ConfigOptions; +import com.minecrafttas.mctcommon.events.EventClient.EventClientInit; import com.minecrafttas.mctcommon.events.EventListenerRegistry; -import com.minecrafttas.mctcommon.server.ByteBufferBuilder; -import com.minecrafttas.mctcommon.server.Client.Side; -import com.minecrafttas.mctcommon.server.exception.PacketNotImplementedException; -import com.minecrafttas.mctcommon.server.exception.WrongSideException; -import com.minecrafttas.mctcommon.server.interfaces.ClientPacketHandler; -import com.minecrafttas.mctcommon.server.interfaces.PacketID; -import com.minecrafttas.tasmod.TASmod; +import com.minecrafttas.mctcommon.networking.ByteBufferBuilder; +import com.minecrafttas.mctcommon.networking.Client.Side; +import com.minecrafttas.mctcommon.networking.exception.PacketNotImplementedException; +import com.minecrafttas.mctcommon.networking.exception.WrongSideException; +import com.minecrafttas.mctcommon.networking.interfaces.ClientPacketHandler; +import com.minecrafttas.mctcommon.networking.interfaces.PacketID; import com.minecrafttas.tasmod.TASmodClient; import com.minecrafttas.tasmod.events.EventClient.EventClientTickPost; -import com.minecrafttas.tasmod.events.EventClient.EventVirtualCameraAngleTick; -import com.minecrafttas.tasmod.events.EventClient.EventVirtualKeyboardTick; -import com.minecrafttas.tasmod.events.EventClient.EventVirtualMouseTick; +import com.minecrafttas.tasmod.events.EventPlaybackClient; import com.minecrafttas.tasmod.events.EventPlaybackClient.EventControllerStateChange; import com.minecrafttas.tasmod.events.EventPlaybackClient.EventPlaybackJoinedWorld; -import com.minecrafttas.tasmod.monitoring.DesyncMonitoring; +import com.minecrafttas.tasmod.events.EventPlaybackClient.EventPlaybackTick; +import com.minecrafttas.tasmod.events.EventPlaybackClient.EventRecordTick; +import com.minecrafttas.tasmod.events.EventVirtualInput; import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; -import com.minecrafttas.tasmod.networking.TASmodPackets; import com.minecrafttas.tasmod.playback.metadata.PlaybackMetadata; -import com.minecrafttas.tasmod.playback.metadata.PlaybackMetadataRegistry; 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.LoggerMarkers; import com.minecrafttas.tasmod.util.Scheduler.Task; import com.minecrafttas.tasmod.virtual.VirtualCameraAngle; import com.minecrafttas.tasmod.virtual.VirtualInput; +import com.minecrafttas.tasmod.virtual.VirtualInput.VirtualCameraAngleInput; import com.minecrafttas.tasmod.virtual.VirtualKeyboard; import com.minecrafttas.tasmod.virtual.VirtualMouse; -import com.mojang.realmsclient.util.Pair; import net.minecraft.client.Minecraft; import net.minecraft.client.entity.EntityPlayerSP; @@ -56,6 +58,7 @@ import net.minecraft.client.multiplayer.WorldClient; import net.minecraft.util.text.TextComponentString; import net.minecraft.util.text.TextFormatting; +import net.minecraft.util.text.event.ClickEvent; /** * A controller where the inputs are stored.
@@ -75,7 +78,7 @@ * @author Scribble * */ -public class PlaybackControllerClient implements ClientPacketHandler, EventVirtualKeyboardTick, EventVirtualMouseTick, EventVirtualCameraAngleTick, EventClientTickPost { +public class PlaybackControllerClient implements ClientPacketHandler, EventClientInit, EventVirtualInput.EventVirtualKeyboardTick, EventVirtualInput.EventVirtualMouseTick, EventVirtualInput.EventVirtualCameraAngleTick, EventClientTickPost { /** * The current state of the controller. @@ -83,14 +86,14 @@ public class PlaybackControllerClient implements ClientPacketHandler, EventVirtu private TASstate state = TASstate.NONE; /** - * The state of the controller when the {@link #state} is paused + * The {@link #state} that this controller will return to, after a pause */ - private TASstate tempPause = TASstate.NONE; + private TASstate stateAfterPause = TASstate.NONE; /** * The current index of the inputs */ - private int index; + private long index; private VirtualKeyboard keyboard = new VirtualKeyboard(); @@ -99,32 +102,13 @@ public class PlaybackControllerClient implements ClientPacketHandler, EventVirtu private VirtualCameraAngle camera = new VirtualCameraAngle(); public final File directory = new File(Minecraft.getMinecraft().mcDataDir.getAbsolutePath() + File.separator + "saves" + File.separator + "tasfiles"); - + /** * The place where all inputs get stored */ - private BigArrayList inputs = new BigArrayList(directory + File.separator + "temp"); - - /** - * A map of control bytes. Used to change settings during playback via the - * playback file. - *

- * A full list of changes can be found in {@link ControlByteHandler} - *

- * The values are as follows: - *

- * Map(int playbackLine, List(Pair(String controlCommand, String[] arguments))" - */ - private Map>> controlBytes = new HashMap>>(); // TODO Replace with TASFile extension - - /** - * The comments in the file, used to store them again later - */ - private Map> comments = new HashMap<>(); // TODO Replace with TASFile extension - - public DesyncMonitoring desyncMonitor = new DesyncMonitoring(this); // TODO Replace with TASFile extension + private BigArrayList inputs = new BigArrayList(directory + File.separator + "temp"); - private long startSeed = TASmod.ktrngHandler.getGlobalSeedClient(); // TODO Replace with Metadata extension +// private long startSeed = TASmod.ktrngHandler.getGlobalSeedClient(); // TODO Replace with Metadata extension // ===================================================================================================== @@ -166,8 +150,7 @@ public String setTASStateClient(TASstate stateIn) { */ public String setTASStateClient(TASstate stateIn, boolean verbose) { EventListenerRegistry.fireEvent(EventControllerStateChange.class, stateIn, state); - ControlByteHandler.reset(); // FIXME Controlbytes are resetting when loading a world, due to "Paused" state - // being active during loading... Fix Paused state shenanigans? + if (state == stateIn) { switch (stateIn) { case PLAYBACK: @@ -204,7 +187,7 @@ public String setTASStateClient(TASstate stateIn, boolean verbose) { case PAUSED: LOGGER.debug(LoggerMarkers.Playback, "Pausing a recording"); state = TASstate.PAUSED; - tempPause = TASstate.RECORDING; + stateAfterPause = TASstate.RECORDING; return verbose ? TextFormatting.GREEN + "Pausing a recording" : ""; case NONE: stopRecording(); @@ -220,7 +203,7 @@ public String setTASStateClient(TASstate stateIn, boolean verbose) { case PAUSED: LOGGER.debug(LoggerMarkers.Playback, "Pausing a playback"); state = TASstate.PAUSED; - tempPause = TASstate.PLAYBACK; + stateAfterPause = TASstate.PLAYBACK; TASmodClient.virtual.clear(); return verbose ? TextFormatting.GREEN + "Pausing a playback" : ""; case NONE: @@ -233,20 +216,20 @@ public String setTASStateClient(TASstate stateIn, boolean verbose) { case PLAYBACK: LOGGER.debug(LoggerMarkers.Playback, "Resuming a playback"); state = TASstate.PLAYBACK; - tempPause = TASstate.NONE; + stateAfterPause = TASstate.NONE; return verbose ? TextFormatting.GREEN + "Resuming a playback" : ""; case RECORDING: LOGGER.debug(LoggerMarkers.Playback, "Resuming a recording"); state = TASstate.RECORDING; - tempPause = TASstate.NONE; + stateAfterPause = TASstate.NONE; return verbose ? TextFormatting.GREEN + "Resuming a recording" : ""; case PAUSED: return TextFormatting.RED + "Please report this message to the mod author, because you should never be able to see this (Error: Paused)"; case NONE: LOGGER.debug(LoggerMarkers.Playback, "Aborting pausing"); state = TASstate.NONE; - TASstate statey = tempPause; - tempPause = TASstate.NONE; + TASstate statey = stateAfterPause; + stateAfterPause = TASstate.NONE; return TextFormatting.GREEN + "Aborting a " + statey.toString().toLowerCase() + " that was paused"; } } @@ -255,9 +238,13 @@ public String setTASStateClient(TASstate stateIn, boolean verbose) { private void startRecording() { LOGGER.debug(LoggerMarkers.Playback, "Starting recording"); - if (this.inputs.isEmpty()) { - inputs.add(new TickInputContainer(index)); - desyncMonitor.recordNull(index); + if(this.inputs.isEmpty()) { + VirtualCameraAngleInput CAMERA_ANGLE = TASmodClient.virtual.CAMERA_ANGLE; + Float pitch = CAMERA_ANGLE.getCurrentPitch(); + Float yaw = CAMERA_ANGLE.getCurrentYaw(); + this.camera.set(pitch, yaw); + + inputs.add(new TickContainer()); } } @@ -270,7 +257,7 @@ private void startPlayback() { LOGGER.debug(LoggerMarkers.Playback, "Starting playback"); Minecraft.getMinecraft().gameSettings.chatLinks = false; // #119 index = 0; - TASmod.ktrngHandler.setInitialSeed(startSeed); +// TASmod.ktrngHandler.setInitialSeed(startSeed); } private void stopPlayback() { @@ -288,7 +275,7 @@ public TASstate togglePause() { if (state != TASstate.PAUSED) { setTASStateClient(TASstate.PAUSED); } else { - setTASStateClient(tempPause); + setTASStateClient(stateAfterPause); } return state; } @@ -306,7 +293,7 @@ public void pause(boolean pause) { } } else { if (state == TASstate.PAUSED) { - setTASStateClient(tempPause, false); + setTASStateClient(stateAfterPause, false); } } } @@ -333,6 +320,10 @@ public boolean isNothingPlaying() { public TASstate getState() { return state; } + + public TASstate getStateAfterPause() { + return stateAfterPause; + } // ===================================================================================================== // Methods to update the temporary variables of the container. @@ -390,8 +381,8 @@ public void onClientTickPost(Minecraft mc) { EntityPlayerSP player = mc.player; if (player != null && player.addedToChunk) { - if (isPaused() && tempPause != TASstate.NONE) { - setTASState(tempPause); // The recording is paused in LoadWorldEvents#startLaunchServer + if (isPaused() && stateAfterPause != TASstate.NONE) { + setTASState(stateAfterPause); // The recording is paused in LoadWorldEvents#startLaunchServer pause(false); EventListenerRegistry.fireEvent(EventPlaybackJoinedWorld.class, state); } @@ -407,15 +398,17 @@ public void onClientTickPost(Minecraft mc) { private void recordNextTick() { index++; + TickContainer container = new TickContainer(keyboard.clone(), mouse.clone(), camera.clone()); if (inputs.size() <= index) { if (inputs.size() < index) { LOGGER.warn("Index is {} inputs bigger than the container!", index - inputs.size()); } - inputs.add(new TickInputContainer(index, keyboard.clone(), mouse.clone(), camera.clone())); + inputs.add(container); } else { - inputs.set(index, new TickInputContainer(index, keyboard.clone(), mouse.clone(), camera.clone())); + inputs.set(index, container); } - desyncMonitor.recordMonitor(index); // Capturing monitor values + + EventListenerRegistry.fireEvent(EventRecordTick.class, index, container); } private void playbackNextTick() { @@ -448,14 +441,13 @@ private void playbackNextTick() { } /* Continue condition */ else { - TickInputContainer tickcontainer = inputs.get(index); // Loads the new inputs from the container - this.keyboard = tickcontainer.getKeyboard().clone(); - this.mouse = tickcontainer.getMouse().clone(); - this.camera = tickcontainer.getCameraAngle().clone(); - // check for control bytes - ControlByteHandler.readCotrolByte(controlBytes.get(index)); + TickContainer container = inputs.get(index); // Loads the new inputs from the container + this.keyboard = container.getKeyboard().clone(); + this.mouse = container.getMouse().clone(); + this.camera = container.getCameraAngle().clone(); + EventListenerRegistry.fireEvent(EventPlaybackTick.class, index, container); } - desyncMonitor.playMonitor(index); + } // ===================================================================================================== // Methods to manipulate inputs @@ -468,27 +460,34 @@ public boolean isEmpty() { return inputs.isEmpty(); } - public int index() { + public long index() { return index; } - public BigArrayList getInputs() { + public BigArrayList getInputs() { return inputs; } - - public Map>> getControlBytes() { // TODO Replace with TASFile extension - return controlBytes; + + public void setInputs(BigArrayList inputs) { + this.setInputs(inputs, 0); } - - public Map> getComments() { // TODO Replace with TASFile extension - return comments; + + public void setInputs(BigArrayList inputs, long index) { + try { + this.inputs.clearMemory(); + } catch (IOException e) { + e.printStackTrace(); + } + this.inputs = new BigArrayList(directory + File.separator + "temp"); + SerialiserFlavorBase.addAll(this.inputs, inputs); + setIndex(index); } - public void setIndex(int index) throws IndexOutOfBoundsException { + public void setIndex(long index) throws IndexOutOfBoundsException { if (index <= size()) { this.index = index; if (state == TASstate.PLAYBACK) { - TickInputContainer tickcontainer = inputs.get(index); + TickContainer tickcontainer = inputs.get(index); this.keyboard = tickcontainer.getKeyboard(); this.mouse = tickcontainer.getMouse(); this.camera = tickcontainer.getCameraAngle(); @@ -498,8 +497,8 @@ public void setIndex(int index) throws IndexOutOfBoundsException { } } - public TickInputContainer get(int index) { - TickInputContainer tickcontainer = null; + public TickContainer get(long index) { + TickContainer tickcontainer = null; try { tickcontainer = inputs.get(index); } catch (IndexOutOfBoundsException e) { @@ -509,20 +508,22 @@ public TickInputContainer get(int index) { } /** - * @return The {@link TickInputContainer} at the current index + * @return The {@link TickContainer} at the current index */ - public TickInputContainer get() { + public TickContainer get() { return get(index); } public void clear() { - LOGGER.debug(LoggerMarkers.Playback, "Clearing playback controller"); - inputs = new BigArrayList(directory + File.separator + "temp"); - controlBytes.clear(); - comments.clear(); + LOGGER.info(LoggerMarkers.Playback, "Clearing playback controller"); + EventListenerRegistry.fireEvent(EventPlaybackClient.EventRecordClear.class); + try { + inputs.clearMemory(); + } catch (IOException e) { + e.printStackTrace(); + } + inputs = new BigArrayList(directory + File.separator + "temp"); index = 0; - desyncMonitor.clear(); - PlaybackMetadataRegistry.handleOnClear(); } /** @@ -540,21 +541,15 @@ public String toString() { return out; } - public void fixTicks() { // TODO Remove and use Serializer to list ticks - for (int i = 0; i < inputs.size(); i++) { - inputs.get(i).setTick(i + 1); - } - } - // ============================================================== /** * Clears {@link #keyboard} and {@link #mouse} */ public void unpressContainer() { -// LOGGER.trace(LoggerMarkers.Playback, "Unpressing container"); -// keyboard.clear(); -// mouse.clear(); + LOGGER.trace(LoggerMarkers.Playback, "Unpressing container"); + keyboard.clear(); + mouse.clear(); } // ============================================================== @@ -571,35 +566,35 @@ public void setPlayUntil(int until) { * @author Scribble * */ - public static class TickInputContainer implements Serializable { - - private static final long serialVersionUID = -3420565284438152474L; - - private int tick; + public static class TickContainer implements Serializable { private VirtualKeyboard keyboard; private VirtualMouse mouse; - private VirtualCameraAngle subticks; + private VirtualCameraAngle cameraAngle; + + private CommentContainer comments; - public TickInputContainer(int tick, VirtualKeyboard keyboard, VirtualMouse mouse, VirtualCameraAngle subticks) { - this.tick = tick; + public TickContainer(VirtualKeyboard keyboard, VirtualMouse mouse, VirtualCameraAngle subticks) { + this(keyboard, mouse, subticks, new CommentContainer()); + } + + public TickContainer(VirtualKeyboard keyboard, VirtualMouse mouse, VirtualCameraAngle camera, CommentContainer comments) { this.keyboard = keyboard; this.mouse = mouse; - this.subticks = subticks; + this.cameraAngle = camera; + this.comments = comments; } - public TickInputContainer(int tick) { - this.tick = tick; - this.keyboard = new VirtualKeyboard(); - this.mouse = new VirtualMouse(); - this.subticks = new VirtualCameraAngle(); + public TickContainer() { + this(new VirtualKeyboard(), new VirtualMouse(), new VirtualCameraAngle()); } @Override public String toString() { - return tick + "|" + keyboard.toString() + "|" + mouse.toString() + "|" + subticks.toString(); + String.join("\n// ", comments.inlineComments); + return keyboard.toString() + "|" + mouse.toString() + "|" + cameraAngle.toString() + "\t\t// " + comments.endlineComments; } public VirtualKeyboard getKeyboard() { @@ -611,20 +606,103 @@ public VirtualMouse getMouse() { } public VirtualCameraAngle getCameraAngle() { - return subticks; + return cameraAngle; } - public int getTick() { - return tick; + public CommentContainer getComments() { + return comments; + } + + @Override + public TickContainer clone() { + return new TickContainer(keyboard, mouse, cameraAngle); } - public void setTick(int tick) { - this.tick = tick; + @Override + public boolean equals(Object other) { + if (other instanceof TickContainer) { + TickContainer container = (TickContainer) other; + return keyboard.equals(container.keyboard) && mouse.equals(container.mouse) && cameraAngle.equals(container.cameraAngle) && comments.equals(container.comments); + } + return super.equals(other); + } + } + + public static class CommentContainer implements Serializable{ + + /** + * List of all inline comments in a tick.
+ * These comments take the form: + * + *

+		 * // This is an inline comment
+		 * // This is a second inline comment
+		 * 1|W;w|;0;0;0|0.0;0.0
+		 * 	1|||1.0;1.0
+		 * 
+ * + * Inline comments are supposed to describe the tick as a whole and therefore + * can not be attached to subticks.
+ * like so: + * + *
+		 * 1|W;w|;0;0;0|0.0;0.0
+		 * // This is not allowed. This comment won't be saved
+		 * 	1|||1.0;1.0
+		 * 
+ */ + private List inlineComments; + + /** + * List of all endline comments.
+ * These comments take the form: + * + *
+		 * 1|W;w|;0;0;0|0.0;0.0		// This is an endline comment
+		 * 	1|||1.0;1.0		// This is a second endline comment
+		 * 
+ * + * Endline comments are supposed to describe individual subticks.
+ */ + private List endlineComments; + + public CommentContainer() { + this(new ArrayList<>(), new ArrayList<>()); + } + + public CommentContainer(List inlineComments, List endlineComments) { + this.inlineComments=inlineComments; + this.endlineComments=endlineComments; + } + + public void addInlineComment(String inlineComment) { + inlineComments.add(inlineComment); + } + + public void addEndlineComment(String endlineComment) { + endlineComments.add(endlineComment); + } + + public List getInlineComments() { + return inlineComments; } + public List getEndlineComments() { + return endlineComments; + } + @Override - public TickInputContainer clone() { - return new TickInputContainer(tick, keyboard, mouse, subticks); + public boolean equals(Object obj) { + if(obj instanceof CommentContainer) { + CommentContainer other = (CommentContainer) obj; + return inlineComments.equals(other.inlineComments) && endlineComments.equals(other.endlineComments); + } + return super.equals(obj); + } + + @Override + public String toString() { + return inlineComments.toString()+"\n\n"+endlineComments.toString(); } } @@ -688,38 +766,57 @@ public PacketID[] getAcceptedPacketIDs() { public void onClientPacket(PacketID id, ByteBuffer buf, String username) throws PacketNotImplementedException, WrongSideException, Exception { TASmodPackets packet = (TASmodPackets) id; String name = null; + String flavor = null; Minecraft mc = Minecraft.getMinecraft(); switch (packet) { case PLAYBACK_SAVE: name = TASmodBufferBuilder.readString(buf); -// try { -// TASmodClient.virtual.saveInputs(name); TODO Move to PlaybackController -// } catch (IOException e) { -// if (mc.world != null) -// mc.ingameGUI.getChatGUI().printChatMessage(new TextComponentString(TextFormatting.RED + e.getMessage())); -// else -// e.printStackTrace(); -// return; -// } - if (mc.world != null) - mc.ingameGUI.getChatGUI().printChatMessage(new TextComponentString(TextFormatting.GREEN + "Saved inputs to " + name + ".mctas")); + flavor = TASmodBufferBuilder.readString(buf); + + try { + PlaybackSerialiser.saveToFile(new File(directory, name + ".mctas"), this, flavor); + } catch (PlaybackSaveException e) { + if (mc.world != null) + mc.ingameGUI.getChatGUI().printChatMessage(new TextComponentString(TextFormatting.RED + e.getMessage())); + LOGGER.catching(e); + return; + } catch (Exception e) { + if (mc.world != null) + mc.ingameGUI.getChatGUI().printChatMessage(new TextComponentString(TextFormatting.RED + "Saving failed, something went very wrong")); + LOGGER.catching(e); + return; + } + + if (mc.world != null) { + TextComponentString confirm = new TextComponentString(TextFormatting.GREEN + "Saved inputs to " + name + ".mctas" + TextFormatting.RESET + " [" + TextFormatting.YELLOW + "Open folder" + TextFormatting.RESET + "]"); + confirm.getStyle().setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/folder tasfiles")); + mc.ingameGUI.getChatGUI().printChatMessage(confirm); + } else LOGGER.debug(LoggerMarkers.Playback, "Saved inputs to " + name + ".mctas"); break; case PLAYBACK_LOAD: name = TASmodBufferBuilder.readString(buf); -// try { -// TASmodClient.virtual.loadInputs(name); TODO Move to PlaybackController -// } catch (IOException e) { -// if (mc.world != null) -// mc.ingameGUI.getChatGUI().printChatMessage(new TextComponentString(TextFormatting.RED + e.getMessage())); -// else -// e.printStackTrace(); -// return; -// } + flavor = TASmodBufferBuilder.readString(buf); + + try { + TASmodClient.controller.setInputs(PlaybackSerialiser.loadFromFile(new File(directory, name + ".mctas"), flavor)); + } catch (PlaybackLoadException e) { + if (mc.world != null) { + TextComponentString textComponent = new TextComponentString(e.getMessage()); + mc.ingameGUI.getChatGUI().printChatMessage(textComponent); + } + LOGGER.catching(e); + return; + } catch (Exception e) { + if (mc.world != null) + mc.ingameGUI.getChatGUI().printChatMessage(new TextComponentString(TextFormatting.RED + "Loading failed, something went very wrong")); + LOGGER.catching(e); + } + if (mc.world != null) mc.ingameGUI.getChatGUI().printChatMessage(new TextComponentString(TextFormatting.GREEN + "Loaded inputs from " + name + ".mctas")); else @@ -763,7 +860,7 @@ public void onClientPacket(PacketID id, ByteBuffer buf, String username) throws e.printStackTrace(); } Minecraft.getMinecraft().addScheduledTask(() -> { - TASmodClient.config.set(ConfigOptions.FileToOpen, finalname); + TASmodClient.config.set(TASmodConfig.FileToOpen, finalname); System.exit(0); }); break; @@ -810,4 +907,18 @@ public void onClientPacket(PacketID id, ByteBuffer buf, String username) throws throw new PacketNotImplementedException(packet, this.getClass(), Side.CLIENT); } } + + /** + * Runs on client initialization, used for loading the TASfile after /restartandplay + */ + @Override + public void onClientInit(Minecraft mc) { + // Execute /restartandplay. Load the file to start from the config. If it exists load the playback file on start. + String fileOnStart = TASmodClient.config.get(TASmodConfig.FileToOpen); + if (fileOnStart.isEmpty()) { + fileOnStart = null; + } else { + TASmodClient.config.reset(TASmodConfig.FileToOpen); + } + } } diff --git a/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerServer.java b/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerServer.java index a834fc40..889a5647 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerServer.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerServer.java @@ -1,27 +1,27 @@ package com.minecrafttas.tasmod.playback; import static com.minecrafttas.tasmod.TASmod.LOGGER; -import static com.minecrafttas.tasmod.networking.TASmodPackets.PLAYBACK_CLEAR_INPUTS; -import static com.minecrafttas.tasmod.networking.TASmodPackets.PLAYBACK_FULLPLAY; -import static com.minecrafttas.tasmod.networking.TASmodPackets.PLAYBACK_FULLRECORD; -import static com.minecrafttas.tasmod.networking.TASmodPackets.PLAYBACK_LOAD; -import static com.minecrafttas.tasmod.networking.TASmodPackets.PLAYBACK_PLAYUNTIL; -import static com.minecrafttas.tasmod.networking.TASmodPackets.PLAYBACK_RESTARTANDPLAY; -import static com.minecrafttas.tasmod.networking.TASmodPackets.PLAYBACK_SAVE; -import static com.minecrafttas.tasmod.networking.TASmodPackets.PLAYBACK_STATE; +import static com.minecrafttas.tasmod.registries.TASmodPackets.PLAYBACK_CLEAR_INPUTS; +import static com.minecrafttas.tasmod.registries.TASmodPackets.PLAYBACK_FULLPLAY; +import static com.minecrafttas.tasmod.registries.TASmodPackets.PLAYBACK_FULLRECORD; +import static com.minecrafttas.tasmod.registries.TASmodPackets.PLAYBACK_LOAD; +import static com.minecrafttas.tasmod.registries.TASmodPackets.PLAYBACK_PLAYUNTIL; +import static com.minecrafttas.tasmod.registries.TASmodPackets.PLAYBACK_RESTARTANDPLAY; +import static com.minecrafttas.tasmod.registries.TASmodPackets.PLAYBACK_SAVE; +import static com.minecrafttas.tasmod.registries.TASmodPackets.PLAYBACK_STATE; import static com.minecrafttas.tasmod.util.LoggerMarkers.Playback; import java.nio.ByteBuffer; -import com.minecrafttas.mctcommon.server.Client.Side; -import com.minecrafttas.mctcommon.server.exception.PacketNotImplementedException; -import com.minecrafttas.mctcommon.server.exception.WrongSideException; -import com.minecrafttas.mctcommon.server.interfaces.PacketID; -import com.minecrafttas.mctcommon.server.interfaces.ServerPacketHandler; +import com.minecrafttas.mctcommon.networking.Client.Side; +import com.minecrafttas.mctcommon.networking.exception.PacketNotImplementedException; +import com.minecrafttas.mctcommon.networking.exception.WrongSideException; +import com.minecrafttas.mctcommon.networking.interfaces.PacketID; +import com.minecrafttas.mctcommon.networking.interfaces.ServerPacketHandler; import com.minecrafttas.tasmod.TASmod; import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; -import com.minecrafttas.tasmod.networking.TASmodPackets; import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; +import com.minecrafttas.tasmod.registries.TASmodPackets; /** * The playback controller on the server side.
diff --git a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/PlaybackFileCommand.java b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/PlaybackFileCommand.java new file mode 100644 index 00000000..9aed7f28 --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/PlaybackFileCommand.java @@ -0,0 +1,207 @@ +package com.minecrafttas.tasmod.playback.filecommands; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import com.minecrafttas.mctcommon.registry.Registerable; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TickContainer; + +public class PlaybackFileCommand { + + private String name; + + private String[] args; + + public PlaybackFileCommand(String name) { + this(name, (String[]) null); + } + + public PlaybackFileCommand(String name, String... args) { + if (args == null) { + args = new String[] {}; + } + this.name = name; + this.args = args; + } + + public String getName() { + return name; + } + + public String[] getArgs() { + return args; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof PlaybackFileCommand) { + PlaybackFileCommand other = (PlaybackFileCommand) obj; + return this.name.equals(other.name) && Arrays.equals(this.args, other.args); + } + return super.equals(obj); + } + + @Override + public String toString() { + return String.format("$%s(%s);", name, String.join(", ", args)); + } + + public static abstract class PlaybackFileCommandExtension implements Registerable{ + + protected boolean enabled = false; + + public abstract String[] getFileCommandNames(); + + public void onEnable() { + }; + + public void onDisable() { + }; + + public void onClear() { + }; + + public void onRecord(long tick, TickContainer tickContainer) { + }; + + public void onPlayback(long tick, TickContainer tickContainer) { + }; + + public PlaybackFileCommandContainer onSerialiseInlineComment(long tick, TickContainer tickContainer) { + return null; + } + + public PlaybackFileCommandContainer onSerialiseEndlineComment(long currentTick, TickContainer tickContainer) { + return null; + } + + public void onDeserialiseInlineComment(long tick, TickContainer container, PlaybackFileCommandContainer fileCommandContainer) { + } + + public void onDeserialiseEndlineComment(long tick, TickContainer container, PlaybackFileCommandContainer fileCommandContainer) { + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + if (enabled) + onEnable(); + else + onDisable(); + this.enabled = enabled; + } + + @Override + public String toString() { + return getExtensionName(); + } + } + + public static class PlaybackFileCommandContainer extends LinkedHashMap { + + public PlaybackFileCommandContainer() { + } + + public PlaybackFileCommandContainer(List> list) { + for (List lists : list) { + if (lists != null) { + for (PlaybackFileCommand command : lists) { + this.put(command.getName(), new PlaybackFileCommandLine()); + } + } + } + + for (List lists : list) { + for (Map.Entry entry : this.entrySet()) { + String key = entry.getKey(); + List val = entry.getValue(); + + boolean valuePresent = false; + if (lists != null) { + for (PlaybackFileCommand command : lists) { + if (key.equals(command.getName())) { + valuePresent = true; + val.add(command); + } + } + } + if (!valuePresent) { + val.add(null); + } + } + } + } + + public void add(String key, PlaybackFileCommand fileCommand) { + PlaybackFileCommandLine toAdd = getOrDefault(key, new PlaybackFileCommandLine()); + if (toAdd.isEmpty()) { + put(key, toAdd); + } + + toAdd.add(fileCommand); + } + + public PlaybackFileCommandContainer split(String... keys) { + return split(Arrays.asList(keys)); + } + + public PlaybackFileCommandContainer split(Iterable keys) { + PlaybackFileCommandContainer out = new PlaybackFileCommandContainer(); + for (String key : keys) { + out.put(key, this.get(key)); + } + return out; + } + + public List> valuesBySubtick() { + List> out = new ArrayList<>(); + + int biggestSize = 0; + for (PlaybackFileCommandLine list : values()) { + if (list.size() > biggestSize) { + biggestSize = list.size(); + } + } + + for (int i = 0; i < biggestSize; i++) { + List commandListForOneLine = new ArrayList<>(); + for (PlaybackFileCommandLine list : values()) { + if (i < list.size()) { + PlaybackFileCommand fc = list.get(i); + commandListForOneLine.add(fc); + } else { + commandListForOneLine.add(null); + } + } + out.add(commandListForOneLine); + } + + return out; + } + + @Override + public boolean equals(Object o) { + if (o instanceof PlaybackFileCommandContainer) { + PlaybackFileCommandContainer other = (PlaybackFileCommandContainer) o; + for (java.util.Map.Entry entry : other.entrySet()) { + String key = entry.getKey(); + PlaybackFileCommandLine val = entry.getValue(); + + if (!this.containsKey(key) && !this.get(key).equals(val)) + return false; + } + return true; + } + return super.equals(o); + } + } + + public static class PlaybackFileCommandLine extends ArrayList { + + } +} diff --git a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/PlaybackFileCommandsRegistry.java b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/PlaybackFileCommandsRegistry.java new file mode 100644 index 00000000..d70e4f5e --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/PlaybackFileCommandsRegistry.java @@ -0,0 +1,179 @@ +package com.minecrafttas.tasmod.playback.filecommands; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; + +import com.minecrafttas.mctcommon.Configuration; +import com.minecrafttas.mctcommon.registry.AbstractRegistry; +import com.minecrafttas.tasmod.events.EventPlaybackClient; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TickContainer; +import com.minecrafttas.tasmod.playback.filecommands.PlaybackFileCommand.PlaybackFileCommandContainer; +import com.minecrafttas.tasmod.playback.filecommands.PlaybackFileCommand.PlaybackFileCommandExtension; +import com.minecrafttas.tasmod.registries.TASmodConfig; + +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<>()); + } + + @Override + public void register(PlaybackFileCommandExtension extension) { + super.register(extension); + enabledExtensions = getEnabled(); + } + + @Override + public void unregister(PlaybackFileCommandExtension extension) { + super.unregister(extension); + enabledExtensions = getEnabled(); + } + + 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) { + return false; + } + extension.setEnabled(enabled); + enabledExtensions = getEnabled(); + + if(saveToConfig) { + saveConfig(); + } + return true; + } + + private void disableAll() { + REGISTRY.forEach((name, value) -> { + value.setEnabled(false); + }); + } + + 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) + saveConfig(); + } + + public List getEnabled() { + List out = new ArrayList<>(); + + for (PlaybackFileCommandExtension element : REGISTRY.values()) { + if (element.isEnabled()) { + out.add(element); + } + } + + return out; + } + + public List getAll(){ + return new ArrayList<>(REGISTRY.values()); + } + + @Override + public void onRecordTick(long index, TickContainer container) { + enabledExtensions.forEach(extension -> { + if(extension.isEnabled()) { + extension.onRecord(index, container); + } + }); + } + + @Override + public void onPlaybackTick(long index, TickContainer container) { + enabledExtensions.forEach(extension -> { + if(extension.isEnabled()) { + extension.onPlayback(index, 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) { + out.putAll(extensionContainer); + } + } + return out; + } + + public PlaybackFileCommandContainer handleOnSerialiseEndline(long currentTick, TickContainer container) { + PlaybackFileCommandContainer out = new PlaybackFileCommandContainer(); + for (PlaybackFileCommandExtension extension : enabledExtensions) { + PlaybackFileCommandContainer extensionContainer=extension.onSerialiseEndlineComment(currentTick, container); + if(extensionContainer!=null) { + out.putAll(extensionContainer); + } + } + return out; + } + + public void handleOnDeserialiseInline(long currentTick, TickContainer deserialisedContainer, List> inlineFileCommands) { + PlaybackFileCommandContainer fileCommandContainer = new PlaybackFileCommandContainer(inlineFileCommands); + for (PlaybackFileCommandExtension extension : enabledExtensions) { + String[] fileCommandNames = extension.getFileCommandNames(); + extension.onDeserialiseInlineComment(currentTick, deserialisedContainer, fileCommandContainer.split(fileCommandNames)); + } + } + + public void handleOnDeserialiseEndline(long currentTick, TickContainer deserialisedContainer, List> endlineFileCommands) { + PlaybackFileCommandContainer fileCommandContainer = new PlaybackFileCommandContainer(endlineFileCommands); + for (PlaybackFileCommandExtension extension : enabledExtensions) { + String[] fileCommandNames = extension.getFileCommandNames(); + extension.onDeserialiseEndlineComment(currentTick, deserialisedContainer, fileCommandContainer.split(fileCommandNames)); + } + } + + @Override + public void onClear() { + REGISTRY.values().forEach(fc -> { + fc.onClear(); + }); + } + + public void setConfig(Configuration config) { + this.config = config; + loadConfig(); + } + + private void loadConfig() { + if (config == null) { + return; + } + 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 ->{ + nameList.add(element.getExtensionName()); + }); + config.set(TASmodConfig.EnabledFileCommands, String.join(", ", nameList)); + config.save(); + } +} diff --git a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/integrated/DesyncMonitorFileCommandExtension.java b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/integrated/DesyncMonitorFileCommandExtension.java new file mode 100644 index 00000000..a89fef45 --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/integrated/DesyncMonitorFileCommandExtension.java @@ -0,0 +1,353 @@ +package com.minecrafttas.tasmod.playback.filecommands.integrated; + +import java.io.File; +import java.io.IOException; +import java.io.Serializable; +import java.text.NumberFormat; +import java.text.ParseException; +import java.util.List; +import java.util.Locale; + +import com.dselent.bigarraylist.BigArrayList; +import com.minecrafttas.tasmod.TASmodClient; +import com.minecrafttas.tasmod.events.EventPlaybackClient; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TickContainer; +import com.minecrafttas.tasmod.playback.filecommands.PlaybackFileCommand; +import com.minecrafttas.tasmod.playback.filecommands.PlaybackFileCommand.PlaybackFileCommandContainer; +import com.minecrafttas.tasmod.playback.filecommands.PlaybackFileCommand.PlaybackFileCommandExtension; +import com.minecrafttas.tasmod.playback.tasfile.exception.PlaybackLoadException; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.EntityPlayerSP; +import net.minecraft.util.text.TextFormatting; + +/** + * Stores the players position during recording and compares it with the + * position during playback + * + * @author Scribble + */ +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"); + + private BigArrayList monitorContainer = new BigArrayList(tempDir.toString()); + + private MonitorContainer currentValues; + + public DesyncMonitorFileCommandExtension() { + enabled = true; + } + + @Override + public String getExtensionName() { + return "tasmod_desyncMonitor@v1"; + } + + @Override + public String[] getFileCommandNames() { + return new String[] { "desyncMonitor" }; + } + + @Override + public void onControllerStateChange(TASstate newstate, TASstate oldstate) { + if(newstate==TASstate.RECORDING && monitorContainer.isEmpty()) { + recordNull(0); + } + } + + @Override + public void onRecord(long tick, TickContainer tickContainer) { + EntityPlayerSP player = Minecraft.getMinecraft().player; + MonitorContainer values = null; + if (player != null) { + values = new MonitorContainer(tick, player.posX, player.posY, player.posZ, player.motionX, player.motionY, player.motionZ); + } else { + values = new MonitorContainer(tick); + } + + if (monitorContainer.size() <= tick) { + monitorContainer.add(values); + } else { + monitorContainer.set(tick, values); + } + } + + @Override + public void onDisable() { + this.onClear(); + } + + @Override + public PlaybackFileCommandContainer onSerialiseEndlineComment(long currentTick, TickContainer tickContainer) { + PlaybackFileCommandContainer out = new PlaybackFileCommandContainer(); + MonitorContainer monitoredValues = monitorContainer.get(currentTick); + PlaybackFileCommand command = new PlaybackFileCommand("desyncMonitor", monitoredValues.toStringArray()); + + out.add("desyncMonitor", command); + + return out; + } + + @Override + public void onDeserialiseEndlineComment(long tick, TickContainer container, PlaybackFileCommandContainer fileCommandContainer) { + List commandsEndline = fileCommandContainer.get("desyncMonitor"); + if (commandsEndline == null || commandsEndline.isEmpty()) { + recordNull(tick); + return; + } + + PlaybackFileCommand command = commandsEndline.get(0); + this.monitorContainer.add(loadFromFile(tick, command.getArgs())); + } + + public void recordNull(long tick) { + if (monitorContainer.size() <= tick) { + monitorContainer.add(new MonitorContainer(tick)); + } else { + monitorContainer.set(tick, new MonitorContainer(tick)); + } + } + + @Override + public void onPlayback(long tick, TickContainer tickContainer) { + currentValues = get(tick - 1); + } + + private MonitorContainer loadFromFile(long tick, String[] args) throws PlaybackLoadException { + + if (args.length != 6) + throw new PlaybackLoadException("Tick %s: desyncMonitorArgsLength "); + + double x = 0; + double y = 0; + double z = 0; + double mx = 0; + double my = 0; + double mz = 0; + try { + x = parseDouble(args[0]); + y = parseDouble(args[1]); + z = parseDouble(args[2]); + mx = parseDouble(args[3]); + my = parseDouble(args[4]); + mz = parseDouble(args[5]); + } catch (ParseException e) { + throw new PlaybackLoadException(e); + } + + return new MonitorContainer(tick, x, y, z, mx, my, mz); + } + + public MonitorContainer get(long l) { + try { + return monitorContainer.get(l); + } catch (IndexOutOfBoundsException e) { + return null; + } + } + + private String lastStatus = TextFormatting.GRAY + "Empty"; + + public String getStatus(EntityPlayerSP player) { + if (!TASmodClient.controller.isNothingPlaying()) { + if (currentValues != null) { + double[] playervalues = new double[6]; + playervalues[0] = player.posX; + playervalues[1] = player.posY; + playervalues[2] = player.posZ; + playervalues[3] = player.motionX; + playervalues[4] = player.motionY; + playervalues[5] = player.motionZ; + DesyncStatus status = currentValues.getSeverity(TASmodClient.controller.index(), playervalues); + lastStatus = status.getFormat() + status.getText(); + } else { + lastStatus = TextFormatting.GRAY + "Empty"; + } + } + return lastStatus; + } + + private String lastPos = ""; + + public String getPos() { + if (currentValues != null && !TASmodClient.controller.isNothingPlaying()) { + EntityPlayerSP player = Minecraft.getMinecraft().player; + String[] values = new String[3]; + values[0] = getFormattedString(player.posX - currentValues.values[0]); + values[1] = getFormattedString(player.posY - currentValues.values[1]); + values[2] = getFormattedString(player.posZ - currentValues.values[2]); + + String out = ""; + for (String val : values) { + if (val != null) { + out += val + " "; + } + } + lastPos = out; + } + return lastPos; + } + + private String lastMotion = ""; + + public String getMotion() { + if (currentValues != null && !TASmodClient.controller.isNothingPlaying()) { + EntityPlayerSP player = Minecraft.getMinecraft().player; + String[] values = new String[3]; + values[0] = getFormattedString(player.motionX - currentValues.values[3]); + values[1] = getFormattedString(player.motionY - currentValues.values[4]); + values[2] = getFormattedString(player.motionZ - currentValues.values[5]); + + String out = ""; + for (String val : values) { + if (val != null) { + out += val + " "; + } + } + lastMotion = out; + } + return lastMotion; + } + + private String getFormattedString(double delta) { + String out = ""; + if (delta != 0D) { + DesyncStatus status = DesyncStatus.fromDelta(delta); + if (status == DesyncStatus.EQUAL) { + return ""; + } + out = status.getFormat() + Double.toString(delta); + } + return out; + } + + public class MonitorContainer implements Serializable { + private static final long serialVersionUID = -3138791930493647885L; + + long index; + + double[] values = new double[6]; + + public MonitorContainer(long index, double posx, double posy, double posz, double velx, double vely, double velz) { + this.index = index; + this.values[0] = posx; + this.values[1] = posy; + this.values[2] = posz; + this.values[3] = velx; + this.values[4] = vely; + this.values[5] = velz; + } + + public MonitorContainer(long index) { + this(index, 0, 0, 0, 0, 0, 0); + } + + public String[] toStringArray() { + String[] out = new String[values.length]; + for (int i = 0; i < values.length; i++) { + out[i] = String.format(Locale.ENGLISH, "%.5f", values[i]); + } + return out; + } + + @Override + public String toString() { + return String.format(Locale.US, "%d, %d, %d, %d, %d, %d", values[0], values[1], values[2], values[3], values[4], values[5]); + } + + public DesyncStatus getSeverity(long index, double[] playerValues) { + + DesyncStatus out = null; + + for (int i = 0; i < values.length; i++) { + double delta = 0; + try { + delta = playerValues[i] - values[i]; + } catch (Exception e) { + return DesyncStatus.ERROR; + } + DesyncStatus status = DesyncStatus.fromDelta(delta); + if (out == null || status.getSeverity() > out.getSeverity()) { + out = status; + } + } + + return out; + } + } + + public enum DesyncStatus { + EQUAL(0, TextFormatting.GREEN, "In sync", 0D), + WARNING(1, TextFormatting.YELLOW, "Slight desync", 0.00001D), + MODERATE(2, TextFormatting.RED, "Moderate desync", 0.01D), + TOTAL(3, TextFormatting.DARK_RED, "Total desync"), + ERROR(3, TextFormatting.DARK_PURPLE, "ERROR"); + + private Double tolerance; + private int severity; + private String text; + private TextFormatting format; + + private DesyncStatus(int severity, TextFormatting color, String text) { + this.severity = severity; + this.format = color; + this.text = text; + tolerance = null; + } + + private DesyncStatus(int severity, TextFormatting color, String text, double tolerance) { + this(severity, color, text); + this.tolerance = tolerance; + } + + public static DesyncStatus fromDelta(double delta) { + DesyncStatus out = TOTAL; + for (DesyncStatus status : values()) { + if (status.tolerance == null) { + return status; + } + if (Math.abs(delta) < status.tolerance) { + break; + } + if (Math.abs(delta) >= status.tolerance) { + out = status; + } + } + return out; + } + + public TextFormatting getFormat() { + return format; + } + + public int getSeverity() { + return severity; + } + + public String getText() { + return text; + } + } + + private double parseDouble(String doublestring) throws ParseException { + NumberFormat format = NumberFormat.getInstance(Locale.ENGLISH); + Number number = format.parse(doublestring); + return number.doubleValue(); + } + + @Override + public void onClear() { + currentValues = null; + try { + monitorContainer.clearMemory(); + } catch (IOException e) { + e.printStackTrace(); + } + monitorContainer = new BigArrayList(tempDir.toString()); + lastStatus = TextFormatting.GRAY + "Empty"; + lastPos = ""; + lastMotion = ""; + } +} diff --git a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/integrated/LabelFileCommandExtension.java b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/integrated/LabelFileCommandExtension.java new file mode 100644 index 00000000..e0e11548 --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/integrated/LabelFileCommandExtension.java @@ -0,0 +1,71 @@ +package com.minecrafttas.tasmod.playback.filecommands.integrated; + +import java.io.IOException; + +import com.dselent.bigarraylist.BigArrayList; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TickContainer; +import com.minecrafttas.tasmod.playback.filecommands.PlaybackFileCommand; +import com.minecrafttas.tasmod.playback.filecommands.PlaybackFileCommand.PlaybackFileCommandContainer; +import com.minecrafttas.tasmod.playback.filecommands.PlaybackFileCommand.PlaybackFileCommandExtension; +import com.minecrafttas.tasmod.playback.filecommands.PlaybackFileCommand.PlaybackFileCommandLine; + +public class LabelFileCommandExtension extends PlaybackFileCommandExtension { + + private String labelText = ""; + + BigArrayList label = new BigArrayList<>(); + + public LabelFileCommandExtension() { + enabled = true; + } + + @Override + public String getExtensionName() { + return "tasmod_label@v1"; + } + + @Override + public String[] getFileCommandNames() { + return new String[] { "label" }; + } + + @Override + public void onDeserialiseInlineComment(long tick, TickContainer container, PlaybackFileCommandContainer fileCommandContainer) { + if (fileCommandContainer.containsKey("label")) { + label.add(fileCommandContainer.split("label")); + } + } + + @Override + public void onPlayback(long tick, TickContainer tickContainer) { + PlaybackFileCommandContainer containerInTick = label.get(tick - 1); + if (containerInTick == null) { + return; + } + + PlaybackFileCommandLine line = containerInTick.get("label"); + if (line == null) { + return; + } + + for (PlaybackFileCommand command : line) { + labelText = String.join(", ", command.getArgs()); + } + } + + @Override + public void onClear() { + try { + label.clearMemory(); + } catch (IOException e) { + e.printStackTrace(); + } + + label = new BigArrayList<>(); + labelText = ""; + } + + public String getLabelText() { + return labelText; + } +} diff --git a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/integrated/OptionsFileCommandExtension.java b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/integrated/OptionsFileCommandExtension.java new file mode 100644 index 00000000..af0390ee --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/integrated/OptionsFileCommandExtension.java @@ -0,0 +1,90 @@ +package com.minecrafttas.tasmod.playback.filecommands.integrated; + +import java.io.IOException; + +import com.dselent.bigarraylist.BigArrayList; +import com.minecrafttas.tasmod.TASmod; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TickContainer; +import com.minecrafttas.tasmod.playback.filecommands.PlaybackFileCommand; +import com.minecrafttas.tasmod.playback.filecommands.PlaybackFileCommand.PlaybackFileCommandContainer; +import com.minecrafttas.tasmod.playback.filecommands.PlaybackFileCommand.PlaybackFileCommandExtension; +import com.minecrafttas.tasmod.playback.filecommands.PlaybackFileCommand.PlaybackFileCommandLine; +import com.minecrafttas.tasmod.util.LoggerMarkers; + +public class OptionsFileCommandExtension extends PlaybackFileCommandExtension { + + private boolean shouldRenderHud = true; + + BigArrayList hud = new BigArrayList<>(); + + public OptionsFileCommandExtension() { + enabled = true; + } + + @Override + public String getExtensionName() { + return "tasmod_options@v1"; + } + + @Override + public String[] getFileCommandNames() { + return new String[] { "hud" }; + } + + @Override + public void onDeserialiseInlineComment(long tick, TickContainer container, PlaybackFileCommandContainer fileCommandContainer) { + if (fileCommandContainer.containsKey("hud")) { + hud.add(fileCommandContainer.split("hud")); + } + } + + @Override + public void onPlayback(long tick, TickContainer tickContainer) { + PlaybackFileCommandContainer containerInTick = hud.get(tick); + if(containerInTick == null) { + return; + } + + PlaybackFileCommandLine line = containerInTick.get("hud"); + if(line == null) { + return; + } + + for (PlaybackFileCommand command : line) { + String[] args = command.getArgs(); + if (args.length == 1) { + switch (args[0]) { + case "true": + shouldRenderHud = true; + break; + + case "false": + shouldRenderHud = false; + break; + + default: + TASmod.LOGGER.warn(LoggerMarkers.Playback, "FileCommand hud has the wrong argument in tick {}: {} (Must be true or false)", tick, args[0]); + break; + } + } else { + TASmod.LOGGER.warn(LoggerMarkers.Playback, "FileCommand hud has the wrong number of arguments in tick {}: {}", tick, args.length); + } + } + } + + @Override + public void onClear() { + try { + hud.clearMemory(); + } catch (IOException e) { + e.printStackTrace(); + } + + hud = new BigArrayList<>(); + shouldRenderHud = true; + } + + public boolean shouldRenderHud() { + return shouldRenderHud; + } +} 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 c2a00c47..d20e2c4a 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/metadata/PlaybackMetadata.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/metadata/PlaybackMetadata.java @@ -1,21 +1,21 @@ package com.minecrafttas.tasmod.playback.metadata; import java.util.ArrayList; -import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -import com.minecrafttas.tasmod.playback.metadata.PlaybackMetadataRegistry.PlaybackMetadataExtension; +import com.minecrafttas.mctcommon.registry.Registerable; /** * Stores a section of
*
*/ public class PlaybackMetadata { + private String extensionName; - private LinkedHashMap metadata; + private LinkedHashMap data; private static String SEPERATOR = ":"; @@ -25,24 +25,29 @@ public PlaybackMetadata(PlaybackMetadataExtension extension) { private PlaybackMetadata(String extensionName) { this.extensionName = extensionName; - this.metadata = new LinkedHashMap(); + this.data = new LinkedHashMap(); + } + + private PlaybackMetadata(String extensionName, LinkedHashMap data) { + this.extensionName = extensionName; + this.data = data; } public void setValue(String key, String value) { if (key.contains(SEPERATOR)) { throw new IllegalArgumentException(String.format("%sKeyname %s can't contain %s", extensionName != null ? extensionName + ": " : "", key, SEPERATOR)); } - metadata.put(key, value); + data.put(key, value); } public String getValue(String key) { - return metadata.get(key); + return data.get(key); } @Override public String toString() { String out = ""; - for (String key : metadata.keySet()) { + for (String key : data.keySet()) { String value = getValue(key); out += (String.format("%s%s%s\n", key, SEPERATOR, value)); } @@ -51,7 +56,7 @@ public String toString() { public List toStringList() { List out = new ArrayList<>(); - for (Object keyObj : metadata.keySet()) { + for (Object keyObj : data.keySet()) { String key = (String) keyObj; String value = getValue(key); out.add(String.format("%s%s%s\n", key, SEPERATOR, value)); @@ -63,15 +68,15 @@ public String getExtensionName() { return extensionName; } - public HashMap getMetadata() { - return metadata; + public LinkedHashMap getData() { + return data; } @Override public boolean equals(Object obj) { if (obj instanceof PlaybackMetadata) { PlaybackMetadata other = (PlaybackMetadata) obj; - return other.metadata.equals(metadata) && other.extensionName.equals(extensionName); + return other.data.equals(data) && other.extensionName.equals(extensionName); } return super.equals(obj); } @@ -92,4 +97,38 @@ 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() {}; + + /** + * Runs, when the TASfile is being stored to a file.
+ * Create a new {@link PlaybackMetadata} with PlaybackMetadata metadata = new PlaybackMetadata(this);.
+ * This will ensure, that the metadata is linked to this extension by using the {@link PlaybackMetadataExtension#getExtensionName()}.
+ * + * @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 + */ + public abstract void onClear(); + } } diff --git a/src/main/java/com/minecrafttas/tasmod/playback/metadata/PlaybackMetadataRegistry.java b/src/main/java/com/minecrafttas/tasmod/playback/metadata/PlaybackMetadataRegistry.java index 4c604261..658638f4 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/metadata/PlaybackMetadataRegistry.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/metadata/PlaybackMetadataRegistry.java @@ -3,131 +3,58 @@ import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; +import com.minecrafttas.mctcommon.registry.AbstractRegistry; import com.minecrafttas.tasmod.TASmod; +import com.minecrafttas.tasmod.events.EventPlaybackClient; +import com.minecrafttas.tasmod.playback.metadata.PlaybackMetadata.PlaybackMetadataExtension; /** - * Registry for registering custom metadata that is stored in the TASFile.
+ * Registry for registering custom metadata that is stored in the TASfile.
*
* The default metadata includes general information such as author name, * savestate/rerecord count and category.
*
* Any custom class has to extend PlaybackMetadataExtension * + * @author Scribble */ -public class PlaybackMetadataRegistry { +public class PlaybackMetadataRegistry extends AbstractRegistry implements EventPlaybackClient.EventRecordClear { - private static final Map METADATA_EXTENSION = new LinkedHashMap<>(); - - /** - * Registers a new class as a metadata extension - * - * @param extension - */ - public static void register(PlaybackMetadataExtension extension) { - if (extension == null) { - throw new NullPointerException("Tried to register a playback extension with value null"); - } - - if (containsClass(extension)) { - TASmod.LOGGER.warn("Trying to register the playback extension {}, but another instance of this class is already registered!", extension.getClass().getName()); - return; - } - - if(METADATA_EXTENSION.containsKey(extension.getExtensionName())) { - TASmod.LOGGER.warn("Trying to register the playback extension {}, but an extension with the same name is already registered!", extension.getExtensionName()); - return; - } - - METADATA_EXTENSION.put(extension.getExtensionName(), extension); - } - - public static void unregister(PlaybackMetadataExtension extension) { - if (extension == null) { - throw new NullPointerException("Tried to unregister an extension with value null"); - } - if (METADATA_EXTENSION.containsKey(extension.getExtensionName())) { - METADATA_EXTENSION.remove(extension.getExtensionName()); - } else { - TASmod.LOGGER.warn("Trying to unregister the playback extension {}, but it was not registered!", extension.getClass().getName()); - } + public PlaybackMetadataRegistry() { + super("METADATA_REGISTRY", new LinkedHashMap<>()); } public static void handleOnCreate() { } - public static List handleOnStore() { + public List handleOnStore() { List metadataList = new ArrayList<>(); - for(PlaybackMetadataExtension extension : METADATA_EXTENSION.values()) { + for(PlaybackMetadataExtension extension : REGISTRY.values()) { metadataList.add(extension.onStore()); } return metadataList; } - public static void handleOnLoad(List meta) { + public void handleOnLoad(List meta) { + if(meta.isEmpty()) + return; for(PlaybackMetadata metadata : meta) { - if(METADATA_EXTENSION.containsKey(metadata.getExtensionName())) { - PlaybackMetadataExtension extension = METADATA_EXTENSION.get(metadata.getExtensionName()); + if(REGISTRY.containsKey(metadata.getExtensionName())) { + PlaybackMetadataExtension extension = REGISTRY.get(metadata.getExtensionName()); extension.onLoad(metadata); } else { - TASmod.LOGGER.warn("The metadata extension {} was not found while loading the TASFile. Things might not be correctly loaded!", metadata.getExtensionName()); + TASmod.LOGGER.warn("The metadata extension {} was not found while loading the TASfile. Things might not be correctly loaded!", metadata.getExtensionName()); } } } - public static void handleOnClear() { - METADATA_EXTENSION.forEach((key, extension) ->{ + @Override + public void onClear() { + REGISTRY.forEach((key, extension) ->{ extension.onClear(); }); } - - private static boolean containsClass(PlaybackMetadataExtension newExtension) { - for (PlaybackMetadataExtension extension : METADATA_EXTENSION.values()) { - if (extension.getClass().equals(newExtension.getClass())) { - return true; - } - } - return false; - } - - public static interface PlaybackMetadataExtension { - - /** - * The name of this playback metadata extension.
- * The name is printed in the playback file and declares this "section".
- * It is also used in the {@link PlaybackMetadata} itself to link the metadata to the extension.
- * @return The name of this playback metadata extension. - */ - public String getExtensionName(); - - /** - * Currently unused.
- * Maybe in the future, TASes have to be created with /create, then you can interactively set the values...
- */ - public void onCreate(); - - /** - * Runs, when the TASfile is being stored to a file.
- * Create a new {@link PlaybackMetadata} with PlaybackMetadata metadata = new PlaybackMetadata(this);.
- * This will ensure, that the metadata is linked to this extension by using the {@link PlaybackMetadataExtension#getExtensionName()}.
- * - * @return The {@link PlaybackMetadata} to be saved in the TASfile - */ - public PlaybackMetadata onStore(); - - /** - * Runs when the TASfile is being loaded from a file
- * - * @param metadata The metadata for this extension to read from - */ - public void onLoad(PlaybackMetadata metadata); - - /** - * Runs when the PlaybackController is cleared - */ - public void onClear(); - } } diff --git a/src/main/java/com/minecrafttas/tasmod/playback/metadata/integrated/CreditsMetadataExtension.java b/src/main/java/com/minecrafttas/tasmod/playback/metadata/integrated/CreditsMetadataExtension.java index ddeb0a9d..a7eec12d 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/metadata/integrated/CreditsMetadataExtension.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/metadata/integrated/CreditsMetadataExtension.java @@ -6,7 +6,7 @@ import com.minecrafttas.tasmod.events.EventPlaybackClient.EventPlaybackJoinedWorld; import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; import com.minecrafttas.tasmod.playback.metadata.PlaybackMetadata; -import com.minecrafttas.tasmod.playback.metadata.PlaybackMetadataRegistry.PlaybackMetadataExtension; +import com.minecrafttas.tasmod.playback.metadata.PlaybackMetadata.PlaybackMetadataExtension; import com.minecrafttas.tasmod.playback.tasfile.exception.PlaybackLoadException; import com.minecrafttas.tasmod.util.LoggerMarkers; @@ -20,7 +20,7 @@ * Credits can be changed in the file and will be printed in chat, when the * player joins a world after /fullplay */ -public class CreditsMetadataExtension implements PlaybackMetadataExtension, EventPlaybackJoinedWorld, EventControllerStateChange { +public class CreditsMetadataExtension extends PlaybackMetadataExtension implements EventPlaybackJoinedWorld, EventControllerStateChange { /** * The title/category of the TAS (e.g. KillSquid - Any% Glitched) diff --git a/src/main/java/com/minecrafttas/tasmod/playback/metadata/integrated/StartpositionMetadataExtension.java b/src/main/java/com/minecrafttas/tasmod/playback/metadata/integrated/StartpositionMetadataExtension.java index 319588e0..81954595 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/metadata/integrated/StartpositionMetadataExtension.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/metadata/integrated/StartpositionMetadataExtension.java @@ -4,18 +4,19 @@ import java.nio.ByteBuffer; -import com.minecrafttas.mctcommon.server.exception.PacketNotImplementedException; -import com.minecrafttas.mctcommon.server.exception.WrongSideException; -import com.minecrafttas.mctcommon.server.interfaces.PacketID; -import com.minecrafttas.mctcommon.server.interfaces.ServerPacketHandler; +import com.minecrafttas.mctcommon.networking.exception.PacketNotImplementedException; +import com.minecrafttas.mctcommon.networking.exception.WrongSideException; +import com.minecrafttas.mctcommon.networking.interfaces.PacketID; +import com.minecrafttas.mctcommon.networking.interfaces.ServerPacketHandler; import com.minecrafttas.tasmod.TASmod; import com.minecrafttas.tasmod.TASmodClient; import com.minecrafttas.tasmod.events.EventPlaybackClient.EventControllerStateChange; import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; -import com.minecrafttas.tasmod.networking.TASmodPackets; import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; import com.minecrafttas.tasmod.playback.metadata.PlaybackMetadata; -import com.minecrafttas.tasmod.playback.metadata.PlaybackMetadataRegistry.PlaybackMetadataExtension; +import com.minecrafttas.tasmod.playback.metadata.PlaybackMetadata.PlaybackMetadataExtension; +import com.minecrafttas.tasmod.playback.tasfile.exception.PlaybackLoadException; +import com.minecrafttas.tasmod.registries.TASmodPackets; import com.minecrafttas.tasmod.util.LoggerMarkers; import net.minecraft.client.Minecraft; @@ -30,7 +31,7 @@ * * @author Scribble */ -public class StartpositionMetadataExtension implements PlaybackMetadataExtension, EventControllerStateChange, ServerPacketHandler { +public class StartpositionMetadataExtension extends PlaybackMetadataExtension implements EventControllerStateChange, ServerPacketHandler { /** * The startposition of the playback @@ -75,21 +76,47 @@ public PlaybackMetadata onStore() { metadata.setValue("x", Double.toString(startPosition.x)); metadata.setValue("y", Double.toString(startPosition.y)); metadata.setValue("z", Double.toString(startPosition.z)); - metadata.setValue("pitch", Double.toString(startPosition.pitch)); - metadata.setValue("yaw", Double.toString(startPosition.yaw)); + metadata.setValue("pitch", Float.toString(startPosition.pitch)); + metadata.setValue("yaw", Float.toString(startPosition.yaw)); return metadata; } @Override public void onLoad(PlaybackMetadata metadata) { - double x = Double.parseDouble(metadata.getValue("x")); - double y = Double.parseDouble(metadata.getValue("y")); - double z = Double.parseDouble(metadata.getValue("z")); - float pitch = Float.parseFloat(metadata.getValue("pitch")); - float yaw = Float.parseFloat(metadata.getValue("yaw")); + 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); this.startPosition = new StartPosition(x, y, z, pitch, yaw); } + + private double getDouble(String key, PlaybackMetadata metadata) { + String out = metadata.getValue(key); + if(out != null) { + try { + return Double.parseDouble(out); + } catch (NumberFormatException e) { + throw new PlaybackLoadException(e); + } + } else { + throw new PlaybackLoadException(String.format("Missing key %s in Start Position metadata", key)); + } + } + + private float getFloat(String key, PlaybackMetadata metadata) { + String out = metadata.getValue(key); + if(out != null) { + try { + return Float.parseFloat(out); + } catch (NumberFormatException e) { + throw new PlaybackLoadException(e); + } + } else { + throw new PlaybackLoadException(String.format("Missing key %s in Start Position metadata", key)); + } + } @Override public void onClear() { @@ -127,7 +154,6 @@ public void onControllerStateChange(TASstate newstate, TASstate oldstate) { } } } - } @Override 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 29b0db5e..e8945a7f 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiser.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiser.java @@ -1,472 +1,263 @@ package com.minecrafttas.tasmod.playback.tasfile; -import com.dselent.bigarraylist.BigArrayList; -import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.monitoring.DesyncMonitoring; -import com.minecrafttas.tasmod.playback.ControlByteHandler; -import com.minecrafttas.tasmod.playback.PlaybackControllerClient; -import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TickInputContainer; -import com.minecrafttas.tasmod.util.FileThread; -import com.minecrafttas.tasmod.util.LoggerMarkers; -import com.minecrafttas.tasmod.virtual.VirtualCameraAngle; -import com.minecrafttas.tasmod.virtual.VirtualKeyboard; -import com.minecrafttas.tasmod.virtual.VirtualMouse; -import com.mojang.realmsclient.util.Pair; -import org.apache.commons.io.FileUtils; - +import java.io.BufferedReader; import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; import java.io.IOException; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; -import java.util.Map; -import static com.minecrafttas.tasmod.TASmod.LOGGER; +import com.dselent.bigarraylist.BigArrayList; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TickContainer; +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.TASmodAPIRegistry; +import com.minecrafttas.tasmod.util.FileThread; /** - * Saves a given {@linkplain PlaybackControllerClient} to a file. Is also able to read an input container from a file.
- *
- * I plan to be backwards compatible so all the save functions have a V1 in their name by the time of writing this
- *
- * It also serializes the {@linkplain DesyncMonitoring} from the input container
- *
- * Side: Client + * Loads and stores the {@link PlaybackControllerClient} to/from a file.
* - * @author ScribbleLP - * + * @author Scribble */ public class PlaybackSerialiser { - + + private static String defaultFlavor = "beta1"; + /** - * A list of sections to check for in the playback file - * @author ScribbleLP - * + * Saves the {@link PlaybackControllerClient} to a file + * + * @param file The file to save the serialised inputs to + * @param controller The {@link PlaybackControllerClient} to use. Uses the {@link PlaybackControllerClient#getInputs() getInputs()} method, to extract the ticks. + * @param flavorName The name of the {@link SerialiserFlavorBase flavor} to use for the tasfile + * @throws PlaybackSaveException When a saving operation fails */ - public enum SectionsV1{ - TICKS("Ticks", ""), - KEYBOARD("Keyboard", "(\\|Keyboard:)"), - MOUSE("Mouse", "(\\|Mouse:)"), - CAMERA("Camera", "(\\|Camera:)"); - - private String name; - private String regex; - - private SectionsV1(String nameIn, String regexIn) { - name=nameIn; - regex=regexIn; - } - - public String getName() { - return name; - } - - public String getRegex() { - return regex; - } - - public static String getRegexString() { - String out=""; - for(SectionsV1 section : values()) { - if(!section.getRegex().isEmpty()) { - String seperator="|"; - if(values().length-1==section.ordinal()) { - seperator=""; - } - out=out.concat(section.getRegex()+seperator); - } - } - return out; + public static void saveToFile(File file, PlaybackControllerClient controller, String flavorName) throws PlaybackSaveException { + saveToFile(file, controller, flavorName, -1L); + } + + /** + * Saves the {@link PlaybackControllerClient} partially to a file + * + * @param file The file to save the serialised inputs to + * @param controller The {@link PlaybackControllerClient} to use. Uses the {@link PlaybackControllerClient#getInputs() getInputs()} method, to extract the ticks. + * @param flavorName The name of the {@link SerialiserFlavorBase flavor} to use for the tasfile + * @param stopIndex The index at which the serialiser stops. Use -1L to parse the entire file + * @throws PlaybackSaveException When a saving operation fails + */ + public static void saveToFile(File file, PlaybackControllerClient controller, String flavorName, long stopIndex) throws PlaybackSaveException { + if (controller == null) { + throw new PlaybackSaveException("Save to file failed. No controller specified"); } + saveToFile(file, controller.getInputs(), flavorName, stopIndex); } - + /** - * Saves all inputs of the input container - * @param file Where to save the container - * @param container The container to save - * @throws IOException When the input container is empty + * Saves a BigArrayList of {@link TickContainer TickContainers} to a file + * + * @param file The file to save the serialised inputs to + * @param container The list of {@link TickContainer TickContainers} to use + * @param flavorName The name of the {@link SerialiserFlavorBase flavor} to use for the tasfile + * @throws PlaybackSaveException When a saving operation fails */ - public void saveToFileV1(File file, PlaybackControllerClient container) throws IOException { - saveToFileV1Until(file, container, -1); + public static void saveToFile(File file, BigArrayList container, String flavorName) throws PlaybackSaveException { + saveToFile(file, container, flavorName, -1); } - + /** - * Saves inputs up to a certain index of the input container - * @param file Where to save the container - * @param container The container to save - * @param index index until the inputs get saved - * @throws IOException When the input container is empty + * Saves a BigArrayList of {@link TickContainer TickContainers} partially to a file + * @param file The file to save the serialised inputs to + * @param container The list of {@link TickContainer TickContainers} to use + * @param flavorName The name of the {@link SerialiserFlavorBase flavor} to use for the tasfile + * @param stopIndex The index at which the serialiser stops. Use -1L to parse the entire file + * @throws PlaybackSaveException When a saving operation fails */ - public void saveToFileV1Until(File file, PlaybackControllerClient container, int index) throws IOException{ - LOGGER.debug(LoggerMarkers.Playback, "Saving playback controller to file {}", file); - if (container.size() == 0) { - throw new IOException("There are no inputs to save to a file"); + public static void saveToFile(File file, BigArrayList container, String flavorName, long stopIndex) throws PlaybackSaveException { + if (file == null) { + throw new PlaybackSaveException("Save to file failed. No file specified"); } - FileThread fileThread = new FileThread(file, false); -// FileThread monitorThread= new FileThread(new File(file, "../"+file.getName().replace(".mctas", "")+".mon"), false); - fileThread.start(); -// monitorThread.start(); - -// fileThread.addLine("################################################# 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" -// + "# #\n" -// + "#------------------------------------------------ Header ---------------------------------------------------#\n" -// + "#Author:" + container.getAuthors() + "\n" -// + "# #\n" -// + "#Title:" + container.getTitle() + "\n" -// + "# #\n" -// + "#Playing Time:" + container.getPlaytime() + "\n" -// + "# #\n" -// + "#Rerecords:"+container.getRerecords() + "\n" -// + "# #\n" -// + "#----------------------------------------------- Settings --------------------------------------------------#\n" -// + "#StartPosition:"+container.getStartLocation()+"\n" -// + "# #\n" -// + "#StartSeed:" + container.getStartSeed() + "\n" -// + "#############################################################################################################\n" -// + "#Comments start with \"//\" at the start of the line, comments with # will not be saved\n"); - - BigArrayList ticks = container.getInputs(); - Map>> cbytes= container.getControlBytes(); - Map> comments = container.getComments(); - - for (int i = 0; i < ticks.size(); i++) { - if(i==index) { - break; - } - - // Add comments - if(comments.containsKey(i)) { - List multiLine=comments.get(i); - multiLine.forEach(comment -> { - fileThread.addLine("//"+comment+"\n"); - }); - } - - // Add controlbytes - if(cbytes.containsKey(i)) { - List> cbytelist= cbytes.get(i); - String cbyteString= ControlByteHandler.toString(cbytelist); - if(!cbyteString.isEmpty()) { - fileThread.addLine(cbyteString); - } - } - - // Add a data line - TickInputContainer tickInput = ticks.get(i); - fileThread.addLine(tickInput.toString() + "~&\t\t\t\t//Monitoring:"+container.desyncMonitor.get(i)+"\n"); + if (container == null) { + throw new PlaybackSaveException("Save to file failed. No tickcontainer list specified"); } - fileThread.close(); - } - public int getFileVersion(File file) throws IOException { - LOGGER.trace(LoggerMarkers.Playback, "Retrieving file version from {}", file); - List lines = FileUtils.readLines(file, Charset.defaultCharset()); - for (String line : lines) { - if (line.contains("Version")) { - String trimmed = line.replaceAll("#|\t", ""); - int tick=0; - try { - tick=Integer.parseInt(trimmed.split(":")[1]); - } catch (NumberFormatException e) { - throw new IOException("Can't read the file version: "+trimmed); - } - return tick; - } + if (flavorName == null || flavorName.isEmpty()) { + if (defaultFlavor == null || defaultFlavor.isEmpty()) + throw new PlaybackSaveException("No default flavor specified... Please specify a flavor name first"); + flavorName = defaultFlavor; + } else { + defaultFlavor = flavorName; } - return 0; - } - public PlaybackControllerClient fromEntireFileV1(File file) throws IOException { - LOGGER.debug(LoggerMarkers.Playback, "Loading playback controller to file {}", file); - List lines = FileUtils.readLines(file, StandardCharsets.UTF_8); - - File monitorFile=new File(file, "../"+file.getName().replace(".mctas", "")+".mon"); - - List monitorLines=new ArrayList<>(); - - // Read the legacy monitoring file system. Still reads the file but deletes it afterwards - if(monitorFile.exists()) { - monitorLines = FileUtils.readLines(monitorFile, StandardCharsets.UTF_8); - monitorFile.delete(); + FileThread writerThread; + try { + writerThread = new FileThread(file, false); + } catch (FileNotFoundException e) { + throw new PlaybackSaveException(e, "Trying to save the file %s, but the file can't be created", file.getName()); } - boolean oldmonfileLoaded=!monitorLines.isEmpty(); + writerThread.start(); - PlaybackControllerClient controller = new PlaybackControllerClient(); + SerialiserFlavorBase flavor = TASmodAPIRegistry.SERIALISER_FLAVOR.getFlavor(flavorName); - String author = "Insert author here"; + List header = flavor.serialiseHeader(); + for (String line : header) { + writerThread.addLine(line); + } - String title = "Insert TAS category here"; + BigArrayList tickLines = flavor.serialise(container, stopIndex); + for (long i = 0; i < tickLines.size(); i++) { + writerThread.addLine(tickLines.get(i)); + } - String playtime = "00:00.0"; + writerThread.close(); + } - int rerecords = 0; - - // No default start location - String startLocation=""; - - // Default the start seed to the current global ktrng seed. If KTRNG is not loaded, defaults to 0 - long startSeed=TASmod.ktrngHandler.getGlobalSeedClient(); + /** + * Loads a BigArrayList of {@link TickContainer TickContainers} from a file.
+ * Tries to determine the {@link SerialiserFlavorBase flavor} by reading the header of the TASfile + * + * @param file The file to load from + * @return The loaded BigArrayList of {@link TickContainer TickContainers} + * @throws PlaybackLoadException If the file contains errors + * @throws IOException If the file could not be read + */ + public static BigArrayList loadFromFile(File file) throws PlaybackLoadException, IOException { + if (file == null) { + throw new PlaybackLoadException("Load from file failed. No file specified"); + } + if (!file.exists()) { + throw new PlaybackLoadException("Trying to load %s but the file doesn't exist", file.getName()); + } - // Clear the current container before reading new data - controller.clear(); + SerialiserFlavorBase flavor = readFlavor(file); - int linenumber = 0; //The current line number + return loadFromFile(file, flavor); + } + + /** + * Loads a BigArrayList of {@link TickContainer TickContainers} from a file, with a specific flavor + * + * @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 + * @return The loaded BigArrayList of {@link TickContainer TickContainers} + * @throws PlaybackLoadException If the file contains errors + * @throws IOException If the file could not be read + */ + public static BigArrayList loadFromFile(File file, String flavorName) throws PlaybackLoadException, IOException { - for (String line : lines) { - linenumber++; - int tickcount=(int) controller.getInputs().size(); - // Read out header - if (line.startsWith("#")) { - // Read author tag - 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 = Integer.parseInt(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:", "")); - } - // Read control bytes - } else if (line.startsWith("$") && line.replace('$', ' ').trim().contains(" ")) { - String[] sections = line.replace('$', ' ').trim().split(" ", 2); - if (sections.length == 0) - continue; - String control = sections[0]; - String[] params = sections[1].split(" "); - List> cbytes = controller.getControlBytes().getOrDefault(tickcount, new ArrayList<>()); - cbytes.add(Pair.of(control, params)); - controller.getControlBytes().put(tickcount, cbytes); - //Read comments - } else if (line.startsWith("//")) { - List commentList = controller.getComments().getOrDefault(tickcount, new ArrayList<>()); - commentList.add(line.replace("//", "")); - controller.getComments().put(tickcount, commentList); - //Read data - } else { - - // Splitting the line into a data- and commentPart, the comment part will most likely contain the Monitoring - String dataPart=line; - String commentPart=""; - if(line.contains("~&")) { - String[] splitComments=line.split("~&"); - dataPart=splitComments[0]; - commentPart=splitComments[1]; - } - String[] sections = dataPart.split(SectionsV1.getRegexString()); - - if (sections.length != SectionsV1.values().length) { - throw new IOException("Error in line " + linenumber + ". Cannot read the line correctly"); - } - - controller.getInputs().add(new TickInputContainer(readTicks(sections[0], linenumber), readKeyboard(sections[1], linenumber), readMouse(sections[2], linenumber), readSubtick(sections[3], linenumber))); - - if(!oldmonfileLoaded) { - String[] commentData = commentPart.split("Monitoring:"); - if(commentData.length==2) { - monitorLines.add(commentData[1]); - } - } - } - } -// controller.setAuthors(author); -// controller.setTitle(title); -// controller.setPlaytime(playtime); -// controller.setRerecords(rerecords); -// controller.setStartLocation(startLocation); -// controller.setStartSeed(startSeed); - if(!monitorLines.isEmpty()) { - controller.desyncMonitor = new DesyncMonitoring(controller, monitorLines); + // If the flavor is null or empty, try to determine the flavor by reading the header + if (flavorName == null || flavorName.isEmpty()) { + return loadFromFile(file); } - - //If an old monitoring file is loaded, save the file immediately to not loose any data. - if(oldmonfileLoaded) { - saveToFileV1(file, controller); + + // Try to get the flavor from the registry via its name + SerialiserFlavorBase flavor = TASmodAPIRegistry.SERIALISER_FLAVOR.getFlavor(flavorName); + + if (flavor == null) { + throw new PlaybackLoadException("Flavor name %s doesn't exist.", flavorName); } - - return controller; - } - private int readTicks(String section, int linenumber) throws IOException { - int ticks = 0; - try { - ticks = Integer.parseInt(section); - } catch (NumberFormatException e) { - throw new IOException(section + " is not a recognised number in line " + linenumber); + // 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"); } - return ticks; + + return loadFromFile(file, flavor); } - - private VirtualKeyboard readKeyboard(String section, int linenumber) throws IOException { - VirtualKeyboard keyboard = new VirtualKeyboard(); - // Remove the prefix - section = section.replace("Keyboard:", ""); + /** + * Loads a BigArrayList of {@link TickContainer TickContainers} from a file, with a specific flavor + * + * @param file The file to load from + * @param flavor The {@link SerialiserFlavorBase flavor} to use. If the detected flavor in the TASfile mismatches, a {@link PlaybackLoadException} is thrown + * @return The loaded BigArrayList of {@link TickContainer TickContainers} + * @throws PlaybackLoadException If the file contains errors + * @throws IOException If the file could not be read + */ + public static BigArrayList loadFromFile(File file, SerialiserFlavorBase flavor) throws PlaybackLoadException, IOException { + if (file == null) { + throw new PlaybackLoadException("Load from file failed. No file specified"); + } - // Split in keys and characters - String[] keys = section.split(";"); + // Read file + BufferedReader reader = null; - // If there is nothing, return the empty keyboard - if (keys.length == 0) { - return keyboard; + try { + reader = new BufferedReader(new FileReader(file)); + } catch (FileNotFoundException e) { + throw new PlaybackLoadException("Trying to load %s, but the file doesn't exist", file.getName()); } - // Check if the keylist is empty - if (!keys[0].isEmpty()) { - - // Split multiple keys - String[] splitKeys = keys[0].split(","); - - for (String key : splitKeys) { - -// VirtualKey vkey = null; -// // Check if the key is a keycode -// if (isNumeric(key)) { -// vkey = keyboard.get(Integer.parseInt(key)); -// } else { -// vkey = keyboard.get(key); -// } -// -// if (vkey == null) { -// throw new IOException(key + " is not a recognised keyboard key in line " + linenumber); -// } -// -// vkey.setPressed(true); - } + BigArrayList lines = new BigArrayList<>(); + String line = null; + while ((line = reader.readLine()) != null) { + lines.add(line); } - - char[] chars = {}; - //Check if the characterlist is empty - if (keys.length == 2) { - chars = keys[1].replace("\\n", "\n").toCharArray(); //Replacing the "\n" in lines to the character \n - } - - for (char onechar : chars) { -// keyboard.addChar(onechar); - } - return keyboard; + reader.close(); + + // Deserialise Header + List headerLines = flavor.extractHeader(lines); + flavor.deserialiseHeader(headerLines); + + // Deserialise main data + BigArrayList deserialisedContainers = flavor.deserialise(lines, headerLines.size()); + + return deserialisedContainers; } - private VirtualMouse readMouse(String section, int linenumber) throws IOException { - 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(","); - for (String button : splitButtons) { - -// VirtualKey vkey = null; -// // Check if the key is a keycode -// if (isNumeric(button)) { -// vkey = mouse.get(Integer.parseInt(button)); -// } else { -// vkey = mouse.get(button); -// } -// if (vkey == null) { -// throw new IOException(button + " is not a recognised mouse key in line " + linenumber); -// } -// mouse.get(button).setPressed(true); + /** + * Searches in a list of lines if one of the {@link SerialiserFlavorBase flavors} matches + * @param lines The lines to search through + * @param flavorList The list of {@link SerialiserFlavorBase flavor} to check for + * @return A copy of the {@link SerialiserFlavorBase flavor} that was found + * @throws PlaybackLoadException If no {@link SerialiserFlavorBase flavor} was found + */ + public static SerialiserFlavorBase searchForFlavor(List lines, List flavorList) { + for (SerialiserFlavorBase flavor : flavorList) { + if (flavor.deserialiseFlavorName(lines)) { + return flavor.clone(); } } -// mouse.setPath(readPath(path, linenumber, mouse)); - - return mouse; + throw new PlaybackLoadException("Couldn't find a flavorname in the file. TASfile is missing a flavor-extension or the file is broken"); } -// private List readPath(String section, int linenumber, VirtualMouse mouse) throws IOException { -// List path = new ArrayList(); -// -// 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 IOException("'" + pathNode + "' couldn't be read in line " + linenumber+": Something is not a number"); -// } catch (ArrayIndexOutOfBoundsException e) { -// throw new IOException("'" + pathNode + "' couldn't be read in line " + linenumber+": Something is missing or is too much"); -// } -// PathNode node = mouse.new PathNode(); -// for (int i=0; i lines = new ArrayList<>(); + String line = null; + + // Reads the first 100 lines + for (int i = 0; i < 100; i++) { + + line = reader.readLine(); + + if (line != null) { + lines.add(line); + } } - return true; + reader.close(); + + SerialiserFlavorBase flavor = null; + + flavor = searchForFlavor(lines, TASmodAPIRegistry.SERIALISER_FLAVOR.getFlavors()); + return flavor; } } diff --git a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiserBase.java b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiserBase.java deleted file mode 100644 index 305b3a74..00000000 --- a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiserBase.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.minecrafttas.tasmod.playback.tasfile; - -import java.util.ArrayList; -import java.util.List; - -import com.minecrafttas.tasmod.playback.PlaybackControllerClient; - -public abstract class PlaybackSerialiserBase { - - public PlaybackSerialiserBase(PlaybackControllerClient controller) { - if(controller == null) { - throw new NullPointerException("Parameter controller can't be null"); - } - - - } - - public void onSave() { - - } - - public void onLoad() { - - } - - public List serialize() { - List out = new ArrayList<>(); - return out; - } - - public void deserialize(List in) { - - } - - public List serializeMetadata(){ - return null; - } - - public void deserializeMetadata(List metadataString) { - - } -} diff --git a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiserOld.java b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiserOld.java new file mode 100644 index 00000000..9a36dcf2 --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiserOld.java @@ -0,0 +1,471 @@ +package com.minecrafttas.tasmod.playback.tasfile; + +import static com.minecrafttas.tasmod.TASmod.LOGGER; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.io.FileUtils; + +import com.dselent.bigarraylist.BigArrayList; +import com.minecrafttas.tasmod.TASmod; +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.util.FileThread; +import com.minecrafttas.tasmod.util.LoggerMarkers; +import com.minecrafttas.tasmod.virtual.VirtualCameraAngle; +import com.minecrafttas.tasmod.virtual.VirtualKeyboard; +import com.minecrafttas.tasmod.virtual.VirtualMouse; + +/** + * Saves a given {@linkplain PlaybackControllerClient} to a file. Is also able to read an input container from a file.
+ *
+ * I plan to be backwards compatible so all the save functions have a V1 in their name by the time of writing this
+ *
+ * It also serializes the {@linkplain DesyncMonitorFileCommandExtension} from the input container
+ *
+ * Side: Client + * + * @author ScribbleLP + * + */ +@Deprecated +public class PlaybackSerialiserOld { + + /** + * A list of sections to check for in the playback file + * @author ScribbleLP + * + */ + public enum SectionsV1{ + TICKS("Ticks", ""), + KEYBOARD("Keyboard", "(\\|Keyboard:)"), + MOUSE("Mouse", "(\\|Mouse:)"), + CAMERA("Camera", "(\\|Camera:)"); + + private String name; + private String regex; + + private SectionsV1(String nameIn, String regexIn) { + name=nameIn; + regex=regexIn; + } + + public String getName() { + return name; + } + + public String getRegex() { + return regex; + } + + public static String getRegexString() { + String out=""; + for(SectionsV1 section : values()) { + if(!section.getRegex().isEmpty()) { + String seperator="|"; + if(values().length-1==section.ordinal()) { + seperator=""; + } + out=out.concat(section.getRegex()+seperator); + } + } + return out; + } + } + + /** + * Saves all inputs of the input container + * @param file Where to save the container + * @param container The container to save + * @throws IOException When the input container is empty + */ + public void saveToFileV1(File file, PlaybackControllerClient container) throws IOException { + saveToFileV1Until(file, container, -1); + } + + /** + * Saves inputs up to a certain index of the input container + * @param file Where to save the container + * @param container The container to save + * @param index index until the inputs get saved + * @throws IOException When the input container is empty + */ + public void saveToFileV1Until(File file, PlaybackControllerClient container, long index) throws IOException{ + LOGGER.debug(LoggerMarkers.Playback, "Saving playback controller to file {}", file); + if (container.size() == 0) { + throw new IOException("There are no inputs to save to a file"); + } + FileThread fileThread = new FileThread(file, false); +// FileThread monitorThread= new FileThread(new File(file, "../"+file.getName().replace(".mctas", "")+".mon"), false); + + fileThread.start(); +// monitorThread.start(); + +// fileThread.addLine("################################################# 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" +// + "# #\n" +// + "#------------------------------------------------ Header ---------------------------------------------------#\n" +// + "#Author:" + container.getAuthors() + "\n" +// + "# #\n" +// + "#Title:" + container.getTitle() + "\n" +// + "# #\n" +// + "#Playing Time:" + container.getPlaytime() + "\n" +// + "# #\n" +// + "#Rerecords:"+container.getRerecords() + "\n" +// + "# #\n" +// + "#----------------------------------------------- Settings --------------------------------------------------#\n" +// + "#StartPosition:"+container.getStartLocation()+"\n" +// + "# #\n" +// + "#StartSeed:" + container.getStartSeed() + "\n" +// + "#############################################################################################################\n" +// + "#Comments start with \"//\" at the start of the line, comments with # will not be saved\n"); + + BigArrayList ticks = container.getInputs(); +// Map>> cbytes= container.getControlBytes(); +// Map> comments = container.getComments(); + +// for (int i = 0; i < ticks.size(); i++) { +// if(i==index) { +// break; +// } +// +// // Add comments +// if(comments.containsKey(i)) { +// List multiLine=comments.get(i); +// multiLine.forEach(comment -> { +// fileThread.addLine("//"+comment+"\n"); +// }); +// } + + // Add controlbytes +// if(cbytes.containsKey(i)) { +// List> cbytelist= cbytes.get(i); +// String cbyteString= ControlByteHandler.toString(cbytelist); +// if(!cbyteString.isEmpty()) { +// fileThread.addLine(cbyteString); +// } +// } +// +// // Add a data line +// TickContainer tickInput = ticks.get(i); +// fileThread.addLine(tickInput.toString() + "~&\t\t\t\t//Monitoring:"+container.desyncMonitor.get(i)+"\n"); +// } +// fileThread.close(); + } + + public int getFileVersion(File file) throws IOException { + LOGGER.trace(LoggerMarkers.Playback, "Retrieving file version from {}", file); + List lines = FileUtils.readLines(file, Charset.defaultCharset()); + for (String line : lines) { + if (line.contains("Version")) { + String trimmed = line.replaceAll("#|\t", ""); + int tick=0; + try { + tick=Integer.parseInt(trimmed.split(":")[1]); + } catch (NumberFormatException e) { + throw new IOException("Can't read the file version: "+trimmed); + } + return tick; + } + } + return 0; + } + + public PlaybackControllerClient fromEntireFileV1(File file) throws IOException { + LOGGER.debug(LoggerMarkers.Playback, "Loading playback controller to file {}", file); + List lines = FileUtils.readLines(file, StandardCharsets.UTF_8); + + File monitorFile=new File(file, "../"+file.getName().replace(".mctas", "")+".mon"); + + List monitorLines=new ArrayList<>(); + + // Read the legacy monitoring file system. Still reads the file but deletes it afterwards + if(monitorFile.exists()) { + monitorLines = FileUtils.readLines(monitorFile, StandardCharsets.UTF_8); + monitorFile.delete(); + } + boolean oldmonfileLoaded=!monitorLines.isEmpty(); + + PlaybackControllerClient controller = new PlaybackControllerClient(); + + String author = "Insert author here"; + + String title = "Insert TAS category here"; + + String playtime = "00:00.0"; + + int rerecords = 0; + + // No default start location + String startLocation=""; + + // Default the start seed to the current global ktrng seed. If KTRNG is not loaded, defaults to 0 + long startSeed=TASmod.ktrngHandler.getGlobalSeedClient(); + + // Clear the current container before reading new data + controller.clear(); + + int linenumber = 0; //The current line number + + for (String line : lines) { + linenumber++; + int tickcount=(int) controller.getInputs().size(); + // Read out header + if (line.startsWith("#")) { + // Read author tag + 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 = Integer.parseInt(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:", "")); + } + // Read control bytes + } else if (line.startsWith("$") && line.replace('$', ' ').trim().contains(" ")) { + String[] sections = line.replace('$', ' ').trim().split(" ", 2); + if (sections.length == 0) + continue; + String control = sections[0]; + String[] params = sections[1].split(" "); +// List> cbytes = controller.getControlBytes().getOrDefault(tickcount, new ArrayList<>()); +// cbytes.add(Pair.of(control, params)); +// controller.getControlBytes().put(tickcount, cbytes); + //Read comments + } else if (line.startsWith("//")) { +// List commentList = controller.getComments().getOrDefault(tickcount, new ArrayList<>()); +// commentList.add(line.replace("//", "")); +// controller.getComments().put(tickcount, commentList); + //Read data + } else { + + // Splitting the line into a data- and commentPart, the comment part will most likely contain the Monitoring + String dataPart=line; + String commentPart=""; + if(line.contains("~&")) { + String[] splitComments=line.split("~&"); + dataPart=splitComments[0]; + commentPart=splitComments[1]; + } + String[] sections = dataPart.split(SectionsV1.getRegexString()); + + if (sections.length != SectionsV1.values().length) { + throw new IOException("Error in line " + linenumber + ". Cannot read the line correctly"); + } + +// controller.getInputs().add(new TickInputContainer(readTicks(sections[0], linenumber), readKeyboard(sections[1], linenumber), readMouse(sections[2], linenumber), readSubtick(sections[3], linenumber))); + + if(!oldmonfileLoaded) { + String[] commentData = commentPart.split("Monitoring:"); + if(commentData.length==2) { + monitorLines.add(commentData[1]); + } + } + } + } +// controller.setAuthors(author); +// controller.setTitle(title); +// controller.setPlaytime(playtime); +// controller.setRerecords(rerecords); +// controller.setStartLocation(startLocation); +// controller.setStartSeed(startSeed); + if(!monitorLines.isEmpty()) { +// controller.desyncMonitor = new DesyncMonitoringFileCommand(controller, monitorLines); + } + + //If an old monitoring file is loaded, save the file immediately to not loose any data. + if(oldmonfileLoaded) { + saveToFileV1(file, controller); + } + + return controller; + } + + private int readTicks(String section, int linenumber) throws IOException { + int ticks = 0; + try { + ticks = Integer.parseInt(section); + } catch (NumberFormatException e) { + throw new IOException(section + " is not a recognised number in line " + linenumber); + } + return ticks; + } + + private VirtualKeyboard readKeyboard(String section, int linenumber) throws IOException { + VirtualKeyboard keyboard = new VirtualKeyboard(); + + // Remove the prefix + section = section.replace("Keyboard:", ""); + + // Split in keys and characters + String[] keys = section.split(";"); + + // If there is nothing, return the empty keyboard + if (keys.length == 0) { + return keyboard; + } + + // Check if the keylist is empty + if (!keys[0].isEmpty()) { + + // Split multiple keys + String[] splitKeys = keys[0].split(","); + + for (String key : splitKeys) { + +// VirtualKey vkey = null; +// // Check if the key is a keycode +// if (isNumeric(key)) { +// vkey = keyboard.get(Integer.parseInt(key)); +// } else { +// vkey = keyboard.get(key); +// } +// +// if (vkey == null) { +// throw new IOException(key + " is not a recognised keyboard key in line " + linenumber); +// } +// +// vkey.setPressed(true); + } + } + + char[] chars = {}; + //Check if the characterlist is empty + if (keys.length == 2) { + chars = keys[1].replace("\\n", "\n").toCharArray(); //Replacing the "\n" in lines to the character \n + } + + for (char onechar : chars) { +// keyboard.addChar(onechar); + } + return keyboard; + } + + private VirtualMouse readMouse(String section, int linenumber) throws IOException { + 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(","); + for (String button : splitButtons) { + +// VirtualKey vkey = null; +// // Check if the key is a keycode +// if (isNumeric(button)) { +// vkey = mouse.get(Integer.parseInt(button)); +// } else { +// vkey = mouse.get(button); +// } +// if (vkey == null) { +// throw new IOException(button + " is not a recognised mouse key in line " + linenumber); +// } +// mouse.get(button).setPressed(true); + } + } +// mouse.setPath(readPath(path, linenumber, mouse)); + + return mouse; + } + +// private List readPath(String section, int linenumber, VirtualMouse mouse) throws IOException { +// List path = new ArrayList(); +// +// 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 IOException("'" + pathNode + "' couldn't be read in line " + linenumber+": Something is not a number"); +// } catch (ArrayIndexOutOfBoundsException e) { +// throw new IOException("'" + pathNode + "' couldn't be read in line " + linenumber+": Something is missing or is too much"); +// } +// PathNode node = mouse.new PathNode(); +// for (int i=0; i serialiseHeader() { + List out = new ArrayList<>(); + out.add(headerStart()); + serialiseFlavorName(out); + serialiseFileCommandNames(out); + serialiseMetadata(out); + out.add(headerEnd()); + return out; + } + + protected void serialiseFlavorName(List out) { + out.add("Flavor: " + getExtensionName()); + } + + protected void serialiseFileCommandNames(List out) { + List stringlist = new ArrayList<>(); + List extensionList = TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.getEnabled(); + extensionList.forEach(extension -> stringlist.add(extension.getExtensionName())); + out.add("FileCommand-Extensions: " + String.join(", ", stringlist)); + out.add(""); + } + + protected void serialiseMetadata(List out) { + List metadataList = TASmodAPIRegistry.PLAYBACK_METADATA.handleOnStore(); + + for (PlaybackMetadata metadata : metadataList) { + serialiseMetadataName(out, metadata.getExtensionName()); + serialiseMetadataValue(out, metadata.getData()); + out.add(""); + } + } + + protected void serialiseMetadataName(List out, String name) { + out.add(createCenteredHeading(name, '-', 50)); + } + + protected void serialiseMetadataValue(List out, LinkedHashMap data) { + data.forEach((key, value) -> { + out.add(String.format("%s:%s", key, value)); + }); + } + + public BigArrayList serialise(BigArrayList inputs, long toTick) { + BigArrayList out = new BigArrayList<>(); + + for (int i = 0; i < inputs.size(); i++) { + if (toTick == i) { + break; + } + currentTick = i; + TickContainer container = inputs.get(i); + serialiseContainer(out, container); + previousTickContainer = container; + } + return out; + } + + protected void serialiseContainer(BigArrayList out, TickContainer container) { + currentLine = out.size()-1; + List serialisedKeyboard = serialiseKeyboard(container.getKeyboard()); + List serialisedMouse = serialiseMouse(container.getMouse()); + List serialisedCameraAngle = serialiseCameraAngle(container.getCameraAngle()); + pruneListEndEmpty(serialisedCameraAngle); + + PlaybackFileCommandContainer fileCommandsInline = TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.handleOnSerialiseInline(currentTick, container); + 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 serialisedEndlineComments = serialiseEndlineComments(comments.getEndlineComments(), fileCommandsEndline.valuesBySubtick()); + + addAll(out, serialisedInlineCommments); + + mergeInputs(out, serialisedKeyboard, serialisedMouse, serialisedCameraAngle, serialisedEndlineComments); + } + + protected String serialiseFileCommand(PlaybackFileCommand fileCommand) { + 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 List serialiseKeyboard(VirtualKeyboard keyboard) { + List out = new ArrayList<>(); + + List subticks = new ArrayList<>(keyboard.getAll()); + pruneListEndEmptySubtickable(subticks); + + for (VirtualKeyboard subtick : subticks) { + out.add(subtick.toString2()); + } + return out; + } + + protected List serialiseMouse(VirtualMouse mouse) { + List out = new ArrayList<>(); + + List subticks = new ArrayList<>(mouse.getAll()); + pruneListEndEmptySubtickable(subticks); + + for (VirtualMouse subtick : subticks) { + out.add(subtick.toString2()); + } + return out; + } + + protected List serialiseCameraAngle(VirtualCameraAngle cameraAngle) { + + VirtualCameraAngle previousCamera = null; + if(previousTickContainer != null) { + previousCamera = previousTickContainer.getCameraAngle(); + } + + List out = new ArrayList<>(); + for (VirtualCameraAngle subtick : cameraAngle.getAll()) { + + if(!subtick.equals(previousCamera)) + out.add(subtick.toString2()); + + previousCamera = subtick; + } + return out; + } + + protected List serialiseInlineComments(List inlineComments, List> fileCommandsInline) { + List out = new ArrayList<>(); + + Queue> fileCommandQueue = null; + if (fileCommandsInline != null) { + fileCommandQueue = new LinkedList<>(fileCommandsInline); + } + + // Serialise comments and merge them with file commands + if (inlineComments != null) { + + Queue commentQueue = new LinkedList<>(inlineComments); + + // 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 = 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 + if (comment == null && command == null) { + out.add(""); + continue; + } + + out.add(String.format("// %s", 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 = serialiseFileCommandsInLine(fileCommandQueue.poll()); + if (command != null) { + out.add(String.format("// %s", command)); + } else { + out.add(""); // Add an empty line if command is null + } + } + } + + return out; + } + + protected List serialiseEndlineComments(List endlineComments, List> fileCommandsEndline) { + return serialiseInlineComments(endlineComments, fileCommandsEndline); + } + + protected void mergeInputs(BigArrayList out, List serialisedKeyboard, List serialisedMouse, List serialisedCameraAngle, List serialisedEndlineComments) { + Queue keyboardQueue = new LinkedBlockingQueue<>(serialisedKeyboard); + Queue mouseQueue = new LinkedBlockingQueue<>(serialisedMouse); + Queue cameraAngleQueue = new LinkedBlockingQueue<>(serialisedCameraAngle); + Queue endlineQueue = new LinkedBlockingQueue<>(serialisedEndlineComments); + + String kb = getOrEmpty(keyboardQueue.poll()); + String ms = getOrEmpty(mouseQueue.poll()); + String ca = getOrEmpty(cameraAngleQueue.poll()); + + String elc = getOrEmpty(endlineQueue.poll()); + if (!elc.isEmpty()) { + elc = "\t\t" + elc; + } + + out.add(String.format("%s|%s|%s|%s%s", currentTick, kb, ms, ca, elc)); + + currentSubtick = 0; + while (!keyboardQueue.isEmpty() || !mouseQueue.isEmpty() || !cameraAngleQueue.isEmpty()) { + currentSubtick++; + kb = getOrEmpty(keyboardQueue.poll()); + ms = getOrEmpty(mouseQueue.poll()); + ca = getOrEmpty(cameraAngleQueue.poll()); + + out.add(String.format("\t%s|%s|%s|%s", currentSubtick, kb, ms, ca)); + } + currentSubtick = 0; + } + + protected String getOrEmpty(String string) { + return string == null ? "" : string; + } + + /** + * 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, Iterable args) { + String out = ""; + + List copy = new ArrayList<>(); + + args.forEach((arg) -> { + if (arg != null && !arg.isEmpty()) { + copy.add(arg); + } + }); + + out = String.join(delimiter, copy); + + return out; + } + + protected String joinNotEmpty(String delimiter, String... args) { + return joinNotEmpty(delimiter, Arrays.asList(args)); + } + + /*======================================================== + _____ _ _ _ + | __ \ (_) | (_) + | | | | ___ ___ ___ _ __ _ __ _| |_ ___ ___ + | | | |/ _ \/ __|/ _ \ '__| |/ _` | | / __|/ _ \ + | |__| | __/\__ \ __/ | | | (_| | | \__ \ __/ + |_____/ \___||___/\___|_| |_|\__,_|_|_|___/\___| + + ======================================================== + * + */ + + public boolean deserialiseFlavorName(List headerLines) { + for (String line : headerLines) { + Matcher matcher = extract("^Flavor: " + getExtensionName(), line); + + if (matcher.find()) { + return true; + } + } + return false; + } + + public void deserialiseHeader(List headerLines) { + deserialiseMetadata(headerLines); + deserialiseFileCommandNames(headerLines); + } + + public List extractHeader(BigArrayList lines) { + List extracted = new ArrayList<>(); + + long maxExtract = 1000; + + maxExtract = lines.size() < maxExtract ? lines.size() : maxExtract; + + for (long i = 0; i < maxExtract; i++) { + String line = lines.get(i); + extracted.add(line); + + if (line.equals(headerEnd())) + return extracted; + } + throw new PlaybackLoadException("Cannot find the end of the header"); + } + + protected void deserialiseFileCommandNames(List headerLines) { + for (String line : headerLines) { + Matcher matcher = extract("FileCommand-Extensions: ?(.*)", line); + + if (matcher.find()) { + String extensionStrings = matcher.group(1); + String[] extensionNames = extensionStrings.split(", ?"); + + TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.setEnabled(Arrays.asList(extensionNames)); + return; + } + } + throw new PlaybackLoadException("FileCommand-Extensions value was not found in the header"); + } + + protected void deserialiseMetadata(List headerLines) { + List out = new ArrayList<>(); + + String metadataName = null; + LinkedHashMap values = new LinkedHashMap<>(); + + for (String headerLine : headerLines) { + + Matcher nameMatcher = extract("^-+ ([^-]+)", headerLine); // If the line starts with ###, an optional space char after and then capture the name + Matcher valueMatcher = extract("^([^#].*?):\\s*(.+)", headerLine); // If the line doesn't start with a #, then the key of the metadata, then a : then any or no number of whitespace chars, then the value of the metadata + + if (nameMatcher.find()) { + + if (metadataName != null && !metadataName.equals(nameMatcher.group(1))) { // If metadataName is null, then the first section begins + // If metadataName is different than the newMetadataName, + // then a new section begins and we first need to store the old. + out.add(PlaybackMetadata.fromHashMap(metadataName, values)); + values.clear(); + } + metadataName = nameMatcher.group(1).trim(); + continue; + + } else if (metadataName != null && valueMatcher.find()) { + values.put(valueMatcher.group(1).trim(), valueMatcher.group(2).trim()); + } + } + + if (metadataName != null) + out.add(PlaybackMetadata.fromHashMap(metadataName, values)); + + TASmodAPIRegistry.PLAYBACK_METADATA.handleOnLoad(out); + } + + /** + * Deserialises the input part of the TASfile + * + * @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} + */ + public BigArrayList deserialise(BigArrayList lines, long startPos) { + BigArrayList out = new BigArrayList<>(); + for (long i = startPos; i < lines.size(); i++) { + List container = new ArrayList<>(); + // Extract the tick and set the index + i = extractContainer(container, lines, i); + currentLine = i; + currentTick++; + // Extract container + deserialiseContainer(out, container); + } + previousTickContainer = null; + return out; + } + + protected enum ExtractPhases { + /** + * InlineComment phase. + * + *
+		 * ---
+		 * // This is a comment
+		 * // $fileCommand();
+		 * 
+		 * ---
+		 * 
+ * + * Empty lines also count as comments + */ + COMMENTS, + /** + * Tick phase. Start with a number, then a | character + * + *
+		 * ---
+		 * 57|W,LCONTROL;w|;0,887,626|17.85;-202.74799
+		 * ---
+		 * 
+ * + * Only one line should be in this phase + */ + TICK, + /** + * Subtick phase. Start with a tabulator, then a number, then a | character + * + *
+		 * --- 1||RC;0,1580,658|17.85;-202.74799\t\t// This is an endline comment
+		 * 2||;0,1580,658|17.85;-202.74799 --- Can have multiple subticks
+		 */
+		SUBTICK,
+		/**
+		 * We are outside a tick
+		 */
+		NONE
+	}
+
+	/**
+	 * 

+ * 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.
+ *

+ * A container has multiple parts to it, that are split into + * {@link ExtractPhases}
+ * The container starts in {@link ExtractPhases#NONE}. + * + *

+	 * --- {@link ExtractPhases#COMMENTS Comment phase} --- 
+	 * // This is a comment 
+	 * // $fileCommand(); 
+	 * --- {@link ExtractPhases#TICK Tick phase} ---
+	 * 57|W,LCONTROL;w|;0,887,626|17.85;-202.74799 
+	 * --- {@link ExtractPhases#SUBTICK Subtick phase} --- 
+	 * 	1||RC;0,1580,658|17.85;-202.74799	// This is an endline comment 
+	 * 	2||;0,1580,658|17.85;-202.74799
+	 * ---------------------
+	 * 
+ * + *

Logic

+ *
    + *
  1. Phase: None + *
      + *
    1. If a comment is found, set the phase to comment
    2. + *
    3. If a tick is found, set the phase to tick
    4. + *
    5. If a subtick is found, throw an error. Subticks always come after + * ticks
    6. + *
    + *
  2. + *
  3. Phase: Comment + *
      + *
    1. If a tick is found, set the phase to tick
    2. + *
    3. If a subtick is found, throw an error. Subticks always come after + * ticks
    4. + *
    + *
  4. + *
  5. Phase: Tick + *
      + *
    1. If a subtick is found, set the phase to subticks
    2. + *
    3. If a tick is found, end the extraction
    4. + *
    5. If a comment is found, end the extraction
    6. + *
    + *
  6. + *
  7. Phase: Subtick + *
      + *
    1. If a tick is found, end the extraction
    2. + *
    3. If a comment is found, end the extraction
    4. + *
    + *
  8. + *
+ * + * @param extracted The extracted lines, passed in by reference + * @param lines The line list + * @param startPos The start position of this tick + * @return The updated index for the next tick + */ + protected long extractContainer(List extracted, BigArrayList lines, long startPos) { + ExtractPhases phase = ExtractPhases.NONE; + + String commentRegex = "^//"; + String tickRegex = "^\\d+\\|"; + String subtickRegex = "^\t\\d+\\|"; + + long counter = 0L; + for (long i = startPos; i < lines.size(); i++) { + String line = lines.get(i); + + switch (phase) { + case NONE: + if (contains(subtickRegex, line)) { // Subtick + throw new PlaybackLoadException(startPos + counter + 1, currentTick, currentSubtick, "Error while trying to parse the file. This should not be a subtick at this position"); + } + + if (contains(commentRegex, line) || line.isEmpty()) { // Comment + phase = ExtractPhases.COMMENTS; + } else if (contains(tickRegex, line)) { // Tick + phase = ExtractPhases.TICK; + } + + break; + case COMMENTS: + if (contains(subtickRegex, line)) { // Subtick + throw new PlaybackLoadException(startPos + counter + 1, currentTick, currentSubtick, "Error while trying to parse the file. This should not be a subtick at this position"); + } + + if (contains(tickRegex, line)) { // Tick + phase = ExtractPhases.TICK; + } + + break; + case TICK: + if (contains(subtickRegex, line)) { // Subtick + phase = ExtractPhases.SUBTICK; + } + + if (contains(commentRegex, line) || contains(tickRegex, line) || line.isEmpty()) { // Comment + return startPos + counter - 1; + } + + break; + case SUBTICK: + if (contains(commentRegex, line) || contains(tickRegex, line) || line.isEmpty()) { // Comment + return startPos + counter - 1; + } + break; + } + if (phase != ExtractPhases.NONE) { + extracted.add(line); + } + counter++; + } + return startPos + counter - 1; + } + + protected void deserialiseContainer(BigArrayList out, List containerLines) { + + List inlineComments = new ArrayList<>(); + List tickLines = new ArrayList<>(); + List> inlineFileCommands = new ArrayList<>(); + splitContainer(containerLines, inlineComments, tickLines, inlineFileCommands); + + List keyboardStrings = new ArrayList<>(); + List mouseStrings = new ArrayList<>(); + List cameraAngleStrings = new ArrayList<>(); + List endlineComments = new ArrayList<>(); + List> endlineFileCommands = new ArrayList<>(); + + splitInputs(containerLines, keyboardStrings, mouseStrings, cameraAngleStrings, endlineComments, endlineFileCommands); + + pruneListEndNull(endlineComments); + + VirtualKeyboard keyboard = deserialiseKeyboard(keyboardStrings); + VirtualMouse mouse = deserialiseMouse(mouseStrings); + VirtualCameraAngle cameraAngle = deserialiseCameraAngle(cameraAngleStrings); + CommentContainer comments = new CommentContainer(inlineComments, endlineComments); + + TickContainer deserialisedContainer = new TickContainer(keyboard, mouse, cameraAngle, comments); + + TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.handleOnDeserialiseInline(currentTick, deserialisedContainer, inlineFileCommands); + TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.handleOnDeserialiseEndline(currentTick, deserialisedContainer, endlineFileCommands); + + previousTickContainer = deserialisedContainer; + + out.add(deserialisedContainer); + } + + /** + * Splits lines into comments and ticks. + * + * @param lines + */ + protected void splitContainer(List lines, List comments, List tick, List> inlineFileCommands) { + 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); + } else { + tick.add(line); + } + } + } + + protected String deserialiseInlineComment(String comment, List deserialisedFileCommands) { + comment = deserialiseFileCommands(comment, deserialisedFileCommands); + comment = extract("^// ?(.+)", comment, 1); + if (comment != null) { + comment = comment.trim(); + if (comment.isEmpty()) { + comment = null; + } + } + return comment; + } + + protected String deserialiseEndlineComment(String comment, List deserialisedFileCommands) { + return deserialiseInlineComment(comment, deserialisedFileCommands); + } + + protected String deserialiseFileCommands(String comment, List deserialisedFileCommands) { + Matcher matcher = extract("\\$(.+?)\\((.*?)\\);", comment); + while (matcher.find()) { + String name = matcher.group(1); + String[] args = matcher.group(2).split(", ?"); + deserialisedFileCommands.add(new PlaybackFileCommand(name, args)); + comment = matcher.replaceFirst(""); + matcher.reset(comment); + } + + return comment; + } + + 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(); + + int[] keycodes = deserialiseVirtualKey(keys, VirtualKey.ZERO); + out.updateFromState(keycodes, chars); + } + currentSubtick++; + } + return out; + } + + protected VirtualMouse deserialiseMouse(List mouseStrings) { + VirtualMouse out = new VirtualMouse(); + + currentSubtick = 0; + Integer previousCursorX = previousTickContainer == null ? null : previousTickContainer.getMouse().getCursorX(); + Integer previousCursorY = previousTickContainer == null ? null : previousTickContainer.getMouse().getCursorY(); + + for (String line : mouseStrings) { + Matcher matcher = extract("(.*?);(.+)", line); + if (matcher.find()) { + String[] buttons = matcher.group(1).split(","); + String[] functions = matcher.group(2).split(","); + + int[] keycodes = deserialiseVirtualKey(buttons, VirtualKey.MOUSEMOVED); + int scrollwheel; + Integer cursorX; + Integer cursorY; + + if (functions.length == 3) { + scrollwheel = parseInt("scrollwheel", functions[0]); + 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"); + } + + out.updateFromState(keycodes, scrollwheel, cursorX, cursorY); + + previousCursorX = cursorX; + previousCursorY = cursorY; + } + currentSubtick++; + } + return out; + } + + 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("(.+?);(.+)", line); + + if (matcher.find()) { + String cameraPitchString = matcher.group(1); + String cameraYawString = matcher.group(2); + + Float cameraPitch = null; + Float cameraYaw = null; + + if (!"null".equals(cameraPitchString)) + cameraPitch = deserialiseRelativeFloat("camera pitch", cameraPitchString, previousPitch); + + if (!"null".equals(cameraYawString)) + cameraYaw = deserialiseRelativeFloat("camera yaw", cameraYawString, previousYaw); + + out.updateFromState(cameraPitch, cameraYaw); + } + currentSubtick++; + } + return out; + } + + protected int[] deserialiseVirtualKey(String[] keyString, VirtualKey defaultKey) { + int[] out = new int[keyString.length]; + + for (int i = 0; i < keyString.length; i++) { + String key = keyString[i]; + + /* 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; + } + + out[i] = VirtualKey.getKeycode(key); + } + return out; + } + + 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); + } + } + + protected int deserialiseRelativeInt(String name, String intstring, Integer previous) { + int out = 0; + if (intstring.startsWith("~")) { + intstring = intstring.replace("~", ""); + int relative = parseInt(name, intstring); + if (previous != null) { + out = previous + relative; + } else { + throw new PlaybackLoadException(currentLine, currentTick, currentSubtick, "Can't process relative value ~%s in %s. Previous value for comparing is not available", intstring, name); + } + } else { + out = parseInt(name, intstring); + } + return out; + } + + 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); + } + } + + protected Float deserialiseRelativeFloat(String name, String floatstring, Float previous) { + if (floatstring == null) { + return null; + } + + float out = 0; + if (floatstring.startsWith("~")) { + floatstring = floatstring.replace("~", ""); + float relative = parseFloat(name, floatstring); + if (previous != null) { + out = previous + relative; + } else { + throw new PlaybackLoadException(currentLine, currentTick, currentSubtick, "Can't process relative value ~%s in %s. Previous value for comparing is not available", floatstring, name); + } + } else { + out = parseFloat(name, floatstring); + } + return out; + } + + protected void splitInputs(List lines, List serialisedKeyboard, List serialisedMouse, List serialisedCameraAngle, List commentsAtEnd, List> endlineFileCommands) { + + String previousCamera = null; + if(previousTickContainer != null) { + previousCamera = previousTickContainer.getCameraAngle().toString2(); + } + + for (String line : lines) { + Matcher tickMatcher = extract("^\\t?\\d+\\|(.*?)\\|(.*?)\\|(\\S*)\\s?", 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); + } + + List deserialisedFileCommands = new ArrayList<>(); + + String endlineComment = line.substring(tickMatcher.group(0).length()); + commentsAtEnd.add(deserialiseEndlineComment(endlineComment, deserialisedFileCommands)); + + if (deserialisedFileCommands.isEmpty()) + deserialisedFileCommands = null; + + endlineFileCommands.add(deserialisedFileCommands); + } + } + } + + protected Matcher extract(String regex, String haystack) { + Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE); + Matcher matcher = pattern.matcher(haystack); + + return matcher; + } + + protected String extract(String regex, String haystack, int group) { + Matcher matcher = extract(regex, haystack); + if (matcher.find()) { + return matcher.group(group); + } + return null; + } + + protected boolean contains(String regex, String haystack) { + return extract(regex, haystack).find(); + } + + protected boolean isNumeric(String string) { + return Pattern.matches("-?\\d+", string); + } + + protected boolean isFloat(String string) { + return Pattern.matches("-?\\d+(?:\\.\\d+)?", string); + } + + /** + * @return {@link #currentTick} + */ + public long getCurrentTick() { + return currentTick; + } + + /** + * @return {@link #currentSubtick} + */ + public Integer getCurrentSubtick() { + return currentSubtick; + } + + public static String createCenteredHeading(String text, char spacingChar, int headingWidth) { + + if (text == null || text.isEmpty()) { + return createPaddedString(spacingChar, headingWidth); + } + + text = " " + text + " "; + + int spacingWidth = headingWidth - text.length(); + + String paddingPre = createPaddedString(spacingChar, spacingWidth % 2 == 1 ? spacingWidth / 2 + 1 : spacingWidth / 2); + String paddingSuf = createPaddedString(spacingChar, spacingWidth / 2); + + return String.format("%s%s%s", paddingPre, text, paddingSuf); + } + + private static String createPaddedString(char spacingChar, int width) { + char[] spacingLine = new char[width]; + for (int i = 0; i < spacingLine.length; i++) { + spacingLine[i] = spacingChar; + } + 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 + * + * @param The element of the list + * @param list The list to prune + */ + protected void pruneListEndNull(List list) { + List copy = new ArrayList<>(list); + for (int i = copy.size() - 1; i >= 0; i--) { + T element = copy.get(i); + if (element != null) + return; + list.remove(list.size() - 1); + } + } + + /** + * Empties the list starting from the back if the values are empty + * + * @param The element of the list + * @param list The list to prune + */ + protected void pruneListEndEmpty(List list) { + List copy = new ArrayList<>(list); + for (int i = copy.size() - 1; i >= 0; i--) { + String element = copy.get(i); + if (!element.isEmpty()) + return; + list.remove(list.size() - 1); + } + } + + /** + * Empties the list starting from the back if the values are empty + * + * @param The element of the list + * @param list The list to prune + */ + protected > void pruneListEndEmptySubtickable(List list) { + List copy = new ArrayList<>(list); + for (int i = copy.size() - 1; i >= 0; i--) { + T element = copy.get(i); + if (!element.isEmpty()) + return; + list.remove(list.size() - 1); + } + } + + + @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/SerialiserFlavorRegistry.java b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorRegistry.java new file mode 100644 index 00000000..f9ad7344 --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorRegistry.java @@ -0,0 +1,34 @@ +package com.minecrafttas.tasmod.playback.tasfile.flavor; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Set; + +import com.minecrafttas.mctcommon.registry.AbstractRegistry; + +/** + * Registry for registering custom PlaybackSerialisers.
+ * This is useful if you want to create your own syntax "flavor" for TASmod to read.
+ * + * @author Scribble + */ +public class SerialiserFlavorRegistry extends AbstractRegistry{ + + public SerialiserFlavorRegistry() { + super("FLAVOR_REGISTRY", new LinkedHashMap<>()); + } + + public Set getFlavorNames(){ + return REGISTRY.keySet(); + } + + public SerialiserFlavorBase getFlavor(String name) { + SerialiserFlavorBase out = REGISTRY.get(name); + return out == null ? null : out.clone(); + } + + public List getFlavors() { + return new ArrayList<>(REGISTRY.values()); + } +} 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/integrated/Beta1Flavor.java new file mode 100644 index 00000000..39f5164c --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/integrated/Beta1Flavor.java @@ -0,0 +1,16 @@ +package com.minecrafttas.tasmod.playback.tasfile.flavor.integrated; + +import com.minecrafttas.tasmod.playback.tasfile.flavor.SerialiserFlavorBase; + +public class Beta1Flavor extends SerialiserFlavorBase { + + @Override + public String getExtensionName() { + return "beta1"; + } + + @Override + public SerialiserFlavorBase clone() { + return new Beta1Flavor(); + } +} diff --git a/src/main/java/com/minecrafttas/tasmod/registries/TASmodAPIRegistry.java b/src/main/java/com/minecrafttas/tasmod/registries/TASmodAPIRegistry.java new file mode 100644 index 00000000..86e68530 --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/registries/TASmodAPIRegistry.java @@ -0,0 +1,39 @@ +package com.minecrafttas.tasmod.registries; + +import com.minecrafttas.tasmod.playback.filecommands.PlaybackFileCommandsRegistry; +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; + +public class TASmodAPIRegistry { + /** + * Registry for registering custom metadata that is stored in the TASfile.
+ *
+ * The default metadata includes general information such as author name, + * savestate/rerecord count and category.
+ *
+ * Any custom class has to implement PlaybackMetadataExtension + */ + public static final PlaybackMetadataRegistry PLAYBACK_METADATA = new PlaybackMetadataRegistry(); + + /** + * Registry for registering custom behavior for each tick during recording and playback.
+ *
+ * File commands give the opportunity to run commands on each recorded tick and each played back tick.
+ * File commands also have access to the TASfile so that data can be stored and read in/from the TASfile. + * + */ + public static final PlaybackFileCommandsRegistry PLAYBACK_FILE_COMMAND = new PlaybackFileCommandsRegistry(); + + /** + * Registry for registering custom serialiser flavors that dictate the syntax of the inputs stored in the TASfile.
+ *
+ * Either create a new flavor by extending {@link SerialiserFlavorBase}
+ * or extend an existing flavor (like {@link Beta1Flavor}) and overwrite parts of the methods.
+ *
+ * The resulting flavor can be registered here and can be found as a saving option with /saveTAS + */ + public static final SerialiserFlavorRegistry SERIALISER_FLAVOR = new SerialiserFlavorRegistry(); + +} diff --git a/src/main/java/com/minecrafttas/tasmod/registries/TASmodConfig.java b/src/main/java/com/minecrafttas/tasmod/registries/TASmodConfig.java new file mode 100644 index 00000000..166582c3 --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/registries/TASmodConfig.java @@ -0,0 +1,33 @@ +package com.minecrafttas.tasmod.registries; + +import com.minecrafttas.mctcommon.Configuration.ConfigOptions; + +public enum TASmodConfig implements ConfigOptions { + FileToOpen("fileToOpen", ""), + ServerConnection("serverConnection", ""), + EnabledFileCommands("enabledFileCommands", "tasmod_desyncMonitor@v1, tasmod_label@v1, tasmod_options@v1"); + + private String configKey; + private String defaultValue; + + private TASmodConfig(String configKey, String defaultValue) { + this.configKey = configKey; + this.defaultValue = defaultValue; + } + + @Override + public String getDefaultValue() { + return defaultValue; + } + + @Override + public String getConfigKey() { + return configKey; + } + + @Override + public String getExtensionName() { + return "TASmodConfig"; + } + +} diff --git a/src/main/java/com/minecrafttas/tasmod/registries/TASmodKeybinds.java b/src/main/java/com/minecrafttas/tasmod/registries/TASmodKeybinds.java new file mode 100644 index 00000000..5abafea7 --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/registries/TASmodKeybinds.java @@ -0,0 +1,78 @@ +package com.minecrafttas.tasmod.registries; + +import org.lwjgl.input.Keyboard; + +import com.minecrafttas.mctcommon.KeybindManager.IsKeyDownFunc; +import com.minecrafttas.mctcommon.KeybindManager.Keybind; +import com.minecrafttas.tasmod.TASmodClient; +import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; +import com.minecrafttas.tasmod.virtual.VirtualKeybindings; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.settings.KeyBinding; +import net.minecraft.util.text.ChatType; +import net.minecraft.util.text.TextComponentString; +import net.minecraft.util.text.TextFormatting; + +public enum TASmodKeybinds { + TICKRATE_0("Tickrate 0 Key", "TASmod", Keyboard.KEY_F8, () -> TASmodClient.tickratechanger.togglePause(), VirtualKeybindings::isKeyDown), + ADVANCE("Advance Tick", "TASmod", Keyboard.KEY_F9, () -> TASmodClient.tickratechanger.advanceTick(), VirtualKeybindings::isKeyDown), + STOP("Recording/Playback Stop", "TASmod", Keyboard.KEY_F10, () -> TASmodClient.controller.setTASState(TASstate.NONE), VirtualKeybindings::isKeyDown), + SAVESTATE("Create Savestate", "TASmod", Keyboard.KEY_J, () -> { + Minecraft.getMinecraft().ingameGUI.addChatMessage(ChatType.CHAT, new TextComponentString("Savestates might not work correctly at the moment... rewriting a lot of core features, which might break this...")); + try { + TASmodClient.client.send(new TASmodBufferBuilder(TASmodPackets.SAVESTATE_SAVE).writeInt(-1)); + } catch (Exception e) { + e.printStackTrace(); + } + }), + LOADSTATE("Load Latest Savestate", "TASmod", Keyboard.KEY_K, () -> { + Minecraft.getMinecraft().ingameGUI.addChatMessage(ChatType.CHAT, new TextComponentString(TextFormatting.RED + "Savestates might not work correctly at the moment... rewriting a lot of core features, which might break this...")); + try { + TASmodClient.client.send(new TASmodBufferBuilder(TASmodPackets.SAVESTATE_LOAD).writeInt(-1)); + } catch (Exception e) { + e.printStackTrace(); + } + }), + INFO_GUI("Open InfoGui Editor", "TASmod", Keyboard.KEY_F6, () -> Minecraft.getMinecraft().displayGuiScreen(TASmodClient.hud)), + TEST1("Various Testing", "TASmod", Keyboard.KEY_F12, () -> { + TASmodClient.controller.setTASState(TASstate.RECORDING); + }, VirtualKeybindings::isKeyDown), + TEST2("Various Testing2", "TASmod", Keyboard.KEY_F7, () -> { + // try { + // TASmodClient.client = new Client("localhost", TASmod.networkingport - 1, TASmodPackets.values(), mc.getSession().getProfile().getName(), true); + // } catch (Exception e) { + // e.printStackTrace(); + // } + TASmodClient.controller.setTASState(TASstate.PLAYBACK); + }, VirtualKeybindings::isKeyDown); + + private Keybind keybind; + + private TASmodKeybinds(String name, String category, int defaultKey, Runnable onKeyDown, IsKeyDownFunc func) { + this.keybind = new Keybind(name, category, defaultKey, onKeyDown, func); + } + + private TASmodKeybinds(String name, String category, int defaultKey, Runnable onKeyDown) { + this(name, category, defaultKey, onKeyDown, null); + } + + public static Keybind[] valuesKeybind() { + TASmodKeybinds[] tasmodkeybinds = values(); + Keybind[] keybinds = new Keybind[tasmodkeybinds.length]; + for (int i = 0; i < tasmodkeybinds.length; i++) { + keybinds[i] = tasmodkeybinds[i].keybind; + } + return keybinds; + } + + public static KeyBinding[] valuesVanillaKeybind() { + TASmodKeybinds[] tasmodkeybinds = values(); + KeyBinding[] keybinds = new KeyBinding[tasmodkeybinds.length]; + for (int i = 0; i < tasmodkeybinds.length; i++) { + keybinds[i] = tasmodkeybinds[i].keybind.vanillaKeyBinding; + } + return keybinds; + } +} diff --git a/src/main/java/com/minecrafttas/tasmod/networking/TASmodPackets.java b/src/main/java/com/minecrafttas/tasmod/registries/TASmodPackets.java similarity index 79% rename from src/main/java/com/minecrafttas/tasmod/networking/TASmodPackets.java rename to src/main/java/com/minecrafttas/tasmod/registries/TASmodPackets.java index ac01c975..b8c996b1 100644 --- a/src/main/java/com/minecrafttas/tasmod/networking/TASmodPackets.java +++ b/src/main/java/com/minecrafttas/tasmod/registries/TASmodPackets.java @@ -1,12 +1,13 @@ -package com.minecrafttas.tasmod.networking; +package com.minecrafttas.tasmod.registries; -import com.minecrafttas.mctcommon.server.CompactPacketHandler; -import com.minecrafttas.mctcommon.server.Client.Side; -import com.minecrafttas.mctcommon.server.interfaces.PacketID; +import com.minecrafttas.mctcommon.networking.Client.Side; +import com.minecrafttas.mctcommon.networking.CompactPacketHandler; +import com.minecrafttas.mctcommon.networking.interfaces.PacketID; import com.minecrafttas.tasmod.commands.CommandFolder; import com.minecrafttas.tasmod.playback.PlaybackControllerClient; import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; -import com.minecrafttas.tasmod.playback.tasfile.PlaybackSerialiser; +import com.minecrafttas.tasmod.playback.filecommands.PlaybackFileCommand.PlaybackFileCommandExtension; +import com.minecrafttas.tasmod.playback.tasfile.flavor.SerialiserFlavorBase; import com.minecrafttas.tasmod.savestates.SavestateHandlerServer.PlayerHandler.MotionData; import com.minecrafttas.tasmod.tickratechanger.TickrateChangerServer.TickratePauseState; @@ -113,7 +114,7 @@ public enum TASmodPackets implements PacketID { */ PLAYBACK_RESTARTANDPLAY, /** - *

Notifies the client to store the current inputs to a file in {@link PlaybackControllerClient}. This is done using {@link PlaybackSerialiser} + *

Notifies the client to store the current inputs to a file in {@link PlaybackControllerClient}. This is done using {@link PlaybackSerialiser2} *

SIDE: Both
* ARGS:
* Client->Server None
@@ -121,7 +122,7 @@ public enum TASmodPackets implements PacketID { */ PLAYBACK_SAVE, /** - *

Notifies the client to load the inputs from a file in {@link PlaybackControllerClient}. This is done using {@link PlaybackSerialiser} + *

Notifies the client to load the inputs from a file in {@link PlaybackControllerClient}. This is done using {@link PlaybackSerialiser2} *

SIDE: Both
* ARGS:
* Client->Server None
@@ -157,6 +158,14 @@ public enum TASmodPackets implements PacketID { * boolean verbose If a chat message should be printed */ PLAYBACK_STATE, + /** + *

Enables/Disables {@link PlaybackFileCommandExtension PlaybackFileCommandExtensions} + *

SIDE: Client
+ * ARGS:
+ * String name Name of the {@link PlaybackFileCommandExtension} + * boolean enable Whether the extensions should be enabled or disabled + */ + PLAYBACK_FILECOMMAND_ENABLE, /** *

Opens a TASmod related folder on the file system *

The action describes which folder to open: @@ -180,6 +189,30 @@ public enum TASmodPackets implements PacketID { break; } }), + /** + *

Requests the list of TASfiles in the folder from the client for use in tab completions + *

SIDE: Both
+ * ARGS:
+ * Server->Client None
+ * Client->Server String The string of TASfilenames seperated with | + */ + COMMAND_TASFILELIST, + /** + *

Requests the list of {@link SerialiserFlavorBase SerialiserFlavors} from the client for use in tab completions + *

SIDE: Both
+ * ARGS:
+ * Server->Client None
+ * Client->Server String The string of flavors seperated with | + */ + COMMAND_FLAVORLIST, + /** + *

Requests the list of {@link PlaybackFileCommandExtension PlaybackFileCommandExtensions} from the client for use in tab completions + *

SIDE: Bith
+ * ARGS:
+ * Server->Client None
+ * Client->Server String The string of file command names, seperated with | + */ + COMMAND_FILECOMMANDLIST, /** *

Sets the KillTheRNG seed *

SIDE: Both
@@ -240,4 +273,9 @@ public String getName() { public boolean shouldTrace() { return shouldTrace; } + + @Override + public String getExtensionName() { + return "TASmodPackets"; + } } diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerClient.java b/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerClient.java index d5d82e37..58e573f0 100644 --- a/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerClient.java +++ b/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerClient.java @@ -6,17 +6,20 @@ import java.io.IOException; import java.nio.ByteBuffer; -import com.minecrafttas.mctcommon.server.Client.Side; -import com.minecrafttas.mctcommon.server.exception.PacketNotImplementedException; -import com.minecrafttas.mctcommon.server.exception.WrongSideException; -import com.minecrafttas.mctcommon.server.interfaces.ClientPacketHandler; -import com.minecrafttas.mctcommon.server.interfaces.PacketID; +import com.dselent.bigarraylist.BigArrayList; +import com.minecrafttas.mctcommon.networking.Client.Side; +import com.minecrafttas.mctcommon.networking.exception.PacketNotImplementedException; +import com.minecrafttas.mctcommon.networking.exception.WrongSideException; +import com.minecrafttas.mctcommon.networking.interfaces.ClientPacketHandler; +import com.minecrafttas.mctcommon.networking.interfaces.PacketID; import com.minecrafttas.tasmod.TASmodClient; import com.minecrafttas.tasmod.mixin.savestates.MixinChunkProviderClient; import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; -import com.minecrafttas.tasmod.networking.TASmodPackets; import com.minecrafttas.tasmod.playback.PlaybackControllerClient; import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TickContainer; +import com.minecrafttas.tasmod.playback.tasfile.PlaybackSerialiser; +import com.minecrafttas.tasmod.registries.TASmodPackets; import com.minecrafttas.tasmod.savestates.SavestateHandlerServer.PlayerHandler.MotionData; import com.minecrafttas.tasmod.savestates.exceptions.SavestateException; import com.minecrafttas.tasmod.savestates.gui.GuiSavestateSavingScreen; @@ -79,7 +82,6 @@ public static void keepPlayerInLoadedEntityList(net.minecraft.entity.player.Enti * This state is fixed, once the player moves into a different chunk, since the * new chunk adds the player to it's list.
*
- * * TLDR:
* Adds the player to the chunk so the player is shown in third person
*
@@ -121,21 +123,22 @@ public static void savestate(String nameOfSavestate) throws SavestateException, PlaybackControllerClient container = TASmodClient.controller; if (container.isRecording()) { - TASmodClient.serialiser.saveToFileV1(targetfile, container); // If the container is recording, store it entirely + PlaybackSerialiser.saveToFile(targetfile, container, ""); // If the container is recording, store it entirely } else if (container.isPlayingback()) { - TASmodClient.serialiser.saveToFileV1Until(targetfile, container, container.index()); // If the container is playing, store it until the current index + PlaybackSerialiser.saveToFile(targetfile, container, "", container.index()); // If the container is playing, store it until the current index } } /** - * Replaces the current recording with the recording from the savestate. Gets - * triggered when a savestate is loaded on the server
- * Side: Client + *

Loads a copy of the TASfile from the file system and applies it depending on the {@link PlaybackControllerClient#state TASstate}. + * + *

Savestates can be loaded while the state is {@link TASstate#RECORDING recording}, {@link TASstate#PLAYBACK playing back} or {@link TASstate#PAUSED paused},
+ * in that case however, the {@link PlaybackControllerClient#stateAfterPause TASstate after pause} will be used. * * @param nameOfSavestate coming from the server * @throws IOException */ - public static void loadstate(String nameOfSavestate) throws IOException { + public static void loadstate(String nameOfSavestate) throws Exception { LOGGER.debug(LoggerMarkers.Savestate, "Loading client savestate {}", nameOfSavestate); if (nameOfSavestate.isEmpty()) { LOGGER.error(LoggerMarkers.Savestate, "No recording savestate loaded since the name of savestate is empty"); @@ -144,21 +147,109 @@ public static void loadstate(String nameOfSavestate) throws IOException { savestateDirectory.mkdir(); + PlaybackControllerClient controller = TASmodClient.controller; + + TASstate state = controller.getState(); + + if (state == TASstate.NONE) { + return; + } + + if(state == TASstate.PAUSED) { + state = controller.getStateAfterPause(); + } + File targetfile = new File(savestateDirectory, nameOfSavestate + ".mctas"); - PlaybackControllerClient container = TASmodClient.controller; - if (!container.isNothingPlaying()) { // If the file exists and the container is recording or playing, load the - // clientSavestate - if (targetfile.exists()) { -// TASmodClient.virtual.loadClientSavestate(TASmodClient.serialiser.fromEntireFileV1(targetfile)); TODO Move to PlaybackController - } else { - TASmodClient.controller.setTASStateClient(TASstate.NONE, false); - Minecraft.getMinecraft().player.sendMessage(new TextComponentString(ChatFormatting.YELLOW + "Inputs could not be loaded for this savestate,")); - Minecraft.getMinecraft().player.sendMessage(new TextComponentString(ChatFormatting.YELLOW + "since the file doesn't exist. Stopping!")); - LOGGER.warn(LoggerMarkers.Savestate, "Inputs could not be loaded for this savestate, since the file doesn't exist."); + BigArrayList savestateContainerList; + + if (targetfile.exists()) { + savestateContainerList = PlaybackSerialiser.loadFromFile(targetfile); + } else { + controller.setTASStateClient(TASstate.NONE, false); + Minecraft.getMinecraft().player.sendMessage(new TextComponentString(ChatFormatting.YELLOW + "Inputs could not be loaded for this savestate,")); + Minecraft.getMinecraft().player.sendMessage(new TextComponentString(ChatFormatting.YELLOW + "since the file doesn't exist. Stopping!")); + LOGGER.warn(LoggerMarkers.Savestate, "Inputs could not be loaded for this savestate, since the file doesn't exist."); + return; + } + + /* + * Imagine a recording that is 20 tick long with VV showing the current index of the controller: + * VV + * 0 20 + * <====================> + * + * Now we load a savestate with only 10 ticks: + * + * 0 10 + * <==========> + * + * We expect to resume the recording at the 10th tick. + * Therefore when loading a client savestate during a recording we set the index to size-1 and preload the inputs at the same index. + * VV + * 0 10 + * <==========> + * + * */ + if (state == TASstate.RECORDING) { + long index = savestateContainerList.size()-1; + + preload(savestateContainerList, index); + controller.setInputs(savestateContainerList, index); + + /* + * When loading a savestate during a playback 2 different scenarios can happen. + * */ + } else if (state == TASstate.PLAYBACK) { + + /* + * Scenario 1: + * The loadstated file is SMALLER than the total inputs in the controller: + * + * The recording is 20 ticks long, with VV being the index where the playback is currently at. + * VV + * 0 13 20 + * <====================> + * + * And our loadstated file being only 10 ticks long: + * + * 0 10 + * <==========> + * + * We expect to start at tick 10 WITHOUT clearing the controller. + * If we were to replace the controller, everything above tick 10 would be lost. + * So we only set the index to 10, preload and preload the inputs. + * + * VV + * 0 10 20 + * <====================> + * */ + if (controller.size() >= savestateContainerList.size()) { + long index = savestateContainerList.size(); + + preload(controller.getInputs(), index); + controller.setIndex(index); + } + /* + * Scenario 2: + * The loadstated file is LARGER than the controller, + * which may happen when loading a more recent savestate after loading an old one + * + * In that case we just apply the playback just like in the recording + * */ + else { + long index = savestateContainerList.size()-1; + + preload(savestateContainerList, index); + controller.setInputs(savestateContainerList, index); } } } + + private static void preload(BigArrayList containerList, long index) { + TickContainer containerToPreload = containerList.get(index); + TASmodClient.virtual.preloadInput(containerToPreload.getKeyboard(), containerToPreload.getMouse(), containerToPreload.getCameraAngle()); + } public static void loadPlayer(NBTTagCompound compound) { LOGGER.trace(LoggerMarkers.Savestate, "Loading client player from NBT"); @@ -167,8 +258,8 @@ public static void loadPlayer(NBTTagCompound compound) { player.readFromNBT(compound); NBTTagCompound motion = compound.getCompoundTag("clientMotion"); - - if(motion.hasNoTags()) { + + if (motion.hasNoTags()) { LOGGER.warn(LoggerMarkers.Savestate, "Could not load the motion from the savestate. Savestate seems to be created manually or by a different mod"); } else { LOGGER.trace(LoggerMarkers.Savestate, "Loading client motion from NBT"); @@ -178,14 +269,14 @@ public static void loadPlayer(NBTTagCompound compound) { player.motionX = x; player.motionY = y; player.motionZ = z; - + float rx = motion.getFloat("RelativeX"); float ry = motion.getFloat("RelativeY"); float rz = motion.getFloat("RelativeZ"); player.moveForward = rx; player.moveVertical = ry; player.moveStrafing = rz; - + boolean sprinting = motion.getBoolean("Sprinting"); float jumpVector = motion.getFloat("JumpFactor"); player.setSprinting(sprinting); @@ -199,8 +290,8 @@ public static void loadPlayer(NBTTagCompound compound) { mc.playerController.setGameType(type); // #?? Player rotation does not change when loading a savestate -// CameraInterpolationEvents.rotationPitch = player.rotationPitch; -// CameraInterpolationEvents.rotationYaw = player.rotationYaw + 180f; + // CameraInterpolationEvents.rotationPitch = player.rotationPitch; + // CameraInterpolationEvents.rotationYaw = player.rotationYaw + 180f; SavestateHandlerClient.keepPlayerInLoadedEntityList(player); } @@ -226,14 +317,7 @@ public static void unloadAllClientChunks() { @Override public PacketID[] getAcceptedPacketIDs() { - return new TASmodPackets[] { - TASmodPackets.SAVESTATE_SAVE, - TASmodPackets.SAVESTATE_LOAD, - TASmodPackets.SAVESTATE_PLAYER, - TASmodPackets.SAVESTATE_REQUEST_MOTION, - TASmodPackets.SAVESTATE_SCREEN, - TASmodPackets.SAVESTATE_UNLOAD_CHUNKS - }; + return new TASmodPackets[] { TASmodPackets.SAVESTATE_SAVE, TASmodPackets.SAVESTATE_LOAD, TASmodPackets.SAVESTATE_PLAYER, TASmodPackets.SAVESTATE_REQUEST_MOTION, TASmodPackets.SAVESTATE_SCREEN, TASmodPackets.SAVESTATE_UNLOAD_CHUNKS }; } @Override @@ -287,20 +371,7 @@ public void onClientPacket(PacketID id, ByteBuffer buf, String username) throws if (!(Minecraft.getMinecraft().currentScreen instanceof GuiSavestateSavingScreen)) { Minecraft.getMinecraft().displayGuiScreen(new GuiSavestateSavingScreen()); } - TASmodClient.client.send( - new TASmodBufferBuilder(TASmodPackets.SAVESTATE_REQUEST_MOTION) - .writeMotionData( - new MotionData( - player.motionX, - player.motionY, - player.motionZ, - player.moveForward, - player.moveVertical, - player.moveStrafing, - player.isSprinting(), - player.jumpMovementFactor) - ) - ); + TASmodClient.client.send(new TASmodBufferBuilder(TASmodPackets.SAVESTATE_REQUEST_MOTION).writeMotionData(new MotionData(player.motionX, player.motionY, player.motionZ, player.moveForward, player.moveVertical, player.moveStrafing, player.isSprinting(), player.jumpMovementFactor))); } break; case SAVESTATE_SCREEN: diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerServer.java b/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerServer.java index 23fd1d68..336ea88b 100644 --- a/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerServer.java +++ b/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerServer.java @@ -15,25 +15,23 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import com.minecrafttas.mctcommon.events.EventListenerRegistry; import org.apache.commons.io.FileUtils; import org.apache.logging.log4j.Logger; import com.google.common.collect.Maps; -import com.minecrafttas.mctcommon.server.Client.Side; -import com.minecrafttas.mctcommon.server.exception.PacketNotImplementedException; -import com.minecrafttas.mctcommon.server.exception.WrongSideException; -import com.minecrafttas.mctcommon.server.interfaces.PacketID; -import com.minecrafttas.mctcommon.server.interfaces.ServerPacketHandler; +import com.minecrafttas.mctcommon.events.EventListenerRegistry; +import com.minecrafttas.mctcommon.networking.Client.Side; +import com.minecrafttas.mctcommon.networking.exception.PacketNotImplementedException; +import com.minecrafttas.mctcommon.networking.exception.WrongSideException; +import com.minecrafttas.mctcommon.networking.interfaces.PacketID; +import com.minecrafttas.mctcommon.networking.interfaces.ServerPacketHandler; import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.events.EventServer.EventCompleteLoadstate; -import com.minecrafttas.tasmod.events.EventServer.EventLoadstate; -import com.minecrafttas.tasmod.events.EventServer.EventSavestate; +import com.minecrafttas.tasmod.events.EventSavestate; import com.minecrafttas.tasmod.mixin.savestates.AccessorAnvilChunkLoader; import com.minecrafttas.tasmod.mixin.savestates.AccessorChunkLoader; import com.minecrafttas.tasmod.mixin.savestates.MixinChunkProviderServer; import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; -import com.minecrafttas.tasmod.networking.TASmodPackets; +import com.minecrafttas.tasmod.registries.TASmodPackets; import com.minecrafttas.tasmod.savestates.SavestateHandlerServer.PlayerHandler.MotionData; import com.minecrafttas.tasmod.savestates.exceptions.LoadstateException; import com.minecrafttas.tasmod.savestates.exceptions.SavestateDeleteException; @@ -197,7 +195,7 @@ public void saveState(int savestateIndex, boolean tickrate0, boolean changeIndex File currentfolder = new File(savestateDirectory, ".." + File.separator + worldname); File targetfolder = getSavestateFile(indexToSave); - EventListenerRegistry.fireEvent(EventSavestate.class, indexToSave, targetfolder, currentfolder); + EventListenerRegistry.fireEvent(EventSavestate.EventServerSavestate.class, indexToSave, targetfolder, currentfolder); if (targetfolder.exists()) { logger.warn(LoggerMarkers.Savestate, "WARNING! Overwriting the savestate with the index {}", indexToSave); @@ -340,7 +338,7 @@ public void loadState(int savestateIndex, boolean tickrate0, boolean changeIndex File currentfolder = new File(savestateDirectory, ".." + File.separator + worldname); File targetfolder = getSavestateFile(indexToLoad); - EventListenerRegistry.fireEvent(EventLoadstate.class, indexToLoad, targetfolder, currentfolder); + EventListenerRegistry.fireEvent(EventSavestate.EventServerLoadstate.class, indexToLoad, targetfolder, currentfolder); /* * Prevents loading an InputSavestate when loading index 0 (Index 0 is the @@ -419,7 +417,7 @@ public void loadState(int savestateIndex, boolean tickrate0, boolean changeIndex TASmod.tickSchedulerServer.add(()->{ - EventListenerRegistry.fireEvent(EventCompleteLoadstate.class); + EventListenerRegistry.fireEvent(EventSavestate.EventServerCompleteLoadstate.class); onLoadstateComplete(); // Unlock savestating diff --git a/src/main/java/com/minecrafttas/tasmod/tickratechanger/TickrateChangerClient.java b/src/main/java/com/minecrafttas/tasmod/tickratechanger/TickrateChangerClient.java index 9363b006..31cd97f8 100644 --- a/src/main/java/com/minecrafttas/tasmod/tickratechanger/TickrateChangerClient.java +++ b/src/main/java/com/minecrafttas/tasmod/tickratechanger/TickrateChangerClient.java @@ -5,15 +5,15 @@ import java.nio.ByteBuffer; import com.minecrafttas.mctcommon.events.EventListenerRegistry; -import com.minecrafttas.mctcommon.server.Client.Side; -import com.minecrafttas.mctcommon.server.exception.PacketNotImplementedException; -import com.minecrafttas.mctcommon.server.exception.WrongSideException; -import com.minecrafttas.mctcommon.server.interfaces.ClientPacketHandler; -import com.minecrafttas.mctcommon.server.interfaces.PacketID; +import com.minecrafttas.mctcommon.networking.Client.Side; +import com.minecrafttas.mctcommon.networking.exception.PacketNotImplementedException; +import com.minecrafttas.mctcommon.networking.exception.WrongSideException; +import com.minecrafttas.mctcommon.networking.interfaces.ClientPacketHandler; +import com.minecrafttas.mctcommon.networking.interfaces.PacketID; import com.minecrafttas.tasmod.TASmodClient; -import com.minecrafttas.tasmod.events.EventClient.EventClientTickrateChange; +import com.minecrafttas.tasmod.events.EventTickratechanger; import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; -import com.minecrafttas.tasmod.networking.TASmodPackets; +import com.minecrafttas.tasmod.registries.TASmodPackets; import com.minecrafttas.tasmod.tickratechanger.TickrateChangerServer.TickratePauseState; import com.minecrafttas.tasmod.util.LoggerMarkers; @@ -89,7 +89,7 @@ public void changeClientTickrate(float tickrate, boolean log) { mc.timer.tickLength = Float.MAX_VALUE; } ticksPerSecond = tickrate; - EventListenerRegistry.fireEvent(EventClientTickrateChange.class, tickrate); + EventListenerRegistry.fireEvent(EventTickratechanger.EventClientTickrateChange.class, tickrate); if (log) log("Setting the client tickrate to " + ticksPerSecond); } diff --git a/src/main/java/com/minecrafttas/tasmod/tickratechanger/TickrateChangerServer.java b/src/main/java/com/minecrafttas/tasmod/tickratechanger/TickrateChangerServer.java index c05ae665..f5a01bf3 100644 --- a/src/main/java/com/minecrafttas/tasmod/tickratechanger/TickrateChangerServer.java +++ b/src/main/java/com/minecrafttas/tasmod/tickratechanger/TickrateChangerServer.java @@ -2,20 +2,20 @@ import java.nio.ByteBuffer; -import com.minecrafttas.mctcommon.events.EventListenerRegistry; import org.apache.logging.log4j.Logger; +import com.minecrafttas.mctcommon.events.EventListenerRegistry; import com.minecrafttas.mctcommon.events.EventServer.EventPlayerJoinedServerSide; import com.minecrafttas.mctcommon.events.EventServer.EventServerStop; -import com.minecrafttas.mctcommon.server.Client.Side; -import com.minecrafttas.mctcommon.server.exception.PacketNotImplementedException; -import com.minecrafttas.mctcommon.server.exception.WrongSideException; -import com.minecrafttas.mctcommon.server.interfaces.PacketID; -import com.minecrafttas.mctcommon.server.interfaces.ServerPacketHandler; +import com.minecrafttas.mctcommon.networking.Client.Side; +import com.minecrafttas.mctcommon.networking.exception.PacketNotImplementedException; +import com.minecrafttas.mctcommon.networking.exception.WrongSideException; +import com.minecrafttas.mctcommon.networking.interfaces.PacketID; +import com.minecrafttas.mctcommon.networking.interfaces.ServerPacketHandler; import com.minecrafttas.tasmod.TASmod; -import com.minecrafttas.tasmod.events.EventServer.EventServerTickrateChange; +import com.minecrafttas.tasmod.events.EventTickratechanger; import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; -import com.minecrafttas.tasmod.networking.TASmodPackets; +import com.minecrafttas.tasmod.registries.TASmodPackets; import com.minecrafttas.tasmod.util.LoggerMarkers; import net.minecraft.entity.player.EntityPlayerMP; @@ -128,7 +128,7 @@ public void changeServerTickrate(float tickrate, boolean log) { } } ticksPerSecond = tickrate; - EventListenerRegistry.fireEvent(EventServerTickrateChange.class, tickrate); + EventListenerRegistry.fireEvent(EventTickratechanger.EventServerTickrateChange.class, tickrate); if (log) { log("Setting the server tickrate to " + ticksPerSecond); } diff --git a/src/main/java/com/minecrafttas/tasmod/ticksync/TickSyncClient.java b/src/main/java/com/minecrafttas/tasmod/ticksync/TickSyncClient.java index cb89d4bc..ffe0f005 100644 --- a/src/main/java/com/minecrafttas/tasmod/ticksync/TickSyncClient.java +++ b/src/main/java/com/minecrafttas/tasmod/ticksync/TickSyncClient.java @@ -5,12 +5,12 @@ import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicBoolean; -import com.minecrafttas.mctcommon.server.interfaces.ClientPacketHandler; -import com.minecrafttas.mctcommon.server.interfaces.PacketID; +import com.minecrafttas.mctcommon.networking.interfaces.ClientPacketHandler; +import com.minecrafttas.mctcommon.networking.interfaces.PacketID; import com.minecrafttas.tasmod.TASmodClient; import com.minecrafttas.tasmod.events.EventClient.EventClientTickPost; import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; -import com.minecrafttas.tasmod.networking.TASmodPackets; +import com.minecrafttas.tasmod.registries.TASmodPackets; import net.minecraft.client.Minecraft; diff --git a/src/main/java/com/minecrafttas/tasmod/ticksync/TickSyncServer.java b/src/main/java/com/minecrafttas/tasmod/ticksync/TickSyncServer.java index e870c16d..18d3ec4d 100644 --- a/src/main/java/com/minecrafttas/tasmod/ticksync/TickSyncServer.java +++ b/src/main/java/com/minecrafttas/tasmod/ticksync/TickSyncServer.java @@ -9,12 +9,12 @@ import java.util.List; import com.minecrafttas.mctcommon.events.EventServer.EventClientCompleteAuthentication; -import com.minecrafttas.mctcommon.server.interfaces.PacketID; -import com.minecrafttas.mctcommon.server.interfaces.ServerPacketHandler; +import com.minecrafttas.mctcommon.networking.interfaces.PacketID; +import com.minecrafttas.mctcommon.networking.interfaces.ServerPacketHandler; import com.minecrafttas.tasmod.TASmod; import com.minecrafttas.tasmod.events.EventServer.EventServerTickPost; import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; -import com.minecrafttas.tasmod.networking.TASmodPackets; +import com.minecrafttas.tasmod.registries.TASmodPackets; import net.minecraft.server.MinecraftServer; diff --git a/src/main/java/com/minecrafttas/tasmod/util/FileThread.java b/src/main/java/com/minecrafttas/tasmod/util/FileThread.java index 759c5af1..1ac9c31c 100644 --- a/src/main/java/com/minecrafttas/tasmod/util/FileThread.java +++ b/src/main/java/com/minecrafttas/tasmod/util/FileThread.java @@ -27,7 +27,7 @@ public FileThread(File fileLocation, boolean append) throws FileNotFoundExceptio public void addLine(String line) { synchronized (output) { - output.add(line); + output.add(line + "\n"); } } diff --git a/src/main/java/com/minecrafttas/tasmod/util/LoggerMarkers.java b/src/main/java/com/minecrafttas/tasmod/util/LoggerMarkers.java index de235e6a..61fcfad0 100644 --- a/src/main/java/com/minecrafttas/tasmod/util/LoggerMarkers.java +++ b/src/main/java/com/minecrafttas/tasmod/util/LoggerMarkers.java @@ -25,18 +25,18 @@ * */ public class LoggerMarkers { - + public static final Marker Event = MarkerManager.getMarker("Event"); - + public static final Marker Savestate = MarkerManager.getMarker("Savestate"); - + public static final Marker Networking = MarkerManager.getMarker("Networking"); - + public static final Marker Tickrate = MarkerManager.getMarker("Tickrate"); public static final Marker Playback = MarkerManager.getMarker("Playback"); - + public static final Marker Keyboard = MarkerManager.getMarker("Keyboard"); - + public static final Marker Mouse = MarkerManager.getMarker("Mouse"); } diff --git a/src/main/java/com/minecrafttas/tasmod/monitoring/Monitor.java b/src/main/java/com/minecrafttas/tasmod/util/Monitor.java similarity index 90% rename from src/main/java/com/minecrafttas/tasmod/monitoring/Monitor.java rename to src/main/java/com/minecrafttas/tasmod/util/Monitor.java index 7e342eb4..d53e5ac1 100644 --- a/src/main/java/com/minecrafttas/tasmod/monitoring/Monitor.java +++ b/src/main/java/com/minecrafttas/tasmod/util/Monitor.java @@ -1,12 +1,11 @@ -package com.minecrafttas.tasmod.monitoring; +package com.minecrafttas.tasmod.util; import java.lang.reflect.Field; public class Monitor { - - + private static long cooldown; - + public static void printFields(Object monitoredObject) { if (monitoredObject == null) { @@ -37,17 +36,17 @@ public static void printFields(Object monitoredObject) { out = out.concat("------------------------"); System.out.println(out); } - + public static boolean shouldPrint(long cooldownTime) { - if(cooldown<=0) { - cooldown=cooldownTime; + if (cooldown <= 0) { + cooldown = cooldownTime; return true; } else { cooldown--; return false; } } - + public static Object accessField(Object objectToAccess, String fieldname) { Field field = null; try { @@ -56,13 +55,13 @@ public static Object accessField(Object objectToAccess, String fieldname) { e.printStackTrace(); } field.setAccessible(true); - Object out=null; + Object out = null; try { - out=field.get(objectToAccess); + out = field.get(objectToAccess); } catch (IllegalArgumentException | IllegalAccessException e) { e.printStackTrace(); } return out; } - + } diff --git a/src/main/java/com/minecrafttas/tasmod/util/PointerNormalizer.java b/src/main/java/com/minecrafttas/tasmod/util/PointerNormalizer.java index 12a6d2a2..5d6f5063 100644 --- a/src/main/java/com/minecrafttas/tasmod/util/PointerNormalizer.java +++ b/src/main/java/com/minecrafttas/tasmod/util/PointerNormalizer.java @@ -38,7 +38,7 @@ public static int getNormalizedY(int pointerY) { if (mc.currentScreen instanceof GuiContainer) { out = (int) (pointerY - (scaled.getScaledHeight() / 2D)); - } else if (mc.currentScreen instanceof GuiWorldSelection|| mc.currentScreen instanceof GuiMultiplayer) { + } else if (mc.currentScreen instanceof GuiWorldSelection || mc.currentScreen instanceof GuiMultiplayer) { // TODO Figure out what to do here } else { out = (int) (pointerY - (scaled.getScaledHeight() / 4 + 72 + -16)); @@ -113,33 +113,33 @@ private static int greatestCommonDivisor(int a, int b) { * hours. */ -// private double getNormalizedXOld(double pointerX) { -// Minecraft mc = Minecraft.getMinecraft(); -// ScaledResolution scaled = new ScaledResolution(mc); -// return (double) pointerX / (double) mc.displayWidth / (4D / (double) scaled.getScaleFactor()); -// } -// -// public static double getNormalizedYOld(int pointerY) { -// Minecraft mc = Minecraft.getMinecraft(); -// ScaledResolution scaled = new ScaledResolution(mc); -// double out = (double) pointerY / (double) mc.displayHeight / (4D / (double) scaled.getScaleFactor()); -// return out; -// } -// -// public static int getCoordsXOld(double normalizedX) { -// Minecraft mc = Minecraft.getMinecraft(); -// ScaledResolution scaled = new ScaledResolution(mc); -// double guiScaled = normalizedX * (double) mc.displayWidth * (4D / (double) scaled.getScaleFactor()); -// int out = (int) Math.round(guiScaled); -// return out; -// } -// -// public static int getCoordsYOld(double normalizedY) { -// Minecraft mc = Minecraft.getMinecraft(); -// ScaledResolution scaled = new ScaledResolution(mc); -// double guiScaled = normalizedY * (double) mc.displayHeight * (4D / (double) scaled.getScaleFactor()); -// int out = (int) Math.round(guiScaled); -// return out; -// } + // private double getNormalizedXOld(double pointerX) { + // Minecraft mc = Minecraft.getMinecraft(); + // ScaledResolution scaled = new ScaledResolution(mc); + // return (double) pointerX / (double) mc.displayWidth / (4D / (double) scaled.getScaleFactor()); + // } + // + // public static double getNormalizedYOld(int pointerY) { + // Minecraft mc = Minecraft.getMinecraft(); + // ScaledResolution scaled = new ScaledResolution(mc); + // double out = (double) pointerY / (double) mc.displayHeight / (4D / (double) scaled.getScaleFactor()); + // return out; + // } + // + // public static int getCoordsXOld(double normalizedX) { + // Minecraft mc = Minecraft.getMinecraft(); + // ScaledResolution scaled = new ScaledResolution(mc); + // double guiScaled = normalizedX * (double) mc.displayWidth * (4D / (double) scaled.getScaleFactor()); + // int out = (int) Math.round(guiScaled); + // return out; + // } + // + // public static int getCoordsYOld(double normalizedY) { + // Minecraft mc = Minecraft.getMinecraft(); + // ScaledResolution scaled = new ScaledResolution(mc); + // double guiScaled = normalizedY * (double) mc.displayHeight * (4D / (double) scaled.getScaleFactor()); + // int out = (int) Math.round(guiScaled); + // return out; + // } } diff --git a/src/main/java/com/minecrafttas/tasmod/util/Scheduler.java b/src/main/java/com/minecrafttas/tasmod/util/Scheduler.java index 9e462b3a..d1997d0f 100644 --- a/src/main/java/com/minecrafttas/tasmod/util/Scheduler.java +++ b/src/main/java/com/minecrafttas/tasmod/util/Scheduler.java @@ -9,20 +9,20 @@ * @author Scribble */ public class Scheduler { - + Queue queue = new ConcurrentLinkedQueue<>(); - + public void runAllTasks() { Task task; - while((task = queue.poll()) != null) { + while ((task = queue.poll()) != null) { task.runTask(); } } - + public void add(Task task) { queue.add(task); } - + @FunctionalInterface public interface Task { public void runTask(); diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/Subtickable.java b/src/main/java/com/minecrafttas/tasmod/virtual/Subtickable.java index 56a0873e..0f463740 100644 --- a/src/main/java/com/minecrafttas/tasmod/virtual/Subtickable.java +++ b/src/main/java/com/minecrafttas/tasmod/virtual/Subtickable.java @@ -98,4 +98,11 @@ protected boolean isIgnoreFirstUpdate() { protected void resetFirstUpdate() { ignoreFirstUpdate = true; } + + public boolean isEmpty() { + if (isParent()) { + return subtickList.isEmpty(); + } + return true; + } } diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualCameraAngle.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualCameraAngle.java index 607e46ef..be4ee087 100644 --- a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualCameraAngle.java +++ b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualCameraAngle.java @@ -31,7 +31,7 @@ public class VirtualCameraAngle extends Subtickable implemen public VirtualCameraAngle() { this(null, null, new ArrayList<>(), true); } - + /** * Creates a subtick camera angle with {@link Subtickable#subtickList} uninitialized * @param pitch {@link #pitch} @@ -40,7 +40,7 @@ public VirtualCameraAngle() { public VirtualCameraAngle(Float pitch, Float yaw) { this(pitch, yaw, null); } - + /** * Creates a parent camera angle * @param pitch {@link #pitch} @@ -50,7 +50,7 @@ public VirtualCameraAngle(Float pitch, Float yaw) { public VirtualCameraAngle(Float pitch, Float yaw, boolean ignoreFirstUpdate) { this(pitch, yaw, new ArrayList<>(), ignoreFirstUpdate); } - + /** * Creates a camera angle with existing values * @param pitch {@link #pitch} @@ -60,7 +60,7 @@ public VirtualCameraAngle(Float pitch, Float yaw, boolean ignoreFirstUpdate) { public VirtualCameraAngle(Float pitch, Float yaw, List subtickList) { this(pitch, yaw, subtickList, false); } - + /** * Creates a camera angle with initialized values * @param pitch {@link VirtualCameraAngle#pitch} @@ -79,27 +79,42 @@ public VirtualCameraAngle(Float pitch, Float yaw, List subti * @param pitchDelta The difference between absolute coordinates of the pitch, is added to {@link VirtualCameraAngle#pitch} * @param yawDelta The difference between absolute coordinates of the yaw, is added to {@link VirtualCameraAngle#yaw} */ - public void update(float pitchDelta, float yawDelta) { - update(pitchDelta, yawDelta, true); + public void updateFromEvent(float pitchDelta, float yawDelta) { + updateFromEvent(pitchDelta, yawDelta, true); } - + /** * Updates the camera angle. * @param pitchDelta The difference between absolute coordinates of the pitch, is added to {@link VirtualCameraAngle#pitch} * @param yawDelta The difference between absolute coordinates of the yaw, is added to {@link VirtualCameraAngle#yaw} * @param updateSubtick If the previous camera should be added to {@link Subtickable#subtickList} */ - public void update(float pitchDelta, float yawDelta, boolean updateSubtick) { - if(pitch==null || yaw == null) { + public void updateFromEvent(float pitchDelta, float yawDelta, boolean updateSubtick) { + if (pitch == null || yaw == null) { return; } - if(isParent() && !ignoreFirstUpdate() && updateSubtick) { + createSubtick(updateSubtick); + this.pitch = MathHelper.clamp(this.pitch + pitchDelta, -90F, 90F); + this.yaw += yawDelta; + } + + public void updateFromState(Float pitch, Float yaw) { + if (this.pitch != null && this.yaw != null) { + createSubtick(true); + } + if (pitch != null) { + pitch = MathHelper.clamp(pitch, -90F, 90F); + } + this.pitch = pitch; + this.yaw = yaw; + } + + public void createSubtick(boolean updateSubtick) { + if (isParent() && !ignoreFirstUpdate() && updateSubtick) { addSubtick(shallowClone()); } - this.pitch = MathHelper.clamp(this.pitch + pitchDelta, -90.0F, 90.0F); - this.yaw += yawDelta; } - + /** * Setting the absolute camera coordinates directly * @param pitch {@link #pitch} @@ -109,7 +124,7 @@ public void set(float pitch, float yaw) { this.pitch = pitch; this.yaw = yaw; } - + /** * A list of all camera states in this VirtualCameraAngle. * It consists of: {@link Subtickable#subtickList} + this @@ -121,13 +136,13 @@ public void getStates(List reference) { reference.add(this); } } - - /** - * Moves the data from another camera angle into this camera without creating a new object. - * @param camera The camera to copy from - */ + + /** + * Moves the data from another camera angle into this camera without creating a new object. + * @param camera The camera to copy from + */ public void moveFrom(VirtualCameraAngle camera) { - if(camera == null) + if (camera == null) return; this.pitch = camera.pitch; this.yaw = camera.yaw; @@ -135,20 +150,20 @@ public void moveFrom(VirtualCameraAngle camera) { this.subtickList.addAll(camera.subtickList); camera.subtickList.clear(); } - - /** - * Copies the data from another camera angle into this camera without creating a new object. - * @param camera The camera to copy from - */ + + /** + * Copies the data from another camera angle into this camera without creating a new object. + * @param camera The camera to copy from + */ public void deepCopyFrom(VirtualCameraAngle camera) { - if(camera == null || !camera.isParent()) + if (camera == null || !camera.isParent()) return; this.pitch = camera.pitch; this.yaw = camera.yaw; this.subtickList.clear(); this.subtickList.addAll(camera.subtickList); } - + /** * Sets {@link #pitch} and {@link #yaw} to null */ @@ -158,14 +173,14 @@ public void clear() { this.yaw = null; super.clear(); } - + /** * Creates a clone of this object as a subtick */ public VirtualCameraAngle shallowClone() { return new VirtualCameraAngle(pitch, yaw); } - + @Override public VirtualCameraAngle clone() { return new VirtualCameraAngle(pitch, yaw, new ArrayList<>(subtickList), isIgnoreFirstUpdate()); @@ -175,24 +190,35 @@ public VirtualCameraAngle clone() { public boolean equals(Object obj) { if (obj instanceof VirtualCameraAngle) { VirtualCameraAngle angle = (VirtualCameraAngle) obj; - return (pitch != null && pitch.equals(angle.pitch)) && (yaw != null && yaw.equals(angle.yaw)); + if (pitch == null && angle.pitch != null) { + return false; + } + if (yaw == null && angle.yaw != null) { + return false; + } + if (pitch != null && !pitch.equals(angle.pitch)) { + return false; + } + if (yaw != null && !yaw.equals(angle.yaw)) + return false; + return true; } return super.equals(obj); } @Override public String toString() { - if(isParent()) { + if (isParent()) { return getAll().stream().map(VirtualCameraAngle::toString2).collect(Collectors.joining("\n")); } else { return toString2(); } } - - private String toString2() { + + public String toString2() { return String.format("%s;%s", pitch, yaw); } - + /** * @return {@link #pitch} */ @@ -206,4 +232,9 @@ public Float getPitch() { public Float getYaw() { return yaw; } + + @Override + public boolean isEmpty() { + return super.isEmpty() && (pitch == null || pitch == 0) && (yaw == null || yaw == 0); + } } diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java index ea385bf6..7ff5ccad 100644 --- a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java +++ b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java @@ -12,9 +12,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import com.minecrafttas.mctcommon.events.EventListenerRegistry; -import com.minecrafttas.tasmod.events.EventClient.EventVirtualCameraAngleTick; -import com.minecrafttas.tasmod.events.EventClient.EventVirtualKeyboardTick; -import com.minecrafttas.tasmod.events.EventClient.EventVirtualMouseTick; +import com.minecrafttas.tasmod.events.EventVirtualInput; import com.minecrafttas.tasmod.mixin.playbackhooks.MixinEntityRenderer; import com.minecrafttas.tasmod.mixin.playbackhooks.MixinMinecraft; import com.minecrafttas.tasmod.util.Ducks; @@ -23,6 +21,7 @@ import com.minecrafttas.tasmod.virtual.event.VirtualKeyboardEvent; import com.minecrafttas.tasmod.virtual.event.VirtualMouseEvent; +import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiScreen; import net.minecraft.util.math.MathHelper; @@ -87,14 +86,14 @@ public void update(GuiScreen currentScreen) { } while (Mouse.next()) { if (currentScreen == null) { - MOUSE.updateNextMouse(Mouse.getEventButton()-100, Mouse.getEventButtonState(), Mouse.getEventDWheel(), 0, 0); + MOUSE.updateNextMouse(Mouse.getEventButton() - 100, Mouse.getEventButtonState(), Mouse.getEventDWheel(), 0, 0); } else { Ducks.GuiScreenDuck screen = (Ducks.GuiScreenDuck) currentScreen; int eventX = screen.unscaleX(Mouse.getEventX()); int eventY = screen.unscaleY(Mouse.getEventY()); eventX = PointerNormalizer.getNormalizedX(eventX); eventY = PointerNormalizer.getNormalizedY(eventY); - MOUSE.updateNextMouse(Mouse.getEventButton()-100, Mouse.getEventButtonState(), Mouse.getEventDWheel(), eventX, eventY); + MOUSE.updateNextMouse(Mouse.getEventButton() - 100, Mouse.getEventButtonState(), Mouse.getEventDWheel(), eventX, eventY); } } } @@ -108,13 +107,13 @@ public void update(GuiScreen currentScreen) { * @return If the key is down either on mouse or keyboard */ public boolean isKeyDown(int keycode) { - if(keycode >= 0) { + if (keycode >= 0) { return KEYBOARD.isKeyDown(keycode); } else { return MOUSE.isKeyDown(keycode); } } - + /** * If the keyboard or mouse key is will be down in the next tick. * If keycode >= 0 then {@link VirtualKeyboardInput#willKeyBeDown(int)} will be called,
@@ -124,13 +123,13 @@ public boolean isKeyDown(int keycode) { * @return If the key will be down either on mouse or keyboard */ public boolean willKeyBeDown(int keycode) { - if(keycode >= 0) { + if (keycode >= 0) { return KEYBOARD.willKeyBeDown(keycode); } else { return MOUSE.willKeyBeDown(keycode); } } - + /** * Unpresses all keys in {@link VirtualKeyboardInput#nextKeyboard} and {@link VirtualMouseInput#nextMouse} */ @@ -139,7 +138,16 @@ public void clear() { MOUSE.nextMouse.clear(); CAMERA_ANGLE.nextCameraAngle.clear(); } - + + public void preloadInput(VirtualKeyboard keyboardToPreload, VirtualMouse mouseToPreload, VirtualCameraAngle angleToPreload) { + KEYBOARD.nextKeyboard.deepCopyFrom(keyboardToPreload); + MOUSE.nextMouse.deepCopyFrom(mouseToPreload); + CAMERA_ANGLE.nextCameraAngle.deepCopyFrom(angleToPreload); + Minecraft.getMinecraft().runTickKeyboard(); // Letting mouse and keyboard tick once to load inputs into the "currentKeyboard" + // + Minecraft.getMinecraft().runTickMouse(); + } + /** * Subclass of {@link VirtualInput} handling keyboard logic.
*
@@ -225,7 +233,7 @@ public VirtualKeyboardInput(VirtualKeyboard preloadedKeyboard) { public void updateNextKeyboard(int keycode, boolean keystate, char character) { updateNextKeyboard(keycode, keystate, character, false); } - + /** * Updates the next keyboard * @@ -236,10 +244,10 @@ public void updateNextKeyboard(int keycode, boolean keystate, char character) { * @param repeatEventsEnabled If repeat events are enabled */ public void updateNextKeyboard(int keycode, boolean keystate, char character, boolean repeatEventsEnabled) { - LOGGER.debug(LoggerMarkers.Keyboard, "Update: {}, {}, {}, {}", keycode, keystate, character); // Activate with -Dtasmod.marker.keyboard=ACCEPT in VM arguments (and -Dtasmod.log.level=debug) - nextKeyboard.update(keycode, keystate, character, repeatEventsEnabled); + LOGGER.debug(LoggerMarkers.Keyboard, "Update: {}, {}, {}, {}", keycode, keystate, character); // Activate with -Dtasmod.marker.keyboard=ACCEPT in VM arguments (and -Dtasmod.log.level=debug) + nextKeyboard.updateFromEvent(keycode, keystate, character, repeatEventsEnabled); } - + /** * Runs when the next keyboard tick is about to occur.
* Used to load {@link #nextKeyboard} into {@link #currentKeyboard}, creating @@ -248,7 +256,7 @@ public void updateNextKeyboard(int keycode, boolean keystate, char character, bo * @see MixinMinecraft#playback_injectRunTickKeyboard(org.spongepowered.asm.mixin.injection.callback.CallbackInfo) */ public void nextKeyboardTick() { - nextKeyboard.deepCopyFrom((VirtualKeyboard) EventListenerRegistry.fireEvent(EventVirtualKeyboardTick.class, nextKeyboard)); + nextKeyboard.deepCopyFrom((VirtualKeyboard) EventListenerRegistry.fireEvent(EventVirtualInput.EventVirtualKeyboardTick.class, nextKeyboard)); currentKeyboard.getVirtualEvents(nextKeyboard, keyboardEventQueue); currentKeyboard.moveFrom(nextKeyboard); } @@ -284,7 +292,7 @@ public boolean getEventKeyboardState() { public char getEventKeyboardCharacter() { return currentKeyboardEvent.getCharacter(); } - + /** * If the key is currently down and recognised by Minecraft * @param keycode The keycode of the key in question @@ -293,7 +301,7 @@ public char getEventKeyboardCharacter() { public boolean isKeyDown(int keycode) { return currentKeyboard.isKeyDown(keycode); } - + /** * If the key will be down and recognised in the next tick by Minecraft.
* This is equal to checking if a key on the physical keyboard is pressed @@ -396,8 +404,8 @@ public VirtualMouseInput(VirtualMouse preloadedMouse) { * @param cursorY The y coordinate of the cursot of this event */ public void updateNextMouse(int keycode, boolean keystate, int scrollwheel, int cursorX, int cursorY) { - LOGGER.debug(LoggerMarkers.Mouse,"Update: {} ({}), {}, {}, {}, {}", keycode, VirtualKey.getName(keycode), keystate, scrollwheel, cursorX, cursorY); // Activate with -Dtasmod.marker.mouse=ACCEPT in VM arguments (and -Dtasmod.log.level=debug) - nextMouse.update(keycode, keystate, scrollwheel, cursorX, cursorY); + LOGGER.debug(LoggerMarkers.Mouse, "Update: {} ({}), {}, {}, {}, {}", keycode, VirtualKey.getName(keycode), keystate, scrollwheel, cursorX, cursorY); // Activate with -Dtasmod.marker.mouse=ACCEPT in VM arguments (and -Dtasmod.log.level=debug) + nextMouse.updateFromEvent(keycode, keystate, scrollwheel, cursorX, cursorY); } /** @@ -408,7 +416,7 @@ public void updateNextMouse(int keycode, boolean keystate, int scrollwheel, int * @see MixinMinecraft#playback_injectRunTickMouse(org.spongepowered.asm.mixin.injection.callback.CallbackInfo) */ public void nextMouseTick() { - nextMouse.deepCopyFrom((VirtualMouse) EventListenerRegistry.fireEvent(EventVirtualMouseTick.class, nextMouse)); + nextMouse.deepCopyFrom((VirtualMouse) EventListenerRegistry.fireEvent(EventVirtualInput.EventVirtualMouseTick.class, nextMouse)); currentMouse.getVirtualEvents(nextMouse, mouseEventQueue); currentMouse.moveFrom(nextMouse); } @@ -458,21 +466,21 @@ public int getEventCursorX() { public int getNormalizedCursorX() { return currentMouseEvent.getCursorX(); } - + /** * @return The scaled y coordinate of the cursor of {@link #currentMouseEvent} */ public int getEventCursorY() { return PointerNormalizer.reapplyScalingY(getNormalizedCursorY()); } - + /** * @return The y coordinate of the cursor of {@link #currentMouseEvent} */ public int getNormalizedCursorY() { return currentMouseEvent.getCursorY(); } - + /** * If the key is currently down and recognised by Minecraft * @param keycode The keycode of the key in question @@ -481,7 +489,7 @@ public int getNormalizedCursorY() { public boolean isKeyDown(int keycode) { return currentMouse.isKeyDown(keycode); } - + /** * If the key will be down and recognised in the next tick by Minecraft.
* This is equal to checking if a key on the physical mouse is pressed @@ -491,7 +499,7 @@ public boolean isKeyDown(int keycode) { public boolean willKeyBeDown(int keycode) { return nextMouse.isKeyDown(keycode); } - + } /** @@ -585,20 +593,20 @@ public VirtualCameraAngleInput(VirtualCameraAngle preloadedCamera) { public void updateNextCameraAngle(float pitchDelta, float yawDelta) { updateNextCameraAngle(pitchDelta, yawDelta, true); } - + /** * Update the camera angle.
*
* Runs every frame * * @see com.minecrafttas.tasmod.mixin.playbackhooks.MixinEntityRenderer#runUpdate(float); - * @param pitchDelta Relative rotationPitch delta from LWJGLs mouse delta. - * @param yawDelta Relative rotationYaw delta from LWJGLs mouse delta. + * @param pitchDelta Relative rotationPitch delta from LWJGLs mouse delta. + * @param yawDelta Relative rotationYaw delta from LWJGLs mouse delta. * @param updateSubtick Whether to add the previous camera angle to the {@link Subtickable#subtickList} */ public void updateNextCameraAngle(float pitchDelta, float yawDelta, boolean updateSubtick) { -// LOGGER.debug("Pitch: {}, Yaw: {}", pitch, yaw); - nextCameraAngle.update(pitchDelta, yawDelta, updateSubtick); + // LOGGER.debug("Pitch: {}, Yaw: {}", pitch, yaw); + nextCameraAngle.updateFromEvent(pitchDelta, yawDelta, updateSubtick); } /** @@ -608,7 +616,7 @@ public void updateNextCameraAngle(float pitchDelta, float yawDelta, boolean upda * @see MixinEntityRenderer#runUpdate(float) */ public void nextCameraTick() { - nextCameraAngle.deepCopyFrom((VirtualCameraAngle) EventListenerRegistry.fireEvent(EventVirtualCameraAngleTick.class, nextCameraAngle)); + nextCameraAngle.deepCopyFrom((VirtualCameraAngle) EventListenerRegistry.fireEvent(EventVirtualInput.EventVirtualCameraAngleTick.class, nextCameraAngle)); cameraAngleInterpolationStates.clear(); nextCameraAngle.getStates(cameraAngleInterpolationStates); currentCameraAngle.moveFrom(nextCameraAngle); @@ -638,7 +646,7 @@ public void setCamera(Float pitch, Float yaw) { public Float getCurrentPitch() { return currentCameraAngle.getPitch(); } - + /** * @return The absolute yaw coordinate of the player. May be null when it's initialized */ @@ -656,7 +664,7 @@ public Float getCurrentYaw() { * @return A triple of pitch, yaw and roll, as left, middle and right respectively */ public Triple getInterpolatedState(float partialTick, float pitch, float yaw, boolean enable) { - + float interpolatedPitch = nextCameraAngle.getPitch() == null ? pitch : nextCameraAngle.getPitch(); float interpolatedYaw = nextCameraAngle.getYaw() == null ? yaw : nextCameraAngle.getYaw() + 180; diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKey.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKey.java index 98e2d273..53414215 100644 --- a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKey.java +++ b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKey.java @@ -6,189 +6,194 @@ * @author Scribble */ public enum VirtualKey { - // Keyboard - ZERO(0), - ESC(1), - KEY_1(2), - KEY_2(3), - KEY_3(4), - KEY_4(5), - KEY_5(6), - KEY_6(7), - KEY_7(8), - KEY_8(9), - KEY_9(10), - KEY_0(11), - MINUS(12), - EQUALS(13), - BACK(14), - TAB(15), - Q(16), - W(17), - E(18), - R(19), - T(20), - Y(21), - U(22), - I(23), - O(24), - P(25), - LBRACKET(26), - RBRACKET(27), - RETURN(28), - LCONTROL(29), - A(30), - S(31), - D(32), - F(33), - G(34), - H(35), - J(36), - K(37), - L(38), - SEMICOLON(39), - APOSTROPHE(40), - GRAVE(41), - LSHIFT(42), - BACKSLASH(43), - Z(44), - X(45), - C(46), - V(47), - B(48), - N(49), - M(50), - COMMA(51), - PERIOD(52), - SLASH(53), - RSHIFT(54), - MULTIPLY(55), - ALT(56), - SPACE(57), - CAPSLOCK(58), - F1(59), - F2(60), - F3(61), - F4(62), - F5(63), - F6(64), - F7(65), - F8(66), - F9(67), - F10(68), - NUMLOCK(69), - SCROLL(70), - NUMPAD7(71), - NUMPAD8(72), - NUMPAD9(73), - SUBTRACT(74), - NUMPAD4(75), - NUMPAD5(76), - NUMPAD6(77), - ADD(78), - NUMPAD1(79), - NUMPAD2(80), - NUMPAD3(81), - NUMPAD0(82), - DECIMAL(83), - F11(87), - F12(88), - F13(100), - F14(101), - F15(102), - F16(103), - F17(104), - F18(105), - KANA(112), - F19(113), - CONVERT(121), - NOCONVERT(123), - YEN(125), - NUMPADEQUALS(141), - CIRCUMFLEX(144), - AT(145), - COLON(146), - UNDERLINE(147), - KANJI(148), - STOP(149), - NUMPADENTER(156), - RCONTROL(157), - NUMPADCOMMA(179), - DIVIDE(181), - PRINT(183), - ALT_GR(184), - PAUSE(197), - HOME(199), - UP(200), - PRIOR(201), - LEFT(203), - RIGHT(205), - END(207), - DOWN(208), - NEXT(209), - INSERT(210), - DELETE(211), - WIN(219), - APPS(221), + // Keyboard + ZERO(0), + ESC(1), + KEY_1(2), + KEY_2(3), + KEY_3(4), + KEY_4(5), + KEY_5(6), + KEY_6(7), + KEY_7(8), + KEY_8(9), + KEY_9(10), + KEY_0(11), + MINUS(12), + EQUALS(13), + BACK(14), + TAB(15), + Q(16), + W(17), + E(18), + R(19), + T(20), + Y(21), + U(22), + I(23), + O(24), + P(25), + LBRACKET(26), + RBRACKET(27), + RETURN(28), + LCONTROL(29), + A(30), + S(31), + D(32), + F(33), + G(34), + H(35), + J(36), + K(37), + L(38), + SEMICOLON(39), + APOSTROPHE(40), + GRAVE(41), + LSHIFT(42), + BACKSLASH(43), + Z(44), + X(45), + C(46), + V(47), + B(48), + N(49), + M(50), + COMMA(51), + PERIOD(52), + SLASH(53), + RSHIFT(54), + MULTIPLY(55), + ALT(56), + SPACE(57), + CAPSLOCK(58), + F1(59), + F2(60), + F3(61), + F4(62), + F5(63), + F6(64), + F7(65), + F8(66), + F9(67), + F10(68), + NUMLOCK(69), + SCROLL(70), + NUMPAD7(71), + NUMPAD8(72), + NUMPAD9(73), + SUBTRACT(74), + NUMPAD4(75), + NUMPAD5(76), + NUMPAD6(77), + ADD(78), + NUMPAD1(79), + NUMPAD2(80), + NUMPAD3(81), + NUMPAD0(82), + DECIMAL(83), + F11(87), + F12(88), + F13(100), + F14(101), + F15(102), + F16(103), + F17(104), + F18(105), + KANA(112), + F19(113), + CONVERT(121), + NOCONVERT(123), + YEN(125), + NUMPADEQUALS(141), + CIRCUMFLEX(144), + AT(145), + COLON(146), + UNDERLINE(147), + KANJI(148), + STOP(149), + NUMPADENTER(156), + RCONTROL(157), + NUMPADCOMMA(179), + DIVIDE(181), + PRINT(183), + ALT_GR(184), + PAUSE(197), + HOME(199), + UP(200), + PRIOR(201), + LEFT(203), + RIGHT(205), + END(207), + DOWN(208), + NEXT(209), + INSERT(210), + DELETE(211), + WIN(219), + APPS(221), - // Mouse - MOUSEMOVED(-101), - LC(-100), - RC(-99), - MC(-98), - MBUTTON4(-97), - MBUTTON5(-96), - MBUTTON6(-95), - MBUTTON7(-94), - MBUTTON8(-93), - MBUTTON9(-92), - MBUTTON10(-91), - MBUTTON11(-90), - MBUTTON12(-89), - MBUTTON13(-88), - MBUTTON14(-87), - MBUTTON15(-86), - MBUTTON16(-85); + // Mouse + MOUSEMOVED(-101), + LC(-100), + RC(-99), + MC(-98), + MBUTTON4(-97), + MBUTTON5(-96), + MBUTTON6(-95), + MBUTTON7(-94), + MBUTTON8(-93), + MBUTTON9(-92), + MBUTTON10(-91), + MBUTTON11(-90), + MBUTTON12(-89), + MBUTTON13(-88), + MBUTTON14(-87), + MBUTTON15(-86), + MBUTTON16(-85); - private final int keycode; + private final int keycode; - private VirtualKey(int keycode) { - this.keycode = keycode; - } + private VirtualKey(int keycode) { + this.keycode = keycode; + } - public int getKeycode() { - return keycode; - } + public int getKeycode() { + return keycode; + } - public static Integer getKeycode(String keyname) { - VirtualKey key = get(keyname); - if (key != null) - return key.getKeycode(); - return null; - } + public static Integer getKeycode(String keyname) { + VirtualKey key = get(keyname); + if (key != null) + return key.getKeycode(); + return null; + } - public static String getName(int keycode) { - VirtualKey key = get(keycode); - if (key != null) - return key.name(); - return Integer.toString(keycode); - } + public static String getName(int keycode) { + VirtualKey key = get(keycode); + if (key != null) + return key.name(); + return Integer.toString(keycode); + } - public static VirtualKey get(int keycode) { - for (VirtualKey key : values()) { - if (key.getKeycode() == keycode) { - return key; - } - } - return null; - } + public static VirtualKey get(int keycode) { + for (VirtualKey key : values()) { + if (key.getKeycode() == keycode) { + return key; + } + } + return null; + } + + public static VirtualKey get(String keyname) { + if (keyname.isEmpty()) { + return null; + } + + for (VirtualKey key : values()) { + if (key.name().equalsIgnoreCase(keyname)) { + return key; + } + } + return null; + } - public static VirtualKey get(String keyname) { - for (VirtualKey key : values()) { - if (key.name().equalsIgnoreCase(keyname)) { - return key; - } - } - return null; - } } diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKeybindings.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKeybindings.java index ca930fe8..7e65f680 100644 --- a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKeybindings.java +++ b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKeybindings.java @@ -38,7 +38,7 @@ public class VirtualKeybindings { /** * The standard cooldown for a keybinding in milliseconds */ - private static final long cooldown = 50*5; + private static final long cooldown = 50 * 5; /** * Stores the start time of a keybinding, used for cooldown calculation */ @@ -52,7 +52,6 @@ public class VirtualKeybindings { */ public static boolean focused = false; - /** * Checks whether the keycode is pressed, regardless of any gui screens * @@ -65,10 +64,10 @@ public static boolean isKeyDown(KeyBinding keybind) { boolean down = false; - if(mc.currentScreen instanceof GuiControls) { + if (mc.currentScreen instanceof GuiControls) { return false; } - + if (isKeyCodeAlwaysBlocked(keycode)) { down = keycode >= 0 ? Keyboard.isKeyDown(keycode) : Mouse.isButtonDown(keycode + 100); } else { diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKeyboard.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKeyboard.java index 123bc8d4..dbfd6da0 100644 --- a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKeyboard.java +++ b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualKeyboard.java @@ -10,10 +10,11 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.stream.Collectors; -import com.minecrafttas.tasmod.virtual.event.VirtualKeyboardEvent; import org.apache.commons.lang3.StringUtils; import com.google.common.collect.ImmutableList; +import com.minecrafttas.tasmod.playback.tasfile.flavor.SerialiserFlavorBase; +import com.minecrafttas.tasmod.virtual.event.VirtualKeyboardEvent; /** * Stores keyboard specific values in a given timeframe.
@@ -38,7 +39,7 @@ *

Updating the keyboard

* This keyboard stores it's values in "states".
* That means that all the keys that are currently pressed are stored in {@link #pressedKeys}.
- * And this list is updated via a keyboard event in {@link #update(int, boolean, char)}.
+ * And this list is updated via a keyboard event in {@link #updateFromEvent(int, boolean, char)}.
*

Difference

* When comparing 2 keyboard states, we can generate a list of differences from them in form of {@link VirtualKeyboardEvent}s.
*
@@ -62,7 +63,7 @@
  * Now you had to hold the key until the next tick to get it recognised by the game.
*
* To fix this, now every subtick is stored as a keyboard state as well.
- * When updating the keyboard in {@link #update(int, boolean, char)}, a clone of itself is created and stored in {@link #subtickList},
+ * When updating the keyboard in {@link #updateFromEvent(int, boolean, char)}, a clone of itself is created and stored in {@link #subtickList},
* with the difference that the subtick state has no {@link #subtickList}.
* In a nutshell, the keyboard stores it's past changes in {@link #subtickList} with the first being the oldest change. * @@ -71,42 +72,42 @@ */ public class VirtualKeyboard extends VirtualPeripheral implements Serializable { - /** - * The list of characters that were pressed on this keyboard. - */ - private final List charList; - - /** - * A queue of characters used in {@link #getDifference(VirtualKeyboard, Queue)}.
- * Used for distributing characters to {@link VirtualKeyboardEvent}s in an order. - */ - private final ConcurrentLinkedQueue charQueue = new ConcurrentLinkedQueue<>(); - - /** - * Creates an empty parent keyboard with all keys unpressed - */ - public VirtualKeyboard() { - this(new LinkedHashSet<>(), new ArrayList<>(), new ArrayList<>(), true); - } - - /** - * Creates a subtick keyboard with {@link VirtualPeripheral#subtickList} uninitialized - * @param pressedKeys The new list of pressed keycodes for this subtickKeyboard - * @param charList A list of characters for this subtickKeyboard - */ - public VirtualKeyboard(Set pressedKeys, List charList){ - this(pressedKeys, charList, null, false); - } - - /** - * Creates a keyboard from existing variables - * @param pressedKeys The existing list of pressed keycodes - * @param charList The existing list of characters - */ + /** + * The list of characters that were pressed on this keyboard. + */ + private final List charList; + + /** + * A queue of characters used in {@link #getDifference(VirtualKeyboard, Queue)}.
+ * Used for distributing characters to {@link VirtualKeyboardEvent}s in an order. + */ + private final ConcurrentLinkedQueue charQueue = new ConcurrentLinkedQueue<>(); + + /** + * Creates an empty parent keyboard with all keys unpressed + */ + public VirtualKeyboard() { + this(new LinkedHashSet<>(), new ArrayList<>(), new ArrayList<>(), true); + } + + /** + * Creates a subtick keyboard with {@link VirtualPeripheral#subtickList} uninitialized + * @param pressedKeys The new list of pressed keycodes for this subtickKeyboard + * @param charList A list of characters for this subtickKeyboard + */ + public VirtualKeyboard(Set pressedKeys, List charList) { + this(pressedKeys, charList, null, false); + } + + /** + * Creates a keyboard from existing variables + * @param pressedKeys The existing list of pressed keycodes + * @param charList The existing list of characters + */ public VirtualKeyboard(Set pressedKeys, List charList, boolean ignoreFirstUpdate) { this(pressedKeys, charList, null, ignoreFirstUpdate); } - + /** * Creates a keyboard from existing variables * @param pressedKeys The existing list of {@link VirtualPeripheral#pressedKeys} @@ -114,46 +115,81 @@ public VirtualKeyboard(Set pressedKeys, List charList, boole * @param subtickList {@link VirtualPeripheral#subtickList} * @param ignoreFirstUpdate The {@link VirtualPeripheral#ignoreFirstUpdate} */ - public VirtualKeyboard(Set pressedKeys, List charList, List subtickList, boolean ignoreFirstUpdate) { - super(pressedKeys, subtickList, ignoreFirstUpdate); - this.charList = charList; - } - - /** - * Updates the keyboard, adds a new subtick to this keyboard - * @param keycode The keycode of this key - * @param keystate The keystate of this key, true for pressed - * @param keycharacter The character that is associated with that key. Can change between keyboards or whenever shift is held in combination. - */ - public void update(int keycode, boolean keystate, char keycharacter, boolean repeatEventsEnabled) { - if(isParent() && !ignoreFirstUpdate()) { - addSubtick(shallowClone()); - } - charList.clear(); + public VirtualKeyboard(Set pressedKeys, List charList, List subtickList, boolean ignoreFirstUpdate) { + super(pressedKeys, subtickList, ignoreFirstUpdate); + this.charList = charList; + } + + /** + * Updates the keyboard from an event, adds a new subtick to this keyboard.
+ *
+ * An event updates one key at a time. + * @param keycode The keycode of this key + * @param keystate The keystate of this key, true for pressed + * @param keycharacter The character that is associated with that key. Can change between keyboards or whenever shift is held in combination. + */ + public void updateFromEvent(int keycode, boolean keystate, char keycharacter, boolean repeatEventsEnabled) { + createSubtick(); + charList.clear(); if (keystate) { addChar(keycharacter, repeatEventsEnabled); } - setPressed(keycode, keystate); - } - - public void update(int keycode, boolean keystate, char keycharacter) { - update(keycode, keystate, keycharacter, false); - } - - public void update(VirtualKey key, boolean keystate, char keycharacter) { - update(key.getKeycode(), keystate, keycharacter); - } - - public void update(VirtualKey key, boolean keystate, char keycharacter, boolean repeatEventsEnabled) { - update(key.getKeycode(), keystate, keycharacter, false); - } - - @Override - public void setPressed(int keycode, boolean keystate) { - if (keycode >= 0) { // Keyboard keys always have a keycode larger or equal than 0 - super.setPressed(keycode, keystate); - } - } + setPressed(keycode, keystate); + } + + public void updateFromEvent(int keycode, boolean keystate, char keycharacter) { + updateFromEvent(keycode, keystate, keycharacter, false); + } + + public void updateFromEvent(VirtualKey key, boolean keystate, char keycharacter) { + updateFromEvent(key.getKeycode(), keystate, keycharacter); + } + + public void updateFromEvent(VirtualKey key, boolean keystate, char keycharacter, boolean repeatEventsEnabled) { + updateFromEvent(key.getKeycode(), keystate, keycharacter, false); + } + + /** + * Updates this keyboard from a state, and adds a new subtick.
+ *
+ * The difference to {@link #updateFromEvent(int, boolean, char)} is,
+ * that a state may update multiple pressed keys and chars at once.
+ *
+ * While update fromEvent is used when the player inputs something on the keyboard,
+ * updateFromState is used when creating a VirtualKeyboard by deserialising the TASfile,
+ * as the inputs in the TASfile are stored in states. + * + * @param keycodes An array of keycodes, that replaces {@link Subtickable#pressedKeys} + * @param chars An array of characters, that replaces {@link #charList} + * @see SerialiserFlavorBase#deserialiseKeyboard + */ + public void updateFromState(int[] keycodes, char[] chars) { + createSubtick(); + + this.pressedKeys.clear(); + for (int i : keycodes) { + this.pressedKeys.add(i); + } + + this.charList.clear(); + for (char c : chars) { + this.charList.add(c); + } + } + + @Override + public void createSubtick() { + if (isParent() && !ignoreFirstUpdate()) { + addSubtick(shallowClone()); + } + } + + @Override + public void setPressed(int keycode, boolean keystate) { + if (keycode >= 0) { // Keyboard keys always have a keycode larger or equal than 0 + super.setPressed(keycode, keystate); + } + } /** * Calculates a list of {@link VirtualKeyboardEvent}s to the next peripheral, @@ -169,7 +205,7 @@ public void setPressed(int keycode, boolean keystate) { public void getVirtualEvents(VirtualKeyboard nextKeyboard, Queue reference) { if (isParent()) { VirtualKeyboard currentSubtick = this; - for(VirtualKeyboard subtick : nextKeyboard.getAll()) { + for (VirtualKeyboard subtick : nextKeyboard.getAll()) { currentSubtick.getDifference(subtick, reference); currentSubtick = subtick; } @@ -186,10 +222,10 @@ public void getVirtualEvents(VirtualKeyboard nextKeyboard, Queue reference) { - charQueue.addAll(nextKeyboard.charList); + public void getDifference(VirtualKeyboard nextKeyboard, Queue reference) { + charQueue.addAll(nextKeyboard.charList); - /* Calculate symmetric difference of keycodes */ + /* Calculate symmetric difference of keycodes */ /* Calculate unpressed keys @@ -198,11 +234,11 @@ public void getDifference(VirtualKeyboard nextKeyboard, Queue - * Null characters will be discarded; - * @param character The character to add - */ - public void addChar(char character, boolean repeatEventsEnabled) { - if(character != Character.MIN_VALUE || repeatEventsEnabled) { - charList.add(character); - } - } - - @Override - public void clear(){ - super.clear(); - charList.clear(); - } - + while (!charQueue.isEmpty()) { + reference.add(new VirtualKeyboardEvent(lastKey, true, getOrMinChar(charQueue.poll()))); + } + + } + + private char getOrMinChar(Character charr) { + if (charr == null) { + charr = Character.MIN_VALUE; + } + return charr; + } + + /** + * Add a character to the {@link #charList}
+ * Null characters will be discarded; + * @param character The character to add + */ + public void addChar(char character, boolean repeatEventsEnabled) { + if (character != Character.MIN_VALUE || repeatEventsEnabled) { + charList.add(character); + } + } + + @Override + public void clear() { + super.clear(); + charList.clear(); + } + @Override public String toString() { if (isParent()) { @@ -274,7 +310,7 @@ public String toString() { } } - private String toString2(){ + public String toString2() { return String.format("%s;%s", super.toString(), charListToString(charList)); } @@ -292,65 +328,69 @@ private String charListToString(List charList) { * Clones this VirtualKeyboard without subticks. */ public VirtualKeyboard shallowClone() { - return new VirtualKeyboard(new HashSet<>(this.pressedKeys), new ArrayList<>(this.charList), isIgnoreFirstUpdate()); - } - + return new VirtualKeyboard(new HashSet<>(this.pressedKeys), new ArrayList<>(this.charList), isIgnoreFirstUpdate()); + } + @Override - public VirtualKeyboard clone(){ + public VirtualKeyboard clone() { return new VirtualKeyboard(new HashSet<>(this.pressedKeys), new ArrayList<>(this.charList), new ArrayList<>(subtickList), isIgnoreFirstUpdate()); } - - @Override - public void moveFrom(VirtualKeyboard keyboard) { - if(keyboard == null) - return; - super.moveFrom(keyboard); - charList.clear(); - charList.addAll(keyboard.charList); - keyboard.charList.clear(); - } - - @Override + + @Override + public void moveFrom(VirtualKeyboard keyboard) { + if (keyboard == null) + return; + super.moveFrom(keyboard); + charList.clear(); + charList.addAll(keyboard.charList); + keyboard.charList.clear(); + } + + @Override public void copyFrom(VirtualKeyboard keyboard) { - if(keyboard == null) - return; - super.copyFrom(keyboard); - charList.clear(); - charList.addAll(keyboard.charList); - } - - @Override - public void deepCopyFrom(VirtualKeyboard keyboard) { - if(keyboard == null) - return; - super.deepCopyFrom(keyboard); - charList.clear(); - charList.addAll(keyboard.charList); - } - - @Override - public boolean equals(Object obj) { - if(obj instanceof VirtualKeyboard) { - VirtualKeyboard keyboard = (VirtualKeyboard) obj; - - if(charList.size() != keyboard.charList.size()) { - return false; - } - - for (int i = 0; i < charList.size(); i++) { - if(charList.get(i)!=keyboard.charList.get(i)) { + if (keyboard == null) + return; + super.copyFrom(keyboard); + charList.clear(); + charList.addAll(keyboard.charList); + } + + @Override + public void deepCopyFrom(VirtualKeyboard keyboard) { + if (keyboard == null) + return; + super.deepCopyFrom(keyboard); + charList.clear(); + charList.addAll(keyboard.charList); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof VirtualKeyboard) { + VirtualKeyboard keyboard = (VirtualKeyboard) obj; + + if (charList.size() != keyboard.charList.size()) { + return false; + } + + for (int i = 0; i < charList.size(); i++) { + if (charList.get(i) != keyboard.charList.get(i)) { return false; } } - return super.equals(obj); - } - return super.equals(obj); - } - - /** - * @return An immutable {@link #charList} - */ - public List getCharList() { - return ImmutableList.copyOf(charList); - } + return super.equals(obj); + } + return super.equals(obj); + } + + /** + * @return An immutable {@link #charList} + */ + public List getCharList() { + return ImmutableList.copyOf(charList); + } + + public boolean isEmpty() { + return super.isEmpty() && charList.isEmpty(); + } } diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualMouse.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualMouse.java index 6b83e7a5..3d4efab5 100644 --- a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualMouse.java +++ b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualMouse.java @@ -1,11 +1,17 @@ package com.minecrafttas.tasmod.virtual; -import com.minecrafttas.tasmod.virtual.event.VirtualMouseEvent; - import java.io.Serializable; -import java.util.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Queue; +import java.util.Set; import java.util.stream.Collectors; +import com.minecrafttas.tasmod.playback.tasfile.flavor.SerialiserFlavorBase; +import com.minecrafttas.tasmod.virtual.event.VirtualMouseEvent; + /** * Stores the mouse specific values in a given timeframe
*
@@ -37,7 +43,7 @@ public class VirtualMouse extends VirtualPeripheral implements Ser /** * Creates a mouse with no buttons pressed and no data */ - public VirtualMouse(){ + public VirtualMouse() { this(new LinkedHashSet<>(), 0, 0, 0, new ArrayList<>(), true); } @@ -62,7 +68,7 @@ public VirtualMouse(Set pressedKeys, int scrollWheel, int cursorX, int * @param cursorY The {@link #cursorY} * @param subtickList The {@link VirtualPeripheral#subtickList} */ - public VirtualMouse(Set pressedKeys, int scrollWheel, Integer cursorX, Integer cursorY, List subtickList) { + public VirtualMouse(Set pressedKeys, int scrollWheel, int cursorX, int cursorY, List subtickList) { this(pressedKeys, scrollWheel, cursorX, cursorY, subtickList, false); } @@ -74,27 +80,27 @@ public VirtualMouse(Set pressedKeys, int scrollWheel, Integer cursorX, * @param cursorX The {@link #cursorX} * @param cursorY The {@link #cursorY} * @param subtickList The {@link VirtualPeripheral#subtickList} - * @param ignoreFirstUpdate Whether the first call to {@link #update(int, boolean, int, Integer, Integer)} should create a new subtick + * @param ignoreFirstUpdate Whether the first call to {@link #updateFromEvent(int, boolean, int, Integer, Integer)} should create a new subtick */ - public VirtualMouse(Set pressedKeys, int scrollWheel, Integer cursorX, Integer cursorY, List subtickList, boolean ignoreFirstUpdate) { + public VirtualMouse(Set pressedKeys, int scrollWheel, int cursorX, int cursorY, List subtickList, boolean ignoreFirstUpdate) { super(pressedKeys, subtickList, ignoreFirstUpdate); this.scrollWheel = scrollWheel; this.cursorX = cursorX; this.cursorY = cursorY; } - /** - * Updates the mouse, adds a new subtick to this mouse - * @param keycode The keycode of this button - * @param keystate The keystate of this button, true for pressed - * @param scrollwheel The scroll wheel for this mouse - * @oaram cursorX The pointer location in the x axis - * @param cursorY The pointer location in the y axis - */ - public void update(int keycode, boolean keystate, int scrollwheel, Integer cursorX, Integer cursorY) { - if (isParent() && !ignoreFirstUpdate()) { - addSubtick(shallowClone()); - } + /** + * Updates the mouse, adds a new subtick to this mouse
+ *
+ * An event updates one key at a time. + * @param keycode The keycode of this button + * @param keystate The keystate of this button, true for pressed + * @param scrollwheel The scroll wheel for this mouse + * @param cursorX The pointer location in the x axis + * @param cursorY The pointer location in the y axis + */ + public void updateFromEvent(int keycode, boolean keystate, int scrollwheel, int cursorX, int cursorY) { + createSubtick(); setPressed(keycode, keystate); this.scrollWheel = scrollwheel; this.cursorX = cursorX; @@ -102,27 +108,65 @@ public void update(int keycode, boolean keystate, int scrollwheel, Integer curso } /** - * Updates the mouse, adds a new subtick to this mouse + * Updates the mouse, adds a new subtick to this mouse
+ *
+ * An event updates one key at a time. * - * @param key The key - * @param keystate The keystate of this button, true for pressed + * @param key The key + * @param keystate The keystate of this button, true for pressed * @param scrollwheel The scroll wheel for this mouse - * @oaram cursorX The pointer location in the x axis + * @param cursorX The pointer location in the x axis * @param cursorY The pointer location in the y axis */ - public void update(VirtualKey key, boolean keystate, int scrollwheel, Integer cursorX, Integer cursorY) { - update(key.getKeycode(), keystate, scrollwheel, cursorX, cursorY); + public void updateFromEvent(VirtualKey key, boolean keystate, int scrollwheel, int cursorX, int cursorY) { + updateFromEvent(key.getKeycode(), keystate, scrollwheel, cursorX, cursorY); + } + + /** + * Updates this mouse from a state, and adds a new subtick.
+ *
+ * The difference to {@link #updateFromEvent(int, boolean, int, Integer, Integer) updateFromEvent} is,
+ * that a state may update multiple pressed keys at once.
+ *
+ * While update fromEvent is used when the player inputs something on the mouse,
+ * updateFromState is used when creating a VirtualMouse by deserialising the TASfile,
+ * as the inputs in the TASfile are stored in states. + * + * @param keycodes An array of keycodes, that replaces {@link Subtickable#pressedKeys pressedKeys} + * @param scrollwheel The scroll wheel of this mouse state + * @param cursorX The pointer location in the x axis + * @param cursorY The pointer location in the y axis + * @see SerialiserFlavorBase#deserialiseMouse + */ + public void updateFromState(int[] keycodes, int scrollwheel, int cursorX, int cursorY) { + createSubtick(); + + this.pressedKeys.clear(); + for (int i : keycodes) { + this.pressedKeys.add(i); + } + + this.scrollWheel = scrollwheel; + this.cursorX = cursorX; + this.cursorY = cursorY; + } + + @Override + public void createSubtick() { + if (isParent() && !ignoreFirstUpdate()) { + addSubtick(shallowClone()); + } } @Override public void setPressed(int keycode, boolean keystate) { - if (keycode < 0) { // Mouse buttons always have a keycode smaller than 0 + if (keycode < 0) { // Mouse buttons always have a keycode smaller than 0 super.setPressed(keycode, keystate); } } /** - * Calculates a list of {@link VirtualMouseEvent}s, when comparing this mouse to + * Calculates a list of {@link VirtualMouseEvent VirtualMouseEvents}, when comparing this mouse to * the next mouse in the sequence,
* which also includes the subticks. * @@ -136,7 +180,7 @@ public void setPressed(int keycode, boolean keystate) { public void getVirtualEvents(VirtualMouse nextMouse, Queue reference) { if (isParent()) { VirtualMouse currentSubtick = this; - for(VirtualMouse subtick : nextMouse.getAll()) { + for (VirtualMouse subtick : nextMouse.getAll()) { currentSubtick.getDifference(subtick, reference); currentSubtick = subtick; } @@ -146,7 +190,7 @@ public void getVirtualEvents(VirtualMouse nextMouse, Queue re /** * Calculates the difference between 2 mice via symmetric difference
* and returns a list of the changes between them in form of - * {@link VirtualMouseEvent}s + * {@link VirtualMouseEvent VirtualMouseEvents} * * @param nextMouse The mouse that comes after this one.
* If this one is loaded at tick 15, the nextMouse should be @@ -154,12 +198,12 @@ public void getVirtualEvents(VirtualMouse nextMouse, Queue re * @param reference The queue to fill. Passed in by reference. */ public void getDifference(VirtualMouse nextMouse, Queue reference) { - + /* * Checks if pressedKeys are the same... */ - if(pressedKeys.equals(nextMouse.pressedKeys)){ - + if (pressedKeys.equals(nextMouse.pressedKeys)) { + /* * ...but scrollWheel, cursorX or cursorY are different. * Without this, the scrollWheel would only work if a mouse button is pressed at the same time. @@ -168,7 +212,7 @@ public void getDifference(VirtualMouse nextMouse, Queue refer * Otherwise, repeated usage of the scrollWheel will result in #equals being true, * which doesn't trigger the if clause like it should. */ - if(!equals(nextMouse) || scrollWheel != 0) { + if (!equals(nextMouse) || scrollWheel != 0) { reference.add(new VirtualMouseEvent(VirtualKey.MOUSEMOVED.getKeycode(), false, nextMouse.scrollWheel, nextMouse.cursorX, nextMouse.cursorY)); } return; @@ -179,21 +223,22 @@ public void getDifference(VirtualMouse nextMouse, Queue refer /* Calculate symmetric difference of keycodes */ - /* - Calculate unpressed keys - this: LC RC - next: LC MC - ------------- - RC <- unpressed - */ - for(int keycode : pressedKeys) { + /* + Calculate unpressed keys + this: LC RC + next: LC MC + ------------- + RC <- unpressed + */ + for (int keycode : pressedKeys) { if (!nextMouse.getPressedKeys().contains(keycode)) { reference.add(new VirtualMouseEvent(keycode, false, scrollWheelCopy, cursorXCopy, cursorYCopy)); scrollWheelCopy = 0; cursorXCopy = 0; cursorYCopy = 0; } - }; + } + ; /* Calculate pressed keys @@ -202,11 +247,12 @@ public void getDifference(VirtualMouse nextMouse, Queue refer ------------- MC <- pressed */ - for(int keycode : nextMouse.getPressedKeys()) { + for (int keycode : nextMouse.getPressedKeys()) { if (!this.pressedKeys.contains(keycode)) { reference.add(new VirtualMouseEvent(keycode, true, scrollWheelCopy, cursorXCopy, cursorYCopy)); } - }; + } + ; } @Override @@ -214,7 +260,7 @@ public void clear() { super.clear(); clearMouseData(); } - + /** * Resets mouse specific data to it's defaults */ @@ -223,8 +269,7 @@ private void clearMouseData() { cursorX = 0; cursorY = 0; } - - + @Override public String toString() { if (isParent()) { @@ -233,8 +278,8 @@ public String toString() { return toString2(); } } - - private String toString2(){ + + public String toString2() { return String.format("%s;%s,%s,%s", super.toString(), scrollWheel, cursorX, cursorY); } @@ -244,7 +289,7 @@ private String toString2(){ public VirtualMouse shallowClone() { return new VirtualMouse(new HashSet<>(this.pressedKeys), scrollWheel, cursorX, cursorY, null, ignoreFirstUpdate()); } - + @Override public VirtualMouse clone() { return new VirtualMouse(new HashSet<>(this.pressedKeys), scrollWheel, cursorX, cursorY, new ArrayList<>(subtickList), isIgnoreFirstUpdate()); @@ -252,65 +297,67 @@ public VirtualMouse clone() { @Override public void moveFrom(VirtualMouse mouse) { - if(mouse==null) + if (mouse == null) return; super.moveFrom(mouse); this.scrollWheel = mouse.scrollWheel; this.cursorX = mouse.cursorX; this.cursorY = mouse.cursorY; - mouse.scrollWheel=0; + mouse.scrollWheel = 0; } - + @Override public void copyFrom(VirtualMouse mouse) { - if(mouse==null) + if (mouse == null) return; super.copyFrom(mouse); this.scrollWheel = mouse.scrollWheel; this.cursorX = mouse.cursorX; this.cursorY = mouse.cursorY; } - + @Override public void deepCopyFrom(VirtualMouse mouse) { - if(mouse==null) + if (mouse == null) return; super.deepCopyFrom(mouse); this.scrollWheel = mouse.scrollWheel; this.cursorX = mouse.cursorX; this.cursorY = mouse.cursorY; } - + @Override public boolean equals(Object obj) { if (obj instanceof VirtualMouse) { VirtualMouse mouse = (VirtualMouse) obj; - return super.equals(obj) && - scrollWheel == mouse.scrollWheel && - cursorX == mouse.cursorX && - cursorY == mouse.cursorY; + return super.equals(obj) && scrollWheel == mouse.scrollWheel && cursorX == mouse.cursorX && cursorY == mouse.cursorY; } return super.equals(obj); } - + /** * @return {@link #scrollWheel} */ public int getScrollWheel() { return scrollWheel; } - + /** * @return {@link #cursorX} */ public int getCursorX() { return cursorX; } - + /** * @return {@link #cursorY} */ public int getCursorY() { return cursorY; } + + @Override + public boolean isEmpty() { + return super.isEmpty() && scrollWheel == 0 && cursorX == 0 && cursorY == 0; + } } diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualPeripheral.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualPeripheral.java index e194a4b0..824bcb15 100644 --- a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualPeripheral.java +++ b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualPeripheral.java @@ -78,6 +78,13 @@ public List getCurrentPresses() { return out; } + /** + * Creates a new subtick by {@link #shallowClone() shallowCloning} this VirtualPeripheral.
+ * If {@link Subtickable#ignoreFirstUpdate} is true, no new subtick will be created.
+ */ + public void createSubtick() { + } + @Override public String toString() { return String.join(",", getCurrentPresses()); @@ -136,42 +143,50 @@ public boolean equals(Object obj) { /** * Moves the data from another virtual peripheral into this peripheral without creating a new object.
* Deletes the data in the other peripheral.
- * Ignores {@link com.minecrafttas.tasmod.virtual.Subtickable.subtickList} + * Ignores {@link com.minecrafttas.tasmod.virtual.Subtickable.subtickList subtickList} * * @param peripheral The peripheral to move from */ protected void moveFrom(T peripheral) { - if(peripheral == null) + if (peripheral == null) return; copyFrom(peripheral); peripheral.subtickList.clear(); peripheral.resetFirstUpdate(); } - + /** * Copies the data from another virtual peripheral into this peripheral without creating a new object.
* Does not delete the data from the other peripehral.
- * Ignores {@link com.minecrafttas.tasmod.virtual.Subtickable.subtickList} + * Ignores the {@link com.minecrafttas.tasmod.virtual.Subtickable.subtickList subtickList} * * @param peripheral The peripheral to copy from */ protected void copyFrom(T peripheral) { - if(peripheral == null) + if (peripheral == null) return; this.pressedKeys.clear(); this.pressedKeys.addAll(peripheral.pressedKeys); } - + /** - * Copies the data from another virtual peripheral similar to {@link #copyFrom(VirtualPeripheral)}, but including the {@link com.minecrafttas.tasmod.virtual.Subtickable.subtickList} + * Copies the data from another virtual peripheral similar to {@link #copyFrom(VirtualPeripheral) copyFrom}, but including the {@link com.minecrafttas.tasmod.virtual.Subtickable.subtickList subtickList} * @param peripheral */ protected void deepCopyFrom(T peripheral) { - if(peripheral == null || !peripheral.isParent()) + if (peripheral == null || !peripheral.isParent()) return; copyFrom(peripheral); this.subtickList.clear(); this.subtickList.addAll(peripheral.subtickList); } - + + @Override + public boolean isEmpty() { + boolean flag = pressedKeys.isEmpty(); + if (pressedKeys.size() == 1) { + flag = pressedKeys.contains(VirtualKey.ZERO.getKeycode()) || pressedKeys.contains(VirtualKey.MOUSEMOVED.getKeycode()); + } + return super.isEmpty() && flag; + } } diff --git a/src/test/java/mctcommon/TestConfiguration.java b/src/test/java/mctcommon/TestConfiguration.java index 846b6d18..70177b22 100644 --- a/src/test/java/mctcommon/TestConfiguration.java +++ b/src/test/java/mctcommon/TestConfiguration.java @@ -7,7 +7,6 @@ import java.io.File; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -15,21 +14,46 @@ import com.minecrafttas.mctcommon.Configuration.ConfigOptions; class TestConfiguration { + + enum TestConfig implements ConfigOptions { + FileToOpen("fileToOpen", ""), + ServerConnection("serverConnection", ""); + + private String configKey; + private String defaultValue; + + private TestConfig(String configKey, String defaultValue) { + this.configKey = configKey; + this.defaultValue = defaultValue; + } + + @Override + public String getDefaultValue() { + return defaultValue; + } + + @Override + public String getConfigKey() { + return configKey; + } + + @Override + public String getExtensionName() { + return "TestConfig"; + } + } - private static Configuration config; + private Configuration config; private static final File configPath = new File("./config.xml"); - @BeforeAll - static void setUpBeforeClass() throws Exception { + @BeforeEach + void beforeEach() { config = new Configuration("Test config", configPath); + config.register(TestConfig.values()); + config.load(); } - @BeforeEach - void resetOptions() throws Exception { - config.reset(ConfigOptions.FileToOpen); - } - @AfterAll static void tearDownAfterClass() throws Exception { configPath.delete(); @@ -50,7 +74,9 @@ void testIfInitialized() { void testDefault() { configPath.delete(); config = new Configuration("Test config", configPath); - assertEquals("", config.get(ConfigOptions.FileToOpen)); + config.register(TestConfig.values()); + config.load(); + assertEquals("", config.get(TestConfig.FileToOpen)); } /** @@ -58,9 +84,11 @@ void testDefault() { */ @Test void testSavingAndLoading() { - config.set(ConfigOptions.FileToOpen, "Test"); + config.set(TestConfig.FileToOpen, "Test"); config = new Configuration("Test config", configPath); - assertEquals("Test", config.get(ConfigOptions.FileToOpen)); + config.register(TestConfig.values()); + config.load(); + assertEquals("Test", config.get(TestConfig.FileToOpen)); } /** @@ -68,8 +96,8 @@ void testSavingAndLoading() { */ @Test void testIntegers() { - config.set(ConfigOptions.FileToOpen, 3); - assertEquals(3, config.getInt(ConfigOptions.FileToOpen)); + config.set(TestConfig.FileToOpen, 3); + assertEquals(3, config.getInt(TestConfig.FileToOpen)); } /** @@ -77,8 +105,8 @@ void testIntegers() { */ @Test void testBooleans() { - config.set(ConfigOptions.FileToOpen, true); - assertEquals(true, config.getBoolean(ConfigOptions.FileToOpen)); + config.set(TestConfig.FileToOpen, true); + assertEquals(true, config.getBoolean(TestConfig.FileToOpen)); } /** @@ -86,8 +114,8 @@ void testBooleans() { */ @Test void testDeleteAndContains() { - config.delete(ConfigOptions.FileToOpen); - assertFalse(config.has(ConfigOptions.FileToOpen)); + config.delete(TestConfig.FileToOpen); + assertFalse(config.has(TestConfig.FileToOpen)); } /** @@ -95,7 +123,7 @@ void testDeleteAndContains() { */ @Test void resetToDefault() { - config.reset(ConfigOptions.FileToOpen); - assertEquals("", config.get(ConfigOptions.FileToOpen)); + config.reset(TestConfig.FileToOpen); + assertEquals("", config.get(TestConfig.FileToOpen)); } } diff --git a/src/test/java/mctcommon/server/ByteBufferBuilderTest.java b/src/test/java/mctcommon/server/ByteBufferBuilderTest.java index 8a78432f..dc3f5f02 100644 --- a/src/test/java/mctcommon/server/ByteBufferBuilderTest.java +++ b/src/test/java/mctcommon/server/ByteBufferBuilderTest.java @@ -10,10 +10,10 @@ import org.junit.jupiter.api.Test; -import com.minecrafttas.mctcommon.server.ByteBufferBuilder; -import com.minecrafttas.mctcommon.server.CompactPacketHandler; -import com.minecrafttas.mctcommon.server.Client.Side; -import com.minecrafttas.mctcommon.server.interfaces.PacketID; +import com.minecrafttas.mctcommon.networking.ByteBufferBuilder; +import com.minecrafttas.mctcommon.networking.CompactPacketHandler; +import com.minecrafttas.mctcommon.networking.Client.Side; +import com.minecrafttas.mctcommon.networking.interfaces.PacketID; class ByteBufferBuilderTest { @@ -58,6 +58,11 @@ public String getName() { public boolean shouldTrace() { return false; } + + @Override + public String getExtensionName() { + return "TestPackets"; + } } diff --git a/src/test/java/mctcommon/server/ServerTest.java b/src/test/java/mctcommon/server/ServerTest.java index 93459991..fc096cf4 100644 --- a/src/test/java/mctcommon/server/ServerTest.java +++ b/src/test/java/mctcommon/server/ServerTest.java @@ -15,17 +15,17 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import com.minecrafttas.mctcommon.server.ByteBufferBuilder; -import com.minecrafttas.mctcommon.server.Client; -import com.minecrafttas.mctcommon.server.CompactPacketHandler; -import com.minecrafttas.mctcommon.server.Client.Side; -import com.minecrafttas.mctcommon.server.PacketHandlerRegistry; -import com.minecrafttas.mctcommon.server.Server; -import com.minecrafttas.mctcommon.server.exception.PacketNotImplementedException; -import com.minecrafttas.mctcommon.server.exception.WrongSideException; -import com.minecrafttas.mctcommon.server.interfaces.ClientPacketHandler; -import com.minecrafttas.mctcommon.server.interfaces.PacketID; -import com.minecrafttas.mctcommon.server.interfaces.ServerPacketHandler; +import com.minecrafttas.mctcommon.networking.ByteBufferBuilder; +import com.minecrafttas.mctcommon.networking.Client; +import com.minecrafttas.mctcommon.networking.CompactPacketHandler; +import com.minecrafttas.mctcommon.networking.PacketHandlerRegistry; +import com.minecrafttas.mctcommon.networking.Server; +import com.minecrafttas.mctcommon.networking.Client.Side; +import com.minecrafttas.mctcommon.networking.exception.PacketNotImplementedException; +import com.minecrafttas.mctcommon.networking.exception.WrongSideException; +import com.minecrafttas.mctcommon.networking.interfaces.ClientPacketHandler; +import com.minecrafttas.mctcommon.networking.interfaces.PacketID; +import com.minecrafttas.mctcommon.networking.interfaces.ServerPacketHandler; /** * An integration test for the {@link Server} class by setting up a connection. @@ -87,6 +87,11 @@ public boolean shouldTrace() { return false; } + @Override + public String getExtensionName() { + return "TestPacketIds"; + } + } private static Client.Side side = null; diff --git a/src/test/java/tasmod/networking/TASmodByteBufferBuilderTest.java b/src/test/java/tasmod/networking/TASmodByteBufferBuilderTest.java index 7fd8ebb5..34a18a8b 100644 --- a/src/test/java/tasmod/networking/TASmodByteBufferBuilderTest.java +++ b/src/test/java/tasmod/networking/TASmodByteBufferBuilderTest.java @@ -10,9 +10,9 @@ import org.junit.jupiter.api.Test; -import com.minecrafttas.mctcommon.server.CompactPacketHandler; -import com.minecrafttas.mctcommon.server.Client.Side; -import com.minecrafttas.mctcommon.server.interfaces.PacketID; +import com.minecrafttas.mctcommon.networking.CompactPacketHandler; +import com.minecrafttas.mctcommon.networking.Client.Side; +import com.minecrafttas.mctcommon.networking.interfaces.PacketID; import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; import net.minecraft.nbt.NBTTagCompound; @@ -57,6 +57,11 @@ public String getName() { public boolean shouldTrace() { return false; } + + @Override + public String getExtensionName() { + return "TestPacketIds"; + } } diff --git a/src/test/java/tasmod/playback/metadata/PlaybackMetadataRegistryTest.java b/src/test/java/tasmod/playback/metadata/PlaybackMetadataRegistryTest.java index 1c3ba61e..326baf9a 100644 --- a/src/test/java/tasmod/playback/metadata/PlaybackMetadataRegistryTest.java +++ b/src/test/java/tasmod/playback/metadata/PlaybackMetadataRegistryTest.java @@ -1,13 +1,6 @@ package tasmod.playback.metadata; -import org.apache.commons.io.FileUtils; -import org.junit.jupiter.api.Test; - -import com.minecrafttas.tasmod.playback.metadata.PlaybackMetadata; -import com.minecrafttas.tasmod.playback.metadata.PlaybackMetadataRegistry; -import com.minecrafttas.tasmod.playback.metadata.PlaybackMetadataRegistry.PlaybackMetadataExtension; - -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.File; import java.io.IOException; @@ -15,17 +8,24 @@ import java.util.ArrayList; import java.util.List; +import org.apache.commons.io.FileUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Test; + +import com.minecrafttas.tasmod.playback.metadata.PlaybackMetadata; +import com.minecrafttas.tasmod.playback.metadata.PlaybackMetadata.PlaybackMetadataExtension; +import com.minecrafttas.tasmod.registries.TASmodAPIRegistry; public class PlaybackMetadataRegistryTest { - class Test1 implements PlaybackMetadataExtension{ + class Test1 extends PlaybackMetadataExtension { private String actual; - + public String getActual() { return actual; } - + @Override public String getExtensionName() { return "Test1"; @@ -50,26 +50,26 @@ public void onLoad(PlaybackMetadata metadata) { @Override public void onClear() { } - + } - - File file = new File("src/test/resources/run/MetadataRegistry.txt"); - + + File file = new File("src/test/resources/metadata/MetadataRegistry.txt"); + void store() { - List list = PlaybackMetadataRegistry.handleOnStore(); + List list = TASmodAPIRegistry.PLAYBACK_METADATA.handleOnStore(); List out = new ArrayList<>(); - + list.forEach(data -> { out.addAll(data.toStringList()); }); - + try { FileUtils.writeLines(file, out); } catch (IOException e) { e.printStackTrace(); } } - + void load() { List loaded = null; try { @@ -77,29 +77,33 @@ void load() { } catch (IOException e) { e.printStackTrace(); } - - + List meta = new ArrayList<>(); - + meta.add(PlaybackMetadata.fromStringList("Test1", loaded)); - - PlaybackMetadataRegistry.handleOnLoad(meta); + + TASmodAPIRegistry.PLAYBACK_METADATA.handleOnLoad(meta); } - + /** * Register, store and read metadata */ @Test void testRegistry() { Test1 actual = new Test1(); - PlaybackMetadataRegistry.register(actual); - + TASmodAPIRegistry.PLAYBACK_METADATA.register(actual); + store(); load(); - + assertEquals("Testing 1", actual.getActual()); - if(file.exists()) { + if (file.exists()) { file.delete(); } } + + @AfterAll + static void afterAll() { + TASmodAPIRegistry.PLAYBACK_METADATA.clear(); + } } diff --git a/src/test/java/tasmod/playback/metadata/PlaybackMetadataTest.java b/src/test/java/tasmod/playback/metadata/PlaybackMetadataTest.java index fbb21a7c..bbb7e94e 100644 --- a/src/test/java/tasmod/playback/metadata/PlaybackMetadataTest.java +++ b/src/test/java/tasmod/playback/metadata/PlaybackMetadataTest.java @@ -11,11 +11,11 @@ import org.junit.jupiter.api.Test; import com.minecrafttas.tasmod.playback.metadata.PlaybackMetadata; -import com.minecrafttas.tasmod.playback.metadata.PlaybackMetadataRegistry.PlaybackMetadataExtension; +import com.minecrafttas.tasmod.playback.metadata.PlaybackMetadata.PlaybackMetadataExtension; public class PlaybackMetadataTest { - class MetadataTest implements PlaybackMetadataExtension{ + class MetadataTest extends PlaybackMetadataExtension{ @Override public String getExtensionName() { @@ -46,7 +46,7 @@ public void onClear() { void testConstructor() { MetadataTest test = new MetadataTest(); PlaybackMetadata metadata = new PlaybackMetadata(test); - assertNotNull(metadata.getMetadata()); + assertNotNull(metadata.getData()); assertEquals("Test", metadata.getExtensionName()); } diff --git a/src/test/java/tasmod/playback/tasfile/PlaybackSerialiserTest.java b/src/test/java/tasmod/playback/tasfile/PlaybackSerialiserTest.java new file mode 100644 index 00000000..91834091 --- /dev/null +++ b/src/test/java/tasmod/playback/tasfile/PlaybackSerialiserTest.java @@ -0,0 +1,339 @@ +package tasmod.playback.tasfile; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertIterableEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.File; +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.io.FileUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import com.dselent.bigarraylist.BigArrayList; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient.CommentContainer; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TickContainer; +import com.minecrafttas.tasmod.playback.filecommands.PlaybackFileCommand; +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.metadata.PlaybackMetadata.PlaybackMetadataExtension; +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.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 PlaybackSerialiserTest { + + private static class TestFlavor extends SerialiserFlavorBase { + + @Override + public String getExtensionName() { + return "Test"; + } + + + @Override + public SerialiserFlavorBase clone() { + return new TestFlavor(); + } + } + + private static class TestMetadatada extends PlaybackMetadataExtension { + + String testValue = ""; + String actual = "e"; + + @Override + public String getExtensionName() { + return "Test"; + } + + @Override + public void onCreate() { + + } + + @Override + public PlaybackMetadata onStore() { + PlaybackMetadata metadata =new PlaybackMetadata(this); + metadata.setValue("TestKey", testValue); + return metadata; + } + + @Override + public void onLoad(PlaybackMetadata metadata) { + actual = metadata.getValue("TestKey"); + } + + @Override + public void onClear() { + } + + } + + private static class TestFileCommand extends PlaybackFileCommandExtension { + + List inline = new ArrayList<>(); + List endline = new ArrayList<>(); + + @Override + public String getExtensionName() { + return "tasmod_testFileExtension"; + } + + @Override + public void onDeserialiseInlineComment(long tick, TickContainer container, PlaybackFileCommandContainer fileCommandContainer) { + inline.add(fileCommandContainer.split("testKey")); + } + + @Override + public void onDeserialiseEndlineComment(long tick, TickContainer container, PlaybackFileCommandContainer fileCommandContainer) { + endline.add(fileCommandContainer.split("endlineKey")); + } + + @Override + public String[] getFileCommandNames() { + return new String[]{"testKey", "endlineKey"}; + } + } + + File file; + + private static TestFlavor testFlavor = new TestFlavor(); + private static TestMetadatada testMetadata = new TestMetadatada(); + private static TestFileCommand testFileCommand = new TestFileCommand(); + + @BeforeAll + static void register() { + TASmodAPIRegistry.SERIALISER_FLAVOR.register(testFlavor); + TASmodAPIRegistry.PLAYBACK_METADATA.register(testMetadata); + TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.register(testFileCommand); + } + + @AfterEach + void afterEach() { + testFileCommand.inline.clear(); + testFileCommand.endline.clear(); + if(file!=null) { + file.delete(); + } + } + + @AfterAll + static void unregister() { + TASmodAPIRegistry.SERIALISER_FLAVOR.unregister(testFlavor); + TASmodAPIRegistry.PLAYBACK_METADATA.unregister(testMetadata); + TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.unregister(testFileCommand); + } + + @Test + void testSerialiser() { + BigArrayList expected = new BigArrayList<>(); + + file = new File("src/test/resources/serialiser/PlaybackSerialiserTest.mctas"); + + testMetadata.testValue = "testing"; + TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.setEnabled("tasmod_testFileExtension", true); + // Tick 1 + + // Keyboard + VirtualKeyboard keyboard1 = new VirtualKeyboard(); + keyboard1.updateFromEvent(VirtualKey.W, true, 'w'); + keyboard1.updateFromEvent(VirtualKey.LCONTROL, true, (char)0); + + // Mouse + VirtualMouse mouse1 = new VirtualMouse(); + mouse1.updateFromEvent(VirtualKey.MOUSEMOVED, false, 15, 0, 0); + mouse1.updateFromEvent(VirtualKey.LC, true, 0, 0, 0); + + // CameraAngle + VirtualCameraAngle angle1 = new VirtualCameraAngle(); + angle1.set(0, 0); + angle1.updateFromEvent(10, 10); + + expected.add(new TickContainer(keyboard1, mouse1, angle1)); + + // Tick 2 + + // Keyboard + VirtualKeyboard keyboard2 = new VirtualKeyboard(); + keyboard2.copyFrom(keyboard1); + keyboard2.updateFromEvent(VirtualKey.W, false, (char)0); + keyboard2.updateFromEvent(VirtualKey.LCONTROL, false, (char)0); + + // Mouse + VirtualMouse mouse2 = new VirtualMouse(); + mouse2.copyFrom(mouse1); + mouse2.updateFromEvent(VirtualKey.MOUSEMOVED, false, 0, 14, 15); + mouse2.updateFromEvent(VirtualKey.LC, false, 0, 0, 0); + + // CameraAngle + VirtualCameraAngle angle2 = new VirtualCameraAngle(); + angle2.deepCopyFrom(angle1); + angle2.updateFromEvent(-10, -10); + + expected.add(new TickContainer(keyboard2, mouse2, angle2)); + + try { + PlaybackSerialiser.saveToFile(file, expected, "Test"); + } catch (PlaybackSaveException e) { + e.printStackTrace(); + } + + try { + BigArrayList actual = PlaybackSerialiser.loadFromFile(file, testFlavor); + assertBigArrayList(expected, actual); + assertEquals("testing", testMetadata.actual); + } catch (PlaybackLoadException | IOException e) { + fail(e); + } + } + + @Test + void testDeserialiser() throws PlaybackLoadException, IOException { + List lines = new ArrayList<>(); + lines.add("TASfile"); + lines.add("FileCommand-Extensions: tasmod_testFileExtension"); + lines.add("Flavor: Test"); + lines.add("----------- Test ----------"); + lines.add("TestKey: Wat"); + lines.add("##################################################"); + lines.add("// This is a regular comment"); + lines.add(""); + lines.add("// $testKey(test);"); + lines.add("1|W;w|| // test"); + lines.add("\t1|W,T;t|| // $testKey(test);$endlineKey();"); + lines.add("2|W;w|-101;0,1,1|1;1"); + lines.add("3|;|-101;0,~1,~1|~1;~1"); + lines.add("\t1|;|-101;0,~1,~1|~1;~1"); + + file = new File("src/test/resources/serialiser/PlaybackSerialiserTest2.mctas"); + try { + FileUtils.writeLines(file, lines); + } catch (IOException e) { + e.printStackTrace(); + } + + BigArrayList actual = PlaybackSerialiser.loadFromFile(file); + + BigArrayList expected = new BigArrayList<>(); + + VirtualKeyboard keyboard = new VirtualKeyboard(); + keyboard.updateFromEvent(VirtualKey.W, true, 'w'); + keyboard.updateFromEvent(VirtualKey.T, true, 't'); + + + CommentContainer container = new CommentContainer(); + container.addInlineComment("This is a regular comment"); + container.addInlineComment(null); + container.addEndlineComment("test"); + expected.add(new TickContainer(keyboard, new VirtualMouse(), new VirtualCameraAngle(), container)); + + + VirtualKeyboard keyboard2 = new VirtualKeyboard(); + keyboard2.updateFromEvent(VirtualKey.W, true, 'w'); + + VirtualMouse mouse2 = new VirtualMouse(); + mouse2.updateFromEvent(VirtualKey.MOUSEMOVED, false, 0, 1, 1); + + VirtualCameraAngle cameraAngle2 = new VirtualCameraAngle(); + cameraAngle2.set(1f, 1f); + + expected.add(new TickContainer(keyboard2, mouse2, cameraAngle2)); + + + VirtualMouse mouse3 = new VirtualMouse(); + mouse3.updateFromEvent(VirtualKey.MOUSEMOVED, false, 0, 2, 2); + mouse3.updateFromEvent(VirtualKey.MOUSEMOVED, false, 0, 3, 3); + + VirtualCameraAngle cameraAngle3 = new VirtualCameraAngle(); + cameraAngle3.updateFromState(2f, 2f); + + expected.add(new TickContainer(new VirtualKeyboard(), mouse3, cameraAngle3)); + + assertBigArrayList(expected, actual); + + assertEquals("Wat", testMetadata.actual); + + List fclist = new ArrayList<>(); + PlaybackFileCommandContainer fccontainer = new PlaybackFileCommandContainer(); + fccontainer.add("testKey", new PlaybackFileCommand("testKey", "test")); + + PlaybackFileCommandContainer fccontainerempty = new PlaybackFileCommandContainer(); + fccontainerempty.put("testKey", null); + + fclist.add(fccontainer); + fclist.add(fccontainerempty); + fclist.add(fccontainerempty); + assertIterableEquals(fclist, testFileCommand.inline); + + List fclistEnd = new ArrayList<>(); + PlaybackFileCommandContainer fccontainerEnd = new PlaybackFileCommandContainer(); + fccontainerEnd.add("endlineKey", null); + fccontainerEnd.add("endlineKey", new PlaybackFileCommand("endlineKey")); + + PlaybackFileCommandContainer fccontainerEndEmpty = new PlaybackFileCommandContainer(); + fccontainerEndEmpty.put("endlineKey", null); + + fclistEnd.add(fccontainerEnd); + fclistEnd.add(fccontainerEndEmpty); + fclistEnd.add(fccontainerEndEmpty); + assertIterableEquals(fclistEnd, testFileCommand.endline); + } + + @Test + void testFlavorNotFound() { + List lines = new ArrayList<>(); + for (int i = 0; i < 500; i++) { + lines.add("Test"); + } + + file = new File("src/test/resources/serialiser/PlaybackSerialiserTest3.mctas"); + try { + FileUtils.writeLines(file, lines); + } catch (IOException e) { + e.printStackTrace(); + } + + Throwable t = assertThrows(PlaybackLoadException.class, ()->{ + PlaybackSerialiser.loadFromFile(file); + }); + + assertEquals("Couldn't find a flavorname in the file. TASfile is missing a flavor-extension or the file is broken", t.getMessage()); + } + + @Test + void testFlavorIsNull() { + Throwable t = assertThrows(PlaybackLoadException.class, ()->{ + PlaybackSerialiser.loadFromFile(file, "NotAFlavor"); + }); + + 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 new file mode 100644 index 00000000..8864d879 --- /dev/null +++ b/src/test/java/tasmod/playback/tasfile/SerialiserFlavorBaseTest.java @@ -0,0 +1,1073 @@ +package tasmod.playback.tasfile; + +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.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import com.dselent.bigarraylist.BigArrayList; +import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TickContainer; +import com.minecrafttas.tasmod.playback.filecommands.PlaybackFileCommand; +import com.minecrafttas.tasmod.playback.filecommands.PlaybackFileCommand.PlaybackFileCommandExtension; +import com.minecrafttas.tasmod.playback.metadata.PlaybackMetadata; +import com.minecrafttas.tasmod.playback.metadata.PlaybackMetadata.PlaybackMetadataExtension; +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; +import com.minecrafttas.tasmod.virtual.VirtualKey; +import com.minecrafttas.tasmod.virtual.VirtualKeyboard; +import com.minecrafttas.tasmod.virtual.VirtualMouse; + +public class SerialiserFlavorBaseTest extends SerialiserFlavorBase { + + @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; + } + + @Override + public String getExtensionName() { + return "Test"; + } + + @Test + void testSerialiseFlavorname() { + List actual = new ArrayList<>(); + serialiseFlavorName(actual); + + List expected = new ArrayList<>(); + expected.add("Flavor: Test"); + + assertIterableEquals(expected, actual); + } + + @Test + void testSerialiseHeaderStart() { + assertEquals("##################### TASfile ####################", headerStart()); + } + + /** + * Test serialising metadata part of the header + */ + @Test + void testSerialiseMetadata() { + + class MetadataTest extends PlaybackMetadataExtension { + + public String testValue; + + @Override + public String getExtensionName() { + return "Test1"; + } + + @Override + public void onCreate() { + + } + + @Override + public PlaybackMetadata onStore() { + PlaybackMetadata metadata = new PlaybackMetadata(this); + metadata.setValue("TestKey", testValue); + return metadata; + } + + @Override + public void onLoad(PlaybackMetadata metadata) { + testValue = metadata.getValue("TestKey"); + } + + @Override + public void onClear() { + this.testValue = null; + } + + } + + class MetadataTest2 extends PlaybackMetadataExtension { + + public String testValue; + + @Override + public String getExtensionName() { + return "Test2"; + } + + @Override + public void onCreate() { + + } + + @Override + public PlaybackMetadata onStore() { + PlaybackMetadata metadata = new PlaybackMetadata(this); + metadata.setValue("TestKey", testValue); + return metadata; + } + + @Override + public void onLoad(PlaybackMetadata metadata) { + testValue = metadata.getValue("TestKey"); + } + + @Override + public void onClear() { + this.testValue = null; + } + + } + + MetadataTest testmetadata1 = new MetadataTest(); + testmetadata1.testValue = "This is a test"; + + MetadataTest2 testmetadata2 = new MetadataTest2(); + testmetadata2.testValue = "This is a second test"; + + TASmodAPIRegistry.PLAYBACK_METADATA.register(testmetadata1); + TASmodAPIRegistry.PLAYBACK_METADATA.register(testmetadata2); + + List actual = new ArrayList<>(); + serialiseMetadata(actual); + + List expected = new ArrayList<>(); + expected.add("---------------------- Test1 ---------------------"); + expected.add("TestKey:This is a test"); + expected.add(""); + expected.add("---------------------- Test2 ---------------------"); + expected.add("TestKey:This is a second test"); + expected.add(""); + + assertIterableEquals(expected, actual); + assertEquals(0, currentTick); + + TASmodAPIRegistry.PLAYBACK_METADATA.unregister(testmetadata1); + TASmodAPIRegistry.PLAYBACK_METADATA.unregister(testmetadata2); + } + + @Test + void testSerialiseFileCommandNames() { + + class TestFileCommand extends PlaybackFileCommandExtension { + + @Override + public String getExtensionName() { + return "tasmod_testFileCommand"; + } + + @Override + public String[] getFileCommandNames() { + return null; + } + } + + TestFileCommand fc = new TestFileCommand(); + TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.register(fc); + TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.setEnabled("tasmod_testFileCommand", true); + + List actual = new ArrayList<>(); + serialiseFileCommandNames(actual); + + List expected = new ArrayList<>(); + expected.add("FileCommand-Extensions: tasmod_testFileCommand"); + expected.add(""); + + assertIterableEquals(expected, actual); + } + + /** + * Test serialising a {@link TickContainer}.
+ * This container contains a keyboard, mouse and camera angle,
+ * with different amounts of subticks each. + */ + @Test + void testSerialiseContainer() { + // Prepare keyboard + VirtualKeyboard keyboard = new VirtualKeyboard(); + keyboard.updateFromEvent(VirtualKey.W, true, 'w'); + keyboard.updateFromEvent(VirtualKey.LCONTROL, true, Character.MIN_VALUE); + + // Prepare mouse + VirtualMouse mouse = new VirtualMouse(); + mouse.updateFromEvent(VirtualKey.LC, true, 0, 0, 0); + + // Prepare camera angle + VirtualCameraAngle angle = new VirtualCameraAngle(0f, 0f, true); + angle.updateFromEvent(1, 1); + angle.updateFromEvent(1, 1); + angle.updateFromEvent(1, 1); + angle.updateFromEvent(1, 1); + + // Create container and fill actual + TickContainer container = new TickContainer(keyboard, mouse, angle); + BigArrayList actual = new BigArrayList<>(); + serialiseContainer(actual, container); + + // Fill expected + BigArrayList expected = new BigArrayList<>(); + expected.add("0|W;w|LC;0,0,0|1.0;1.0"); + expected.add("\t1|W,LCONTROL;||2.0;2.0"); + expected.add("\t2|||3.0;3.0"); + expected.add("\t3|||4.0;4.0"); + + // C o m p a r e + assertBigArrayList(expected, actual); + } + + /** + * Test serialising inline and endline comments. + */ + @Test + void testSerialiseComments() { + List inlineComments = new ArrayList<>(); + + inlineComments.add("Test"); + inlineComments.add(null); // Should result in an empty line + inlineComments.add("Test2"); + inlineComments.add(""); // Should result in "// " + + List actual = serialiseInlineComments(inlineComments, new ArrayList<>()); + + List expected = new ArrayList<>(); + expected.add("// Test"); + expected.add(""); + expected.add("// Test2"); + expected.add("// "); + + assertIterableEquals(expected, actual); + + actual = serialiseEndlineComments(inlineComments, null); + + assertIterableEquals(expected, actual); + + } + + @Test + void testSerialiseFileCommands() { + List> fileCommands = new ArrayList<>(); + List fcInLine = new ArrayList<>(); + fcInLine.add(new PlaybackFileCommand("test")); + fcInLine.add(new PlaybackFileCommand("testing2", "true", "false")); + + List fcInLine2 = new ArrayList<>(); + fcInLine2.add(new PlaybackFileCommand("interpolation", "true")); + + fileCommands.add(fcInLine); + fileCommands.add(null); + fileCommands.add(fcInLine2); + fileCommands.add(new ArrayList<>()); + + List actual = serialiseInlineComments(null, fileCommands); + + List expected = new ArrayList<>(); + expected.add("// $test(); $testing2(true, false);"); + expected.add(""); + expected.add("// $interpolation(true);"); + expected.add("// "); + + assertIterableEquals(expected, actual); + } + + @Test + void testMergingCommentsAndCommands() { + List> fileCommands = new ArrayList<>(); + List fcInLine = new ArrayList<>(); + fcInLine.add(new PlaybackFileCommand("test")); + fcInLine.add(new PlaybackFileCommand("testing2", "true", "false")); + + fileCommands.add(fcInLine); + fileCommands.add(null); + fileCommands.add(null); + fileCommands.add(new ArrayList<>()); + + List fcInLine2 = new ArrayList<>(); + fcInLine2.add(new PlaybackFileCommand("interpolation", "true")); + + fileCommands.add(fcInLine2); + + List fcInLine3 = new ArrayList<>(); + fcInLine3.add(new PlaybackFileCommand("info", "Scribble")); + fcInLine3.add(new PlaybackFileCommand("info", "Dribble")); + + fileCommands.add(fcInLine3); + + List inlineComments = new ArrayList<>(); + + inlineComments.add("Test"); + inlineComments.add(null); + inlineComments.add("Test2"); + inlineComments.add(""); + inlineComments.add(null); + + List actual = serialiseInlineComments(inlineComments, fileCommands); + + List expected = new ArrayList<>(); + + expected.add("// $test(); $testing2(true, false); Test"); // Test both filecommand and comment + expected.add(""); // Test null from both + expected.add("// Test2"); // Test comment only + expected.add("// "); // Test empty from both + expected.add("// $interpolation(true);"); // Test command only + expected.add("// $info(Scribble); $info(Dribble);"); // Test command can't be merged with comments and is added at the end instead + + assertIterableEquals(expected, actual); + } + + @Test + void testExtractHeader() { + BigArrayList lines = new BigArrayList<>(); + lines.add("###### TASfile ######"); + lines.add("Flavor: beta"); + lines.add("Extensions: desync_monitor, control_bytes, vanilla_commands"); + lines.add("### General"); + lines.add("Author: Scribble"); + lines.add("Title: 77 Buttons"); + lines.add("##################################################"); + lines.add("This should not be read anymore"); + lines.add("1|W;w||"); + + List actual = extractHeader(lines); + + List expected = new ArrayList<>(); + expected.add("###### TASfile ######"); + expected.add("Flavor: beta"); + expected.add("Extensions: desync_monitor, control_bytes, vanilla_commands"); + expected.add("### General"); + expected.add("Author: Scribble"); + expected.add("Title: 77 Buttons"); + expected.add("##################################################"); + + assertIterableEquals(expected, actual); + } + + @Test + void testExtractHeaderFail() { + BigArrayList lines = new BigArrayList<>(); + lines.add("###### TASfile ######"); + lines.add("Flavor: beta"); + lines.add("Extensions: desync_monitor, control_bytes, vanilla_commands"); + lines.add("### General"); + lines.add("Author: Scribble"); + lines.add("Title: 77 Buttons"); + lines.add("This should not be read anymore"); + lines.add("1|W;w||"); + + PlaybackLoadException exception = assertThrows(PlaybackLoadException.class, () -> { + extractHeader(lines); + }); + + assertEquals("Cannot find the end of the header", exception.getMessage()); + } + + /** + * Test deserialising metadata + */ + @Test + void testDeserialiseMetadata() { + + class GeneralMetadata extends PlaybackMetadataExtension{ + + PlaybackMetadata metadata = null; + + @Override + public String getExtensionName() { + return "General"; + } + + @Override + public void onCreate() { + } + + @Override + public PlaybackMetadata onStore() { + return null; + } + + @Override + public void onLoad(PlaybackMetadata metadata) { + this.metadata = metadata; + } + + @Override + public void onClear() { + } + + } + + class StartPositionMetadata extends PlaybackMetadataExtension { + + PlaybackMetadata metadata = null; + + @Override + public String getExtensionName() { + return "StartPosition"; + } + + @Override + public void onCreate() { + } + + @Override + public PlaybackMetadata onStore() { + return null; + } + + @Override + public void onLoad(PlaybackMetadata metadata) { + this.metadata = metadata; + } + + @Override + public void onClear() { + } + + } + + GeneralMetadata general = new GeneralMetadata(); + StartPositionMetadata startPosition = new StartPositionMetadata(); + + TASmodAPIRegistry.PLAYBACK_METADATA.register(general); + TASmodAPIRegistry.PLAYBACK_METADATA.register(startPosition); + + List lines = new ArrayList<>(); + lines.add("--- General"); + lines.add("Author: Scribble"); + lines.add("Title: 77 Buttons"); + lines.add("Playing Time:00:00.0"); + lines.add(""); + lines.add("--- StartPosition"); + lines.add("x:1.0"); + lines.add("y:2.0"); + lines.add("z:3.0"); + lines.add("pitch:4.0"); + lines.add("yaw:5.0"); + lines.add(""); + + deserialiseMetadata(lines); + + LinkedHashMap first = new LinkedHashMap<>(); + first.put("Author", "Scribble"); + first.put("Title", "77 Buttons"); + first.put("Playing Time", "00:00.0"); + PlaybackMetadata expected = PlaybackMetadata.fromHashMap("General", first); + + LinkedHashMap second = new LinkedHashMap<>(); + second.put("x", "1.0"); + second.put("y", "2.0"); + second.put("z", "3.0"); + second.put("pitch", "4.0"); + second.put("yaw", "5.0"); + PlaybackMetadata expected2 = PlaybackMetadata.fromHashMap("StartPosition", second); + + assertEquals(expected, general.metadata); + assertEquals(expected2, startPosition.metadata); + } + + @Test + void testDeserialiseFileCommandNames() { + + class Test1 extends PlaybackFileCommandExtension{ + + @Override + public String getExtensionName() { + return "tasmod_test1"; + } + + @Override + public String[] getFileCommandNames() { + return null; + } + + } + + class Test2 extends PlaybackFileCommandExtension { + + @Override + public String getExtensionName() { + return "tasmod_test2"; + } + + @Override + public String[] getFileCommandNames() { + return null; + } + + } + + Test1 test1 = new Test1(); + Test2 test2 = new Test2(); + + TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.register(test1); + TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.register(test2); + + List lines = new ArrayList<>(); + lines.add("FileCommand-Extensions: tasmod_test1, tasmod_test2"); + + deserialiseFileCommandNames(lines); + + assertTrue(test1.isEnabled()); + assertTrue(test2.isEnabled()); + + lines = new ArrayList<>(); + lines.add("FileCommand-Extensions: "); + + deserialiseFileCommandNames(lines); + + assertFalse(test1.isEnabled()); + assertFalse(test2.isEnabled()); + + lines = new ArrayList<>(); + lines.add("FileCommand-Extensions: tasmod_test1,tasmod_test2"); + + deserialiseFileCommandNames(lines); + + assertTrue(test1.isEnabled()); + assertTrue(test2.isEnabled()); + + final List lines2 = new ArrayList<>(); + lines2.add("FileCommand-Extensions tasmod_test1,tasmod_test2"); + + Throwable t = assertThrows(PlaybackLoadException.class, ()->{ + deserialiseFileCommandNames(lines2); + }); + + assertEquals("FileCommand-Extensions value was not found in the header", t.getMessage()); + } + + /** + * Test extracing ticks from some lines + */ + @Test + void testExtractTick() { + // Create lines to be extracted from + BigArrayList lines = new BigArrayList<>(); + lines.add("###### TASfile ######"); + lines.add("Flavor: beta"); + lines.add("Extensions: desync_monitor, control_bytes, vanilla_commands"); + lines.add("##################################################"); + lines.add("55|W,LCONTROL;w|;0,887,626|17.85;-202.74799"); + lines.add("\t1||RC;0,1580,658|17.85;-202.74799"); + lines.add("\t2||;0,1580,658|17.85;-202.74799"); + lines.add("56|W,LCONTROL;w|;0,887,626|17.85;-202.74799"); + lines.add("\t1||RC;0,1580,658|17.85;-202.74799"); + lines.add("\t2||;0,1580,658|17.85;-202.74799"); + lines.add("// This is a comment"); + lines.add("// $fileCommand();"); + lines.add(""); + lines.add("57|W,LCONTROL;w|;0,887,626|17.85;-202.74799"); + lines.add("\t1||RC;0,1580,658|17.85;-202.74799\t\t// This is an endline comment"); + lines.add("\t2||;0,1580,658|17.85;-202.74799"); + + // Fill the actual with lists of the extracted ticks + List> actual = new ArrayList<>(); + // Also fill the actualIndex with the indices that are returned + List actualIndex = new ArrayList<>(); + for (long i = 0; i < lines.size(); i++) { + List tick = new ArrayList<>(); + long index = extractContainer(tick, lines, i); + i = index; + actual.add(tick); + actualIndex.add(index); + } + + // Fill expected + List> expected = new ArrayList<>(); + List tick1 = new ArrayList<>(); + tick1.add("55|W,LCONTROL;w|;0,887,626|17.85;-202.74799"); + tick1.add("\t1||RC;0,1580,658|17.85;-202.74799"); + tick1.add("\t2||;0,1580,658|17.85;-202.74799"); + + List tick2 = new ArrayList<>(); + tick2.add("56|W,LCONTROL;w|;0,887,626|17.85;-202.74799"); + tick2.add("\t1||RC;0,1580,658|17.85;-202.74799"); + tick2.add("\t2||;0,1580,658|17.85;-202.74799"); + + List tick3 = new ArrayList<>(); + tick3.add("// This is a comment"); + tick3.add("// $fileCommand();"); + tick3.add(""); + tick3.add("57|W,LCONTROL;w|;0,887,626|17.85;-202.74799"); + tick3.add("\t1||RC;0,1580,658|17.85;-202.74799\t\t// This is an endline comment"); + tick3.add("\t2||;0,1580,658|17.85;-202.74799"); + + expected.add(tick1); + expected.add(tick2); + expected.add(tick3); + + // Fill expectedIndex + List expectedIndex = new ArrayList<>(); + expectedIndex.add(6L); + expectedIndex.add(9L); + expectedIndex.add(15L); + + // C o m p a r e + assertIterableEquals(expected, actual); + assertIterableEquals(expectedIndex, actualIndex); + } + + @Test + void testExtractExceptions() { + // Create lines to be extracted from + BigArrayList lines = new BigArrayList<>(); + lines.add("\t1||RC;0,1580,658|17.85;-202.74799"); + lines.add("55|W,LCONTROL;w|;0,887,626|17.85;-202.74799"); + lines.add("\t2||;0,1580,658|17.85;-202.74799"); + + Throwable t = assertThrows(PlaybackLoadException.class, ()->{ + extractContainer(new ArrayList<>(), lines, 0); + }); + + // C o m p a r e + assertEquals("Line 1, Tick 0, Subtick 0: Error while trying to parse the file. This should not be a subtick at this position", t.getMessage()); + } + + @Test + void testExtractExceptions2() { + // Create lines to be extracted from + BigArrayList lines = new BigArrayList<>(); + lines.add("// Comment"); + lines.add("\t1||RC;0,1580,658|17.85;-202.74799\t\t// This is an endline comment"); + lines.add("57|W,LCONTROL;w|;0,887,626|17.85;-202.74799"); + lines.add("\t2||;0,1580,658|17.85;-202.74799"); + + Throwable t = assertThrows(PlaybackLoadException.class, ()->{ + extractContainer(new ArrayList<>(), lines, 0); + }); + + // C o m p a r e + assertEquals("Line 2, Tick 0, Subtick 0: Error while trying to parse the file. This should not be a subtick at this position", t.getMessage()); + } + + @Test + void testExtractExceptions3() { + // Create lines to be extracted from + BigArrayList lines = new BigArrayList<>(); + lines.add("57|W,LCONTROL;w|;0,887,626|17.85;-202.74799"); + lines.add("// Comment"); + lines.add("\t1||RC;0,1580,658|17.85;-202.74799\t\t// This is an endline comment"); + lines.add("\t2||;0,1580,658|17.85;-202.74799"); + + extractContainer(new ArrayList<>(), lines, 0); // First extraction passes as it parses up to the comment. + + Throwable t = assertThrows(PlaybackLoadException.class, ()->{ + extractContainer(new ArrayList<>(), lines, 1); // Second extraction fails as it starts with the comment then, a subtick which is disallowed + }); + + // C o m p a r e + assertEquals("Line 3, Tick 0, Subtick 0: Error while trying to parse the file. This should not be a subtick at this position", t.getMessage()); + } + + /** + * Test deserialising a container a.k.a a tick + */ + @Test + void testDeserialiseContainer() { + BigArrayList actual = new BigArrayList<>(); + List tick = new ArrayList<>(); + tick.add("55|W,LCONTROL;w|;0,887,626|17.85;-202.74799"); + tick.add("\t1||RC;-15,1580,658|11.85;-2.74799"); + tick.add("\t2||;0,1580,658|45;-22.799"); + + deserialiseContainer(actual, tick); + + BigArrayList expected = new BigArrayList<>(); + + VirtualKeyboard keyboard = new VirtualKeyboard(); + keyboard.updateFromState(new int[] { VirtualKey.W.getKeycode(), VirtualKey.LCONTROL.getKeycode() }, new char[] { 'w' }); + + VirtualMouse mouse = new VirtualMouse(); + mouse.updateFromState(new int[] { VirtualKey.MOUSEMOVED.getKeycode() }, 0, 887, 626); + mouse.updateFromState(new int[] { VirtualKey.RC.getKeycode() }, -15, 1580, 658); + mouse.updateFromState(new int[] { VirtualKey.MOUSEMOVED.getKeycode() }, 0, 1580, 658); + + VirtualCameraAngle cameraAngle = new VirtualCameraAngle(); + cameraAngle.updateFromState(17.85F, -202.74799F); + cameraAngle.updateFromState(11.85F, -2.74799F); + cameraAngle.updateFromState(45F, -22.799F); + + expected.add(new TickContainer(keyboard, mouse, cameraAngle)); + + assertBigArrayList(expected, actual); + } + + /** + * Test splitting the stringd of inputs including subticks into it's elements + */ + @Test + void testSplitInputs() { + List tick = new ArrayList<>(); + tick.add("55|W,LCONTROL;w|;0,887,626|17.85;-202.74799"); + tick.add("\t1||RC;0,1580,658|17.85;-202.74799 //Test"); + tick.add("\t2||;0,1580,658|17.85;-202.74799 // $test(true);"); + + List actualKeyboard = new ArrayList<>(); + List actualMouse = new ArrayList<>(); + List actualCameraAngle = new ArrayList<>(); + List actualComment = new ArrayList<>(); + List> actualFileCommand = new ArrayList<>(); + + splitInputs(tick, actualKeyboard, actualMouse, actualCameraAngle, actualComment, actualFileCommand); + + List expectedKeyboard = new ArrayList<>(); + List expectedMouse = new ArrayList<>(); + List expectedCameraAngle = new ArrayList<>(); + List expectedComment = new ArrayList<>(); + List> expectedFileCommand = new ArrayList<>(); + + expectedKeyboard.add("W,LCONTROL;w"); + + expectedMouse.add(";0,887,626"); + expectedMouse.add("RC;0,1580,658"); + expectedMouse.add(";0,1580,658"); + + expectedCameraAngle.add("17.85;-202.74799"); + expectedCameraAngle.add("17.85;-202.74799"); + expectedCameraAngle.add("17.85;-202.74799"); + + expectedComment.add(null); + expectedComment.add("Test"); + expectedComment.add(null); + + expectedFileCommand.add(null); + expectedFileCommand.add(null); + + List lineCommand = new ArrayList<>(); + lineCommand.add(new PlaybackFileCommand("test", "true")); + + expectedFileCommand.add(lineCommand); + + assertIterableEquals(actualKeyboard, expectedKeyboard); + assertIterableEquals(expectedMouse, actualMouse); + assertIterableEquals(expectedCameraAngle, actualCameraAngle); + assertIterableEquals(expectedComment, actualComment); + assertIterableEquals(expectedFileCommand, actualFileCommand); + } + + /** + * Test split container + */ + @Test + void testSplitContainer() { + List lines = new ArrayList<>(); + lines.add("// $interpolation(on);"); + lines.add("// Test"); + lines.add("55|W,LCONTROL;w|;0,887,626|17.85;-202.74799"); + lines.add("\t1||RC;-15,1580,658|11.85;-2.74799"); + lines.add("\t2||;0,1580,658|45;-22.799"); + + List actualComments = new ArrayList<>(); + List actualTick = new ArrayList<>(); + List> actualInlineFileCommands = new ArrayList<>(); + + splitContainer(lines, actualComments, actualTick, actualInlineFileCommands); + + List expectedComments = new ArrayList<>(); + List expectedTicks = new ArrayList<>(); + expectedComments.add(null); + expectedComments.add("Test"); + + expectedTicks.add("55|W,LCONTROL;w|;0,887,626|17.85;-202.74799"); + expectedTicks.add("\t1||RC;-15,1580,658|11.85;-2.74799"); + expectedTicks.add("\t2||;0,1580,658|45;-22.799"); + + List> expectedInlineFileCommands = new ArrayList<>(); + List commands = new ArrayList<>(); + commands.add(new PlaybackFileCommand("interpolation", "on")); + expectedInlineFileCommands.add(commands); + expectedInlineFileCommands.add(null); + + assertIterableEquals(expectedComments, actualComments); + assertIterableEquals(expectedTicks, actualTick); + assertIterableEquals(expectedInlineFileCommands, actualInlineFileCommands); + } + + /** + * Test deserialising keyboard + */ + @Test + void testDeserialiseKeyboard() { + List tick = new ArrayList<>(); + tick.add(";a"); + tick.add("W;w"); + tick.add("W,LCONTROL;"); + tick.add("W,LCONTROL,S;s"); + + VirtualKeyboard actual = deserialiseKeyboard(tick); + + VirtualKeyboard expected = new VirtualKeyboard(); + expected.updateFromEvent(VirtualKey.ZERO, false, 'a'); + expected.updateFromEvent(VirtualKey.W, true, 'w'); + expected.updateFromEvent(VirtualKey.LCONTROL, true, Character.MIN_VALUE); + expected.updateFromEvent(VirtualKey.S, true, 's'); + + assertEquals(expected, actual); + } + + @Test + void testDeserialiseKeyboardWithKeyCodes() { + List tick = new ArrayList<>(); + tick.add(";a"); + tick.add("17;w"); + tick.add("17,29;"); + tick.add("17,29,31;s"); + tick.add("17,29,31,500;"); // Test theoretical keycode that doesn't exist + + VirtualKeyboard actual = deserialiseKeyboard(tick); + + VirtualKeyboard expected = new VirtualKeyboard(); + expected.updateFromEvent(VirtualKey.ZERO, false, 'a'); + expected.updateFromEvent(VirtualKey.W, true, 'w'); + expected.updateFromEvent(VirtualKey.LCONTROL, true, Character.MIN_VALUE); + expected.updateFromEvent(VirtualKey.S, true, 's'); + expected.updateFromEvent(500, true, Character.MIN_VALUE); + + assertEquals(expected, actual); + } + + /** + * Test deserialising mouse + */ + @Test + void testDeserialiseMouse() { + List tick = new ArrayList<>(); + tick.add(";0,0,0"); + tick.add("LC;0,12,35"); + tick.add("LC,MC;15,25,34"); + + VirtualMouse actual = deserialiseMouse(tick); + + VirtualMouse expected = new VirtualMouse(); + expected.updateFromEvent(VirtualKey.MOUSEMOVED, false, 0, 0, 0); + expected.updateFromEvent(VirtualKey.LC, true, 0, 12, 35); + expected.updateFromEvent(VirtualKey.MC, true, 15, 25, 34); + + assertEquals(expected, actual); + + 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, ()->{ + deserialiseMouse(tick2); + }); + + assertEquals("Line 1, Tick 29, Subtick 2: Mouse functions do not have the correct length", 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, ()->{ + deserialiseMouse(tick3); + }); + + assertEquals("Line 1, Tick 30, Subtick 1: Mouse functions do not have the correct length", t1.getMessage()); + } + + /** + * Test deserialising cameraAngle + */ + @Test + void testDeserialisingCameraAngle() { + List tick = new ArrayList<>(); + tick.add("19;-202.74799"); + tick.add("11.1241500;-2.799"); + tick.add("17.3;-202.79"); + + VirtualCameraAngle actual = deserialiseCameraAngle(tick); + + VirtualCameraAngle expected = new VirtualCameraAngle(); + expected.set(0, 0); + expected.updateFromEvent(19F, -202.74799F); + expected.updateFromEvent(11.1241500F - 19F, -2.799F + 202.74799F); + expected.updateFromEvent(17.3F - 11.1241500F, -202.79F + 2.799F); + + assertEquals(expected, actual); + } + + /** + * Test isNumeric + */ + @Test + void testIsNumeric() { + assertTrue(isNumeric("12")); + assertTrue(isNumeric("-12")); + assertFalse(isNumeric("-145.23")); + assertTrue(isNumeric(Long.toString(Integer.MAX_VALUE + 1L))); + } + + /** + * Test isFloat + */ + @Test + void testIsFloat() { + assertTrue(isFloat("12")); + assertTrue(isFloat("-12")); + assertTrue(isFloat("-145.23")); + assertTrue(isFloat(Long.toString(Integer.MAX_VALUE + 1L))); + } + + @Test + void testParseInt() { + int actual = parseInt("testParseInt", "12"); + assertEquals(12, actual); + + this.currentTick = 13; + this.currentSubtick = 1; + Throwable t = assertThrows(PlaybackLoadException.class, ()->{ + parseInt("testParseInt", "12.1"); + }); + + assertEquals("Line 1, Tick 13, Subtick 1: Can't parse integer in testParseInt", t.getMessage()); + assertEquals(NumberFormatException.class, t.getCause().getClass()); + assertEquals("For input string: \"12.1\"", t.getCause().getMessage()); + } + + @Test + void testParseFloat() { + float actual = parseFloat("testParseFloat", "12.1"); + assertEquals(12.1f, actual); + + this.currentTick = 15; + this.currentSubtick = 6; + Throwable t = assertThrows(PlaybackLoadException.class, ()->{ + parseFloat("testParseFloat", "12.123h"); + }); + + assertEquals("Line 1, Tick 15, Subtick 6: Can't parse float in testParseFloat", t.getMessage()); + assertEquals(NumberFormatException.class, t.getCause().getClass()); + assertEquals("For input string: \"12.123h\"", t.getCause().getMessage()); + } + + @Test + void testDeserialiseRelativeInt() { + int actual = deserialiseRelativeInt("testParseRelativeInt", "12", null); + assertEquals(12, actual); + + actual = deserialiseRelativeInt("test", "~2", 14); + assertEquals(16, actual); + + this.currentTick = 23; + this.currentSubtick = 11; + Throwable t = assertThrows(PlaybackLoadException.class, ()->{ + deserialiseRelativeInt("testParseRelativeInt", "~12", null); + }); + assertEquals("Line 1, Tick 23, Subtick 11: Can't process relative value ~12 in testParseRelativeInt. Previous value for comparing is not available", t.getMessage()); + } + + @Test + void testDeserialiseRelativeFloat() { + float actual = deserialiseRelativeFloat("testParseRelativeFloat", "12.2", null); + assertEquals(12.2f, actual); + + actual = deserialiseRelativeFloat("test", "~2.4", 14.4f); + assertEquals(16.8f, actual); + + this.currentTick = 20; + this.currentSubtick = 2; + Throwable t = assertThrows(PlaybackLoadException.class, ()->{ + deserialiseRelativeFloat("testParseRelativeFloat", "~12.3", null); + }); + assertEquals("Line 1, Tick 20, Subtick 2: Can't process relative value ~12.3 in testParseRelativeFloat. Previous value for comparing is not available", t.getMessage()); + } + + @Test + void testStringPaddingEven() { + String actual = SerialiserFlavorBase.createCenteredHeading(null, '#', 52); + String expected = "####################################################"; + assertEquals(expected, actual); + } + + @Test + void testStringPaddingOdd() { + String actual = SerialiserFlavorBase.createCenteredHeading(null, '#', 51); + String expected = "###################################################"; + assertEquals(expected, actual); + } + + @Test + void testCenterHeadingEven() { + String actual = SerialiserFlavorBase.createCenteredHeading("TASfile", '#', 52); + String expected = "###################### TASfile #####################"; + assertEquals(expected, actual); + } + + @Test + void testCenterHeadingOdd() { + String actual = SerialiserFlavorBase.createCenteredHeading("TASfile", '#', 51); + String expected = "##################### TASfile #####################"; + assertEquals(expected, actual); + } + + @Test + void testCenterHeadingEvenText() { + String actual = SerialiserFlavorBase.createCenteredHeading("TASfiles", '#', 51); + String expected = "##################### TASfiles ####################"; + assertEquals(expected, actual); + } + + @Test + void testCenterHeadingEvenText2() { + String actual = SerialiserFlavorBase.createCenteredHeading("Keystrokes", '#', 51); + String expected = "#################### Keystrokes ###################"; + assertEquals(expected, actual); + } + + @Test + void testJoinNotEmpty() { + String actual = joinNotEmpty(" ", "Test", "", "Weee", "", "Wow"); + + String expected = "Test Weee Wow"; + + assertEquals(expected, actual); + + List actual2 = new ArrayList<>(); + actual2.add("Test"); + actual2.add(""); + actual2.add("Weee"); + actual2.add(null); + actual2.add("Wow"); + + actual = joinNotEmpty(" ", actual2); + assertEquals(expected, actual); + } + + 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(); + } + + +} diff --git a/src/test/java/tasmod/virtual/VirtualCameraAngleTest.java b/src/test/java/tasmod/virtual/VirtualCameraAngleTest.java index d8773e25..fc77a122 100644 --- a/src/test/java/tasmod/virtual/VirtualCameraAngleTest.java +++ b/src/test/java/tasmod/virtual/VirtualCameraAngleTest.java @@ -51,7 +51,7 @@ void testUpdate() { VirtualCameraAngle actual = new VirtualCameraAngle(0f, 0f, true); - actual.update(x, y); + actual.updateFromEvent(x, y); assertEquals(1f, actual.getPitch()); assertEquals(2f, actual.getYaw()); @@ -64,10 +64,10 @@ void testUpdate() { void testUpdateWithBadPitch() { VirtualCameraAngle actual = new VirtualCameraAngle(0f, 0f, true); - actual.update(-100f, 0f); + actual.updateFromEvent(-100f, 0f); assertEquals(-90f, actual.getPitch()); - actual.update(360f, 0f); + actual.updateFromEvent(360f, 0f); assertEquals(90f, actual.getPitch()); } @@ -82,13 +82,13 @@ void testUpdateWithNull() { VirtualCameraAngle actual = new VirtualCameraAngle(); - actual.update(x, y); + actual.updateFromEvent(x, y); assertEquals(null, actual.getPitch()); assertEquals(null, actual.getYaw()); VirtualCameraAngle actual2 = new VirtualCameraAngle(1f, null); - actual2.update(x, y); + actual2.updateFromEvent(x, y); assertEquals(null, actual.getPitch()); assertEquals(null, actual.getYaw()); @@ -102,7 +102,7 @@ void testSet() { VirtualCameraAngle actual = new VirtualCameraAngle(); actual.set(1f, 2f); - actual.update(1f, 2f); + actual.updateFromEvent(1f, 2f); assertEquals(2f, actual.getPitch()); assertEquals(4f, actual.getYaw()); @@ -115,9 +115,9 @@ void testSet() { void testGetStates() { VirtualCameraAngle test = new VirtualCameraAngle(); test.set(0f, 0f); - test.update(1f, 1f); - test.update(1f, 1f); - test.update(1f, 1f); + test.updateFromEvent(1f, 1f); + test.updateFromEvent(1f, 1f); + test.updateFromEvent(1f, 1f); List actual = new ArrayList<>(); @@ -147,8 +147,8 @@ void testGetStates() { @Test void testCopyFrom() { VirtualCameraAngle expected = new VirtualCameraAngle(0f, 0f, true); - expected.update(1f, 2f); - expected.update(3f, 4f); + expected.updateFromEvent(1f, 2f); + expected.updateFromEvent(3f, 4f); VirtualCameraAngle actual = new VirtualCameraAngle(0f, 0f, true); @@ -176,9 +176,9 @@ void testCopyFrom() { void testClear() { VirtualCameraAngle actual = new VirtualCameraAngle(); actual.set(0f, 0f); - actual.update(1f, 1f); - actual.update(1f, 1f); - actual.update(1f, 1f); + actual.updateFromEvent(1f, 1f); + actual.updateFromEvent(1f, 1f); + actual.updateFromEvent(1f, 1f); actual.clear(); @@ -206,9 +206,9 @@ void testToString() { @Test void testToStringSubticks() { VirtualCameraAngle actual = new VirtualCameraAngle(0f, 0f, true); - actual.update(1f, 2f); - actual.update(3f, 4f); - actual.update(5f, 6f); + actual.updateFromEvent(1f, 2f); + actual.updateFromEvent(3f, 4f); + actual.updateFromEvent(5f, 6f); assertEquals("1.0;2.0\n4.0;6.0\n9.0;12.0", actual.toString()); } diff --git a/src/test/java/tasmod/virtual/VirtualInputEventFiring.java b/src/test/java/tasmod/virtual/VirtualInputEventFiring.java index 2dcc7ada..e93e291a 100644 --- a/src/test/java/tasmod/virtual/VirtualInputEventFiring.java +++ b/src/test/java/tasmod/virtual/VirtualInputEventFiring.java @@ -28,8 +28,8 @@ static void beforeAll() { }; EventCopy copy = (keyboard)-> { VirtualKeyboard newkeyboard = new VirtualKeyboard(); - newkeyboard.update(VirtualKey.A, true, 'a'); - newkeyboard.update(VirtualKey.D, true, 'd'); + newkeyboard.updateFromEvent(VirtualKey.A, true, 'a'); + newkeyboard.updateFromEvent(VirtualKey.D, true, 'd'); keyboard.deepCopyFrom(newkeyboard); }; EventListenerRegistry.register(clear, copy); @@ -39,8 +39,8 @@ static void beforeAll() { void testClear() { VirtualKeyboard keyboard = new VirtualKeyboard(); - keyboard.update(VirtualKey.W, true, 'w'); - keyboard.update(VirtualKey.S, true, 's'); + keyboard.updateFromEvent(VirtualKey.W, true, 'w'); + keyboard.updateFromEvent(VirtualKey.S, true, 's'); EventListenerRegistry.fireEvent(EventTest.class, keyboard); @@ -52,12 +52,12 @@ void testCopy() { VirtualKeyboard actual = new VirtualKeyboard(); - actual.update(VirtualKey.W, true, 'w'); - actual.update(VirtualKey.S, true, 's'); + actual.updateFromEvent(VirtualKey.W, true, 'w'); + actual.updateFromEvent(VirtualKey.S, true, 's'); VirtualKeyboard expected = new VirtualKeyboard(); - expected.update(VirtualKey.A, true, 'a'); - expected.update(VirtualKey.D, true, 'd'); + expected.updateFromEvent(VirtualKey.A, true, 'a'); + expected.updateFromEvent(VirtualKey.D, true, 'd'); EventListenerRegistry.fireEvent(EventCopy.class, actual); diff --git a/src/test/java/tasmod/virtual/VirtualInputTest.java b/src/test/java/tasmod/virtual/VirtualInputTest.java index 17fbd63f..04084feb 100644 --- a/src/test/java/tasmod/virtual/VirtualInputTest.java +++ b/src/test/java/tasmod/virtual/VirtualInputTest.java @@ -12,9 +12,7 @@ import org.junit.jupiter.api.Test; import com.minecrafttas.mctcommon.events.EventListenerRegistry; -import com.minecrafttas.tasmod.events.EventClient.EventVirtualCameraAngleTick; -import com.minecrafttas.tasmod.events.EventClient.EventVirtualKeyboardTick; -import com.minecrafttas.tasmod.events.EventClient.EventVirtualMouseTick; +import com.minecrafttas.tasmod.events.EventVirtualInput; import com.minecrafttas.tasmod.virtual.VirtualCameraAngle; import com.minecrafttas.tasmod.virtual.VirtualInput; import com.minecrafttas.tasmod.virtual.VirtualKey; @@ -27,9 +25,9 @@ class VirtualInputTest { @BeforeAll static void beforeAll() { - EventVirtualKeyboardTick kb = (keyboard)->null; - EventVirtualMouseTick ms = (mouse)->null; - EventVirtualCameraAngleTick cmra = (cameraangle)->null; + EventVirtualInput.EventVirtualKeyboardTick kb = (keyboard)->null; + EventVirtualInput.EventVirtualMouseTick ms = (mouse)->null; + EventVirtualInput.EventVirtualCameraAngleTick cmra = (cameraangle)->null; EventListenerRegistry.register(kb, ms, cmra); } @@ -54,9 +52,9 @@ void testIsKeyDown() { VirtualMouse preloadedMouse = new VirtualMouse(); VirtualCameraAngle preloadedCameraAngle = new VirtualCameraAngle(0f, 0f); - preloadedKeyboard.update(VirtualKey.W.getKeycode(), true, 'w'); - preloadedMouse.update(VirtualKey.LC.getKeycode(), true, 15, 0, 0); - preloadedCameraAngle.update(1f, 2f); + preloadedKeyboard.updateFromEvent(VirtualKey.W.getKeycode(), true, 'w'); + preloadedMouse.updateFromEvent(VirtualKey.LC.getKeycode(), true, 15, 0, 0); + preloadedCameraAngle.updateFromEvent(1f, 2f); VirtualInput input = new VirtualInput(LOGGER, preloadedKeyboard, preloadedMouse, preloadedCameraAngle); @@ -87,9 +85,9 @@ void testPreloadedConstructor() { VirtualMouse preloadedMouse = new VirtualMouse(); VirtualCameraAngle preloadedCameraAngle = new VirtualCameraAngle(0f, 0f); - preloadedKeyboard.update(VirtualKey.W.getKeycode(), true, 'w'); - preloadedMouse.update(VirtualKey.LC.getKeycode(), true, 15, 0, 0); - preloadedCameraAngle.update(1f, 2f); + preloadedKeyboard.updateFromEvent(VirtualKey.W.getKeycode(), true, 'w'); + preloadedMouse.updateFromEvent(VirtualKey.LC.getKeycode(), true, 15, 0, 0); + preloadedCameraAngle.updateFromEvent(1f, 2f); VirtualInput virtual = new VirtualInput(LOGGER, preloadedKeyboard, preloadedMouse, preloadedCameraAngle); @@ -162,7 +160,7 @@ void testKeyboardAddPresses() { void testKeyboardRemovePresses() { VirtualKeyboard preloadedKeyboard = new VirtualKeyboard(); - preloadedKeyboard.update(VirtualKey.W.getKeycode(), true, 'w'); + preloadedKeyboard.updateFromEvent(VirtualKey.W.getKeycode(), true, 'w'); VirtualInput virtual = new VirtualInput(LOGGER, preloadedKeyboard, new VirtualMouse(), new VirtualCameraAngle()); virtual.KEYBOARD.updateNextKeyboard(VirtualKey.W.getKeycode(), false, Character.MIN_VALUE); @@ -230,7 +228,7 @@ void testMousePresses() { @Test void testMouseRemovePresses() { VirtualMouse preloadedMouse = new VirtualMouse(); - preloadedMouse.update(VirtualKey.LC.getKeycode(), true, 15, 10, 20); + preloadedMouse.updateFromEvent(VirtualKey.LC.getKeycode(), true, 15, 10, 20); // Load preloaded mouse VirtualInput virtual = new VirtualInput(LOGGER, new VirtualKeyboard(), preloadedMouse, new VirtualCameraAngle()); diff --git a/src/test/java/tasmod/virtual/VirtualKeyboardTest.java b/src/test/java/tasmod/virtual/VirtualKeyboardTest.java index 7432b97a..713a2c8e 100644 --- a/src/test/java/tasmod/virtual/VirtualKeyboardTest.java +++ b/src/test/java/tasmod/virtual/VirtualKeyboardTest.java @@ -146,8 +146,8 @@ void testToString(){ void testToStringSubticks(){ VirtualKeyboard actual = new VirtualKeyboard(); - actual.update(VirtualKey.W.getKeycode(), true, 'w'); - actual.update(VirtualKey.S.getKeycode(), true, 's'); + actual.updateFromEvent(VirtualKey.W.getKeycode(), true, 'w'); + actual.updateFromEvent(VirtualKey.S.getKeycode(), true, 's'); assertEquals("W;w\nW,S;s", actual.toString()); } @@ -225,8 +225,8 @@ void testShallowClone() { @Test void testDeepClone() { VirtualKeyboard expected = new VirtualKeyboard(); - expected.update(VirtualKey.W, true, 'w'); - expected.update(VirtualKey.S, true, 's'); + expected.updateFromEvent(VirtualKey.W, true, 'w'); + expected.updateFromEvent(VirtualKey.S, true, 's'); VirtualKeyboard actual = expected.clone(); @@ -242,13 +242,13 @@ void testMoveFrom(){ VirtualKeyboard moveFrom = new VirtualKeyboard(); VirtualKeyboard actual = new VirtualKeyboard(); - moveFrom.update(VirtualKey.W.getKeycode(), true, 'w'); - moveFrom.update(VirtualKey.A.getKeycode(), true, 'a'); + moveFrom.updateFromEvent(VirtualKey.W.getKeycode(), true, 'w'); + moveFrom.updateFromEvent(VirtualKey.A.getKeycode(), true, 'a'); VirtualKeyboard expected = moveFrom.clone(); - actual.update(VirtualKey.S.getKeycode(), true, 's'); - actual.update(VirtualKey.D.getKeycode(), true, 'd'); + actual.updateFromEvent(VirtualKey.S.getKeycode(), true, 's'); + actual.updateFromEvent(VirtualKey.D.getKeycode(), true, 'd'); actual.moveFrom(null); actual.moveFrom(moveFrom); @@ -268,13 +268,13 @@ void testCopyFrom() { VirtualKeyboard copyFrom = new VirtualKeyboard(); VirtualKeyboard actual = new VirtualKeyboard(); - copyFrom.update(VirtualKey.W.getKeycode(), true, 'w'); - copyFrom.update(VirtualKey.A.getKeycode(), true, 'a'); + copyFrom.updateFromEvent(VirtualKey.W.getKeycode(), true, 'w'); + copyFrom.updateFromEvent(VirtualKey.A.getKeycode(), true, 'a'); VirtualKeyboard expected = copyFrom.clone(); - actual.update(VirtualKey.S.getKeycode(), true, 's'); - actual.update(VirtualKey.D.getKeycode(), true, 'd'); + actual.updateFromEvent(VirtualKey.S.getKeycode(), true, 's'); + actual.updateFromEvent(VirtualKey.D.getKeycode(), true, 'd'); actual.copyFrom(null); actual.copyFrom(copyFrom); @@ -292,8 +292,8 @@ void testCopyFrom() { @Test void testUpdate(){ VirtualKeyboard actual = new VirtualKeyboard(); - actual.update(VirtualKey.W.getKeycode(), true, 'w'); - actual.update(VirtualKey.A.getKeycode(), true, 'A'); + actual.updateFromEvent(VirtualKey.W.getKeycode(), true, 'w'); + actual.updateFromEvent(VirtualKey.A.getKeycode(), true, 'A'); List expected = new ArrayList<>(); expected.add(new VirtualKeyboard(new HashSet(Arrays.asList(VirtualKey.W.getKeycode())), Arrays.asList('w'))); @@ -309,7 +309,7 @@ void testUpdate(){ void testUpdateOnSubtick() { VirtualKeyboard actual = new VirtualKeyboard(new LinkedHashSet<>(), new ArrayList<>(), null, false); - actual.update(VirtualKey.W.getKeycode(), true, 'w'); + actual.updateFromEvent(VirtualKey.W.getKeycode(), true, 'w'); } /** @@ -334,7 +334,7 @@ void testGetVirtualEventsPress() { VirtualKeyboard unpressed = new VirtualKeyboard(); VirtualKeyboard pressed = new VirtualKeyboard(); - pressed.update(VirtualKey.W.getKeycode(), true, 'w'); + pressed.updateFromEvent(VirtualKey.W.getKeycode(), true, 'w'); // Load actual with the events Queue actual = new ConcurrentLinkedQueue<>(); @@ -354,7 +354,7 @@ void testGetVirtualEventsUnpress() { VirtualKeyboard unpressed = new VirtualKeyboard(); VirtualKeyboard pressed = new VirtualKeyboard(); - pressed.update(VirtualKey.W.getKeycode(), true, 'w'); + pressed.updateFromEvent(VirtualKey.W.getKeycode(), true, 'w'); // Load actual with the events Queue actual = new ConcurrentLinkedQueue<>(); @@ -372,9 +372,9 @@ void testGetVirtualEventsUnpress() { @Test void testClear(){ VirtualKeyboard pressed = new VirtualKeyboard(); - pressed.update(VirtualKey.W.getKeycode(), true, 'w'); - pressed.update(VirtualKey.S.getKeycode(), true, 's'); - pressed.update(VirtualKey.A.getKeycode(), true, 'a'); + pressed.updateFromEvent(VirtualKey.W.getKeycode(), true, 'w'); + pressed.updateFromEvent(VirtualKey.S.getKeycode(), true, 's'); + pressed.updateFromEvent(VirtualKey.A.getKeycode(), true, 'a'); pressed.clear(); @@ -408,9 +408,9 @@ void testRepeatEvents(){ int keycode = VirtualKey.BACK.getKeycode(); // Update the keyboard multiple times with the same value - testKb.update(keycode, true, Character.MIN_VALUE, true); - testKb.update(keycode, true, Character.MIN_VALUE, true); - testKb.update(keycode, true, Character.MIN_VALUE, true); + testKb.updateFromEvent(keycode, true, Character.MIN_VALUE, true); + testKb.updateFromEvent(keycode, true, Character.MIN_VALUE, true); + testKb.updateFromEvent(keycode, true, Character.MIN_VALUE, true); Queue actual = new ConcurrentLinkedQueue<>(); // Fill "actual" with VirtualKeyboardEvents @@ -434,9 +434,9 @@ void testRepeatEventsFail(){ int keycode = VirtualKey.BACK.getKeycode(); // Update the keyboard multiple times with the same value. - testKb.update(keycode, true, Character.MIN_VALUE, false); - testKb.update(keycode, true, Character.MIN_VALUE, false); - testKb.update(keycode, true, Character.MIN_VALUE, false); + testKb.updateFromEvent(keycode, true, Character.MIN_VALUE, false); + testKb.updateFromEvent(keycode, true, Character.MIN_VALUE, false); + testKb.updateFromEvent(keycode, true, Character.MIN_VALUE, false); Queue actual = new ConcurrentLinkedQueue<>(); // Fill "actual" with VirtualKeyboardEvents diff --git a/src/test/java/tasmod/virtual/VirtualMouseTest.java b/src/test/java/tasmod/virtual/VirtualMouseTest.java index 90fd0f4c..127fbb26 100644 --- a/src/test/java/tasmod/virtual/VirtualMouseTest.java +++ b/src/test/java/tasmod/virtual/VirtualMouseTest.java @@ -126,8 +126,8 @@ void testToString() { @Test void testToStringSubtick() { VirtualMouse actual = new VirtualMouse(); - actual.update(VirtualKey.LC.getKeycode(), true, 10, 100, 120); - actual.update(VirtualKey.MC.getKeycode(), true, 0, 12, 3); + actual.updateFromEvent(VirtualKey.LC.getKeycode(), true, 10, 100, 120); + actual.updateFromEvent(VirtualKey.MC.getKeycode(), true, 0, 12, 3); assertEquals("LC;10,100,120\nLC,MC;0,12,3", actual.toString()); } @@ -194,8 +194,8 @@ void testShallowClone() { @Test void testDeepClone() { VirtualMouse expected = new VirtualMouse(); - expected.update(VirtualKey.LC, true, 15, 0, 0); - expected.update(VirtualKey.MOUSEMOVED, true, 0, 0, 0); + expected.updateFromEvent(VirtualKey.LC, true, 15, 0, 0); + expected.updateFromEvent(VirtualKey.MOUSEMOVED, true, 0, 0, 0); VirtualMouse actual = expected.clone(); @@ -211,13 +211,13 @@ void testMoveFrom() { VirtualMouse moveFrom = new VirtualMouse(); VirtualMouse actual = new VirtualMouse(); - moveFrom.update(VirtualKey.LC.getKeycode(), true, 0, 0, 0); - moveFrom.update(VirtualKey.MOUSEMOVED.getKeycode(), false, 120, 10, 20); + moveFrom.updateFromEvent(VirtualKey.LC.getKeycode(), true, 0, 0, 0); + moveFrom.updateFromEvent(VirtualKey.MOUSEMOVED.getKeycode(), false, 120, 10, 20); VirtualMouse expected = moveFrom.clone(); - actual.update(VirtualKey.MBUTTON12.getKeycode(), true, 0, 0, 0); - actual.update(VirtualKey.MOUSEMOVED.getKeycode(), true, -120, -10, -10); + actual.updateFromEvent(VirtualKey.MBUTTON12.getKeycode(), true, 0, 0, 0); + actual.updateFromEvent(VirtualKey.MOUSEMOVED.getKeycode(), true, -120, -10, -10); actual.moveFrom(null); @@ -242,13 +242,13 @@ void testCopyFrom() { VirtualMouse copyFrom = new VirtualMouse(); VirtualMouse actual = new VirtualMouse(); - copyFrom.update(VirtualKey.LC.getKeycode(), true, 0, 0, 0); - copyFrom.update(VirtualKey.MOUSEMOVED.getKeycode(), false, 120, 10, 20); + copyFrom.updateFromEvent(VirtualKey.LC.getKeycode(), true, 0, 0, 0); + copyFrom.updateFromEvent(VirtualKey.MOUSEMOVED.getKeycode(), false, 120, 10, 20); VirtualMouse expected = copyFrom.clone(); - actual.update(VirtualKey.MBUTTON12.getKeycode(), true, 0, 0, 0); - actual.update(VirtualKey.MOUSEMOVED.getKeycode(), true, -120, -10, -10); + actual.updateFromEvent(VirtualKey.MBUTTON12.getKeycode(), true, 0, 0, 0); + actual.updateFromEvent(VirtualKey.MOUSEMOVED.getKeycode(), true, -120, -10, -10); actual.copyFrom(null); @@ -271,8 +271,8 @@ void testCopyFrom() { @Test void testUpdate() { VirtualMouse actual = new VirtualMouse(); - actual.update(VirtualKey.LC.getKeycode(), true, -30, 118, 42); - actual.update(VirtualKey.MOUSEMOVED.getKeycode(), false, 0, 23, 144); + actual.updateFromEvent(VirtualKey.LC.getKeycode(), true, -30, 118, 42); + actual.updateFromEvent(VirtualKey.MOUSEMOVED.getKeycode(), false, 0, 23, 144); List expected = new ArrayList<>(); expected.add(new VirtualMouse(new HashSet(Arrays.asList(VirtualKey.LC.getKeycode())), -30, 118, 42)); @@ -304,7 +304,7 @@ void testGetVirtualEventsPress() { VirtualMouse unpressed = new VirtualMouse(); VirtualMouse pressed = new VirtualMouse(); - pressed.update(VirtualKey.LC.getKeycode(), true, 15, 10, 12); + pressed.updateFromEvent(VirtualKey.LC.getKeycode(), true, 15, 10, 12); // Load actual with the events Queue actual = new ConcurrentLinkedQueue<>(); @@ -325,7 +325,7 @@ void testGetVirtualEventsUnpress() { VirtualMouse unpressed = new VirtualMouse(); VirtualMouse pressed = new VirtualMouse(); - pressed.update(VirtualKey.LC.getKeycode(), true, 15, 10, 12); + pressed.updateFromEvent(VirtualKey.LC.getKeycode(), true, 15, 10, 12); // Load actual with the events Queue actual = new ConcurrentLinkedQueue<>(); @@ -346,8 +346,8 @@ void testSameUpdate() { VirtualMouse unpressed = new VirtualMouse(); VirtualMouse pressed = new VirtualMouse(); - pressed.update(VirtualKey.LC.getKeycode(), true, 0, 10, 12); - pressed.update(VirtualKey.LC.getKeycode(), true, 0, 10, 12); + pressed.updateFromEvent(VirtualKey.LC.getKeycode(), true, 0, 10, 12); + pressed.updateFromEvent(VirtualKey.LC.getKeycode(), true, 0, 10, 12); // Load actual with the events Queue actual = new ConcurrentLinkedQueue<>(); @@ -368,8 +368,8 @@ void testScrollWheelDifferent() { VirtualMouse unpressed = new VirtualMouse(); VirtualMouse pressed = new VirtualMouse(); - pressed.update(VirtualKey.LC.getKeycode(), true, 15, 10, 12); - pressed.update(VirtualKey.LC.getKeycode(), true, 15, 10, 12); + pressed.updateFromEvent(VirtualKey.LC.getKeycode(), true, 15, 10, 12); + pressed.updateFromEvent(VirtualKey.LC.getKeycode(), true, 15, 10, 12); // Load actual with the events Queue actual = new ConcurrentLinkedQueue<>(); @@ -390,8 +390,8 @@ void testCursorXDifferent() { VirtualMouse unpressed = new VirtualMouse(); VirtualMouse pressed = new VirtualMouse(); - pressed.update(VirtualKey.LC.getKeycode(), true, 0, 10, 12); - pressed.update(VirtualKey.LC.getKeycode(), true, 0, 11, 12); + pressed.updateFromEvent(VirtualKey.LC.getKeycode(), true, 0, 10, 12); + pressed.updateFromEvent(VirtualKey.LC.getKeycode(), true, 0, 11, 12); // Load actual with the events Queue actual = new ConcurrentLinkedQueue<>(); @@ -412,8 +412,8 @@ void testCursorYDifferent() { VirtualMouse unpressed = new VirtualMouse(); VirtualMouse pressed = new VirtualMouse(); - pressed.update(VirtualKey.LC.getKeycode(), true, 0, 10, 12); - pressed.update(VirtualKey.LC.getKeycode(), true, 0, 10, 120); + pressed.updateFromEvent(VirtualKey.LC.getKeycode(), true, 0, 10, 12); + pressed.updateFromEvent(VirtualKey.LC.getKeycode(), true, 0, 10, 120); // Load actual with the events Queue actual = new ConcurrentLinkedQueue<>(); diff --git a/src/test/resources/betaflavor/.gitkeep b/src/test/resources/betaflavor/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/src/test/resources/metadata/.gitkeep b/src/test/resources/metadata/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/src/test/resources/serialiser/.gitkeep b/src/test/resources/serialiser/.gitkeep new file mode 100644 index 00000000..e69de29b