diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 80512b33..5832f754 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,7 +19,7 @@ jobs: - name: Setup Gradle uses: gradle/actions/setup-gradle@v3 with: - gradle-version: 8.6 + gradle-version: 8.10.2 - name: Build TASmod with Gradle run: gradle build - name: Upload Test Report diff --git a/.github/workflows/buildandupload.yml b/.github/workflows/buildandupload.yml index 6440866e..7e34c890 100644 --- a/.github/workflows/buildandupload.yml +++ b/.github/workflows/buildandupload.yml @@ -20,7 +20,7 @@ jobs: - name: Setup Gradle uses: gradle/actions/setup-gradle@v3 with: - gradle-version: 8.6 + gradle-version: 8.10.2 - name: Build TASmod with Gradle run: gradle build - name: Upload artifact diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..6bb9cb2d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,31 @@ +name: Create Release +on: + release: + types: [published] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup Java + uses: actions/setup-java@v4 + with: + java-version: '22' + distribution: 'temurin' + architecture: x64 + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + with: + gradle-version: 8.10.2 + - name: Setup workspace + run: gradle build -Prelease=true + - name: Upload assets + uses: softprops/action-gh-release@v2 + with: + files: 'build/libs/!(-@(dev|sources|javadoc|all)).jar' + - name: Publish 1.12.2 + uses: Kir-Antipov/mc-publish@v3.3 + with: + files: 'build/libs/*-1.12.2-*!(*-@(dev|sources|javadoc|all)).jar' + modrinth-token: ${{ secrets.MODRINTH_TOKEN }} + curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }} diff --git a/build.gradle b/build.gradle index 9a6b90ea..dc23f8f3 100644 --- a/build.gradle +++ b/build.gradle @@ -3,22 +3,28 @@ plugins { id 'fabric-loom' version "${loom_version}" // legacy looming (loom plugin improvements) id 'legacy-looming' version "${loom_version}" + id 'com.palantir.git-version' version '3.1.0' } // set basic properties -version = project.version +def hash = "" +if(project.release=="false") { + hash = "-SNAPSHOT_"+versionDetails().gitHash.substring(0,7) +} +version = project.version+hash group = project.group -// compile for java 8 -sourceCompatibility = targetCompatibility = JavaVersion.VERSION_1_8 +java { + // compile for java 8 + sourceCompatibility = targetCompatibility = 8 +} loom { // set access widener accessWidenerPath = file('src/main/resources/tasmod.accesswidener') // add log4jconfig log4jConfigs.from(file('src/main/resources/log4j.xml')) - } // dependency repositories @@ -52,8 +58,6 @@ dependencies { testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0' } - - // task for downloading KillTheRng //task downloadKTRNG(type: Copy) { //group 'tasmod' @@ -62,7 +66,7 @@ dependencies { //into 'run/mods/' //} -compileJava{ +compileJava { options.release = 8 } @@ -70,7 +74,7 @@ compileJava{ processResources { inputs.property "version", project.version inputs.property "mcversion", project.minecraft_version - + filesMatching("fabric.mod.json") { expand 'mod_url': project.mod_url, 'name': project.mod_name, 'mod_version': project.version, 'mod_description': project.mod_description, 'mod_sources': project.mod_sources, 'mod_email': project.mod_email } diff --git a/gradle.properties b/gradle.properties index 8ff73a8a..01525f8f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,8 +3,8 @@ org.gradle.jvmargs=-Xmx3G # Fabric properties minecraft_version=1.12.2 -loader_version=0.15.9 -loom_version=1.6-SNAPSHOT +loader_version=0.16.9 +loom_version=1.8-SNAPSHOT # Mod properties mod_name=Tool-Assisted Speedrun Mod @@ -16,4 +16,5 @@ mod_email=scribble@minecrafttas.com # TASmod properties group=com.minecrafttas artifact=TASmod-1.12.2 -version=Beta1.0-SNAPSHOT +version=Beta1.0 +release=false diff --git a/src/main/java/com/minecrafttas/mctcommon/Configuration.java b/src/main/java/com/minecrafttas/mctcommon/Configuration.java index e8ad5c83..686f3e63 100644 --- a/src/main/java/com/minecrafttas/mctcommon/Configuration.java +++ b/src/main/java/com/minecrafttas/mctcommon/Configuration.java @@ -1,19 +1,11 @@ package com.minecrafttas.mctcommon; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.InvalidPropertiesFormatException; -import java.util.LinkedHashMap; -import java.util.List; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Properties; -import com.minecrafttas.mctcommon.Configuration.ConfigOptions; -import com.minecrafttas.mctcommon.registry.AbstractRegistry; -import com.minecrafttas.mctcommon.registry.Registerable; +import com.minecrafttas.mctcommon.ConfigurationRegistry.ConfigOptions; +import com.minecrafttas.mctcommon.file.AbstractDataFile; /** * A very simple configuration class @@ -21,96 +13,33 @@ * @author Scribble */ -public class Configuration extends AbstractRegistry { +public class Configuration extends AbstractDataFile { - private File file; + private ConfigurationRegistry registry; - private Properties properties; - - private String comment; - - public Configuration(String comment, File configFile) { - super("Configuration", new LinkedHashMap<>()); - - file = configFile; - this.comment = comment; + public Configuration(String comment, Path configFile, ConfigurationRegistry registry) { + super(configFile, "config", comment); + this.registry = registry; } - 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; - } - - configRegistry.remove(registryObject); - } - - public void load() { - if (file.exists()) { - properties = loadInner(); + public void loadFromXML() { + if (Files.exists(file)) { + loadFromXML(file); } - if (properties == null || !file.exists()) { + if (properties == null || !Files.exists(file)) { properties = generateDefault(); - save(); - } - } - - private Properties loadInner() { - FileInputStream fis; - Properties newProp = new Properties(); - try { - fis = new FileInputStream(file); - newProp.loadFromXML(fis); - fis.close(); - } catch (InvalidPropertiesFormatException e) { - MCTCommon.LOGGER.error("The config file could not be read", e); - return null; - } catch (FileNotFoundException e) { - MCTCommon.LOGGER.warn("No config file found: {}", file); - return null; - } catch (IOException e) { - MCTCommon.LOGGER.error("An error occured while reading the config", e); - return null; - } - return newProp; - } - - public void save() { - save(file); - } - - public void save(File file) { - try { - FileOutputStream fos = new FileOutputStream(file); - properties.storeToXML(fos, comment, "UTF-8"); - fos.close(); - } catch (IOException e) { - e.printStackTrace(); + saveToXML(); } } + /** + * Generates the default property list from the values provided in {@link #registry} + * @return The default property list + */ public Properties generateDefault() { Properties newProperties = new Properties(); - configRegistry.forEach((configOption)->{ + registry.getConfigRegistry().forEach((configOption) -> { newProperties.put(configOption.getConfigKey(), configOption.getDefaultValue()); }); return newProperties; @@ -121,6 +50,7 @@ public String get(ConfigOptions configOption) { } public int getInt(ConfigOptions configOption) { + // TODO Add config exception or something... NumberFormatExceptions all around... return Integer.parseInt(get(configOption)); } @@ -133,11 +63,11 @@ public boolean has(ConfigOptions configOption) { } public void set(ConfigOptions configOption, String value) { - if(properties == null) { + if (properties == null) { throw new NullPointerException("Config needs to be loaded first, before trying to set a value"); } properties.setProperty(configOption.getConfigKey(), value); - save(); + saveToXML(); } public void set(ConfigOptions configOption, int value) { @@ -156,13 +86,6 @@ public void reset(ConfigOptions configOption) { public void delete(ConfigOptions configOption) { properties.remove(configOption); - save(); - } - - public interface ConfigOptions extends Registerable { - - public String getDefaultValue(); - - public String getConfigKey(); + saveToXML(); } } diff --git a/src/main/java/com/minecrafttas/mctcommon/ConfigurationRegistry.java b/src/main/java/com/minecrafttas/mctcommon/ConfigurationRegistry.java new file mode 100644 index 00000000..c5f1c459 --- /dev/null +++ b/src/main/java/com/minecrafttas/mctcommon/ConfigurationRegistry.java @@ -0,0 +1,66 @@ +package com.minecrafttas.mctcommon; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; + +import com.minecrafttas.mctcommon.ConfigurationRegistry.ConfigOptions; +import com.minecrafttas.mctcommon.registry.AbstractRegistry; +import com.minecrafttas.mctcommon.registry.Registerable; + +public class ConfigurationRegistry extends AbstractRegistry { + + protected final List configRegistry = new ArrayList<>(); + + public ConfigurationRegistry() { + super("Configuration", new LinkedHashMap<>()); + } + + @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; + } + + configRegistry.remove(registryObject); + } + + public List getConfigRegistry() { + return configRegistry; + } + + /** + *

Interface for registering your own options in the TASmod config + * + * @see com.minecrafttas.tasmod.registries.TASmodConfig TASmodConfig + * @author Scribble + */ + public interface ConfigOptions extends Registerable { + /** + * @return The config key name that is stored in the file + */ + public String getConfigKey(); + + /** + * @return The default value that is used if the config key doesn't exist yet + */ + public String getDefaultValue(); + } +} diff --git a/src/main/java/com/minecrafttas/mctcommon/MCTCommon.java b/src/main/java/com/minecrafttas/mctcommon/MCTCommon.java index 718fedf4..b9451446 100644 --- a/src/main/java/com/minecrafttas/mctcommon/MCTCommon.java +++ b/src/main/java/com/minecrafttas/mctcommon/MCTCommon.java @@ -7,13 +7,13 @@ public class MCTCommon { public static final Logger LOGGER = LogManager.getLogger("MCTCommon"); - + public static final Marker Event = MarkerManager.getMarker("Event"); - + public static final Marker Server = MarkerManager.getMarker("Server"); - + public static final Marker Client = MarkerManager.getMarker("Client"); - + public static final Marker Timeout = MarkerManager.getMarker("Timeout"); } diff --git a/src/main/java/com/minecrafttas/mctcommon/events/EventListenerRegistry.java b/src/main/java/com/minecrafttas/mctcommon/events/EventListenerRegistry.java index 774ca47f..0a42ea10 100644 --- a/src/main/java/com/minecrafttas/mctcommon/events/EventListenerRegistry.java +++ b/src/main/java/com/minecrafttas/mctcommon/events/EventListenerRegistry.java @@ -1,11 +1,13 @@ package com.minecrafttas.mctcommon.events; -import org.apache.commons.lang3.ClassUtils; - import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.ClassUtils; /** * Registry for making objects available to listen for events.
@@ -94,7 +96,8 @@ public static void register(EventBase eventListener) { if (eventListener == null) { throw new NullPointerException("Tried to register a packethandler with value null"); } - for (Class type : eventListener.getClass().getInterfaces()) { + + for (Class type : searchForInterfaces(eventListener.getClass())) { if (EventBase.class.isAssignableFrom(type)) { // If a new event type is being registered, add a new arraylist @@ -107,6 +110,18 @@ public static void register(EventBase eventListener) { } } + private static Class[] searchForInterfaces(Class clazz) { + if (clazz == null) { + return new Class[] {}; + } + Class[] interfaces = clazz.getInterfaces(); + Class superclass = clazz.getSuperclass(); + if (superclass != null && superclass != Object.class) { + interfaces = ArrayUtils.addAll(interfaces, searchForInterfaces(superclass)); + } + return interfaces; + } + /** * Registers multiple objects to be an event listener. The objects must * implement an event extending {@link EventBase} @@ -119,6 +134,18 @@ public static void register(EventBase... eventListeners) { } } + /** + * Registers multiple objects to be an event listener. The objects must + * implement an event extending {@link EventBase} + * + * @param eventListeners The event listeners to register + */ + public static void register(List eventListeners) { + for (EventBase eventListener : eventListeners) { + register(eventListener); + } + } + /** * Unregisters an object from being an event listener. * @@ -128,7 +155,7 @@ public static void unregister(EventBase eventListener) { if (eventListener == null) { throw new NullPointerException("Tried to unregister a packethandler with value null"); } - for (Class type : eventListener.getClass().getInterfaces()) { + for (Class type : searchForInterfaces(eventListener.getClass())) { if (EventBase.class.isAssignableFrom(type)) { ArrayList registryList = EVENTLISTENER_REGISTRY.get(type); if (registryList != null) { @@ -153,6 +180,17 @@ public static void unregister(EventBase... eventListeners) { } } + /** + * Unregisters multiple objects from being an event listener. + * + * @param eventListener The event listeners to unregister + */ + public static void unregister(List eventListeners) { + for (EventBase eventListener : eventListeners) { + unregister(eventListener); + } + } + /** * Fires an event without parameters * diff --git a/src/main/java/com/minecrafttas/mctcommon/file/AbstractDataFile.java b/src/main/java/com/minecrafttas/mctcommon/file/AbstractDataFile.java new file mode 100644 index 00000000..9929075d --- /dev/null +++ b/src/main/java/com/minecrafttas/mctcommon/file/AbstractDataFile.java @@ -0,0 +1,233 @@ +package com.minecrafttas.mctcommon.file; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Type; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.InvalidPropertiesFormatException; +import java.util.Map.Entry; +import java.util.Properties; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.minecrafttas.mctcommon.MCTCommon; + +public abstract class AbstractDataFile { + + /** + * The save location of this data file + */ + protected final Path file; + + /** + * The name of this data file, used in logging + */ + protected final String name; + /** + * The comment stored in the data file, to help recognize the file + */ + protected final String comment; + + /** + * The properties of this data file. + */ + protected Properties properties; + + /** + * Creates an abstract data file and creates it's directory if it doesn't exist + * @param file The {@link #file save location} of the data file + * @param name The {@link #name} of the data file, used in logging + * @param comment The {@link #comment} in the data file + */ + protected AbstractDataFile(Path file, String name, String comment) { + this.file = file; + this.name = name; + this.comment = comment; + this.properties = new Properties(); + + createDirectory(file); + } + + /** + * Creates the directory for the file if it doesn't exist + * @param file The file to create the directory for + */ + protected void createDirectory(Path file) { + try { + Files.createDirectories(file.getParent()); + } catch (IOException e) { + MCTCommon.LOGGER.catching(e); + } + } + + public void load() { + if (Files.exists(file)) { + load(file); + } + } + + public void load(Path file) { + InputStream fis; + Properties newProp = new Properties(); + try { + fis = Files.newInputStream(file); + newProp.load(fis); + fis.close(); + } catch (InvalidPropertiesFormatException e) { + MCTCommon.LOGGER.error("The {} file could not be read", name, e); + return; + } catch (FileNotFoundException e) { + MCTCommon.LOGGER.warn("No {} file found: {}", name, file); + return; + } catch (IOException e) { + MCTCommon.LOGGER.error("An error occured while reading the {} file", file, e); + return; + } + this.properties = newProp; + } + + /** + * Loads the xml {@link #file} into {@link #properties} if it exists + */ + public void loadFromXML() { + if (Files.exists(file)) { + loadFromXML(file); + } + } + + /** + * @param file The xml file to load into {@link #properties} + */ + public void loadFromXML(Path file) { + InputStream fis; + Properties newProp = new Properties(); + try { + fis = Files.newInputStream(file); + newProp.loadFromXML(fis); + fis.close(); + } catch (InvalidPropertiesFormatException e) { + MCTCommon.LOGGER.error("The {} file could not be read", name, e); + return; + } catch (FileNotFoundException e) { + MCTCommon.LOGGER.warn("No {} file found: {}", name, file); + return; + } catch (IOException e) { + MCTCommon.LOGGER.error("An error occured while reading the {} file", file, e); + return; + } + this.properties = newProp; + } + + public void loadFromJson() { + loadFromJson(file); + } + + public void loadFromJson(Path file) { + //@formatter:off + Gson json = new GsonBuilder() + .registerTypeAdapter(Properties.class, new PropertiesDeserializer()) + .create(); + //@formatter:on + + String in; + try { + in = new String(Files.readAllBytes(file)); + } catch (IOException e) { + MCTCommon.LOGGER.catching(e); + return; + } + + properties = json.fromJson(in, Properties.class); + } + + public void save() { + this.save(file); + } + + public void save(Path file) { + try { + OutputStream fos = Files.newOutputStream(file); + properties.store(fos, comment); + fos.close(); + } catch (IOException e) { + MCTCommon.LOGGER.catching(e); + } + } + + /** + * Saves the {@link #properties} to the {@link #file} location + */ + public void saveToXML() { + this.saveToXML(file); + } + + /** + * Saves the {@link #properties} to a specified file + * @param file The file to save the {@link #properties} to + */ + public void saveToXML(Path file) { + try { + OutputStream fos = Files.newOutputStream(file); + properties.storeToXML(fos, comment, "UTF-8"); + fos.close(); + } catch (IOException e) { + MCTCommon.LOGGER.catching(e); + } + } + + public void saveToJson() { + saveToJson(file); + } + + public void saveToJson(Path file) { + //@formatter:off + Gson json = new GsonBuilder() + .registerTypeAdapter(Properties.class, new PropertiesSerializer()) + .setPrettyPrinting() + .create(); + //@formatter:on + try { + String element = json.toJson(properties); + Files.write(file, element.getBytes()); + } catch (IOException e) { + MCTCommon.LOGGER.catching(e); + } + } + + public class PropertiesSerializer implements JsonSerializer { + + @Override + public JsonElement serialize(Properties src, Type typeOfSrc, JsonSerializationContext context) { + JsonObject obj = new JsonObject(); + src.forEach((key, val) -> { + obj.addProperty((String) key, (String) val); + }); + return obj; + } + } + + public class PropertiesDeserializer implements JsonDeserializer { + + @Override + public Properties deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + Properties properties = new Properties(); + JsonObject obj = json.getAsJsonObject(); + for (Entry elem : obj.entrySet()) { + String key = elem.getKey(); + String val = elem.getValue().getAsString(); + properties.put(key, val); + } + return properties; + } + } +} diff --git a/src/main/java/com/minecrafttas/tasmod/TASmod.java b/src/main/java/com/minecrafttas/tasmod/TASmod.java index 57899423..a495dfad 100644 --- a/src/main/java/com/minecrafttas/tasmod/TASmod.java +++ b/src/main/java/com/minecrafttas/tasmod/TASmod.java @@ -1,6 +1,5 @@ package com.minecrafttas.tasmod; -import java.io.File; import java.io.IOException; import org.apache.logging.log4j.LogManager; @@ -26,18 +25,18 @@ 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.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.savestates.storage.SavestateMotionStorage; import com.minecrafttas.tasmod.tickratechanger.TickrateChangerServer; import com.minecrafttas.tasmod.ticksync.TickSyncServer; import com.minecrafttas.tasmod.util.LoggerMarkers; import com.minecrafttas.tasmod.util.Scheduler; import net.fabricmc.api.ModInitializer; +import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.impl.FabricLoaderImpl; import net.minecraft.server.MinecraftServer; @@ -48,21 +47,24 @@ */ public class TASmod implements ModInitializer, EventServerInit, EventServerStop { - private static MinecraftServer serverInstance; - public static final Logger LOGGER = LogManager.getLogger("TASmod"); + public static String version = "dev"; + + private static MinecraftServer serverInstance; + public static PlaybackControllerServer playbackControllerServer = new PlaybackControllerServer();; public static SavestateHandlerServer savestateHandlerServer; - public static KillTheRNGHandler ktrngHandler; + // public static KillTheRNGHandler ktrngHandler; public static TickrateChangerServer tickratechanger; public static TickSyncServer ticksyncServer; public static final Scheduler tickSchedulerServer = new Scheduler(); + public static final Scheduler gameLoopSchedulerServer = new Scheduler(); public static Server server; @@ -81,12 +83,18 @@ public void onInitialize() { LOGGER.info("Initializing TASmod"); + String modVersion = FabricLoader.getInstance().getModContainer("tasmod").get().getMetadata().getVersion().getFriendlyString(); + + if (!"${mod_version}".equals(modVersion)) { + version = modVersion; + } + // 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); @@ -95,17 +103,20 @@ public void onInitialize() { EventListenerRegistry.register(this); EventListenerRegistry.register(ticksyncServer); EventListenerRegistry.register(tickratechanger); - EventListenerRegistry.register(ktrngHandler); + // EventListenerRegistry.register(ktrngHandler); // Register packet handlers LOGGER.info(LoggerMarkers.Networking, "Registering network handlers"); PacketHandlerRegistry.register(ticksyncServer); PacketHandlerRegistry.register(tickratechanger); - PacketHandlerRegistry.register(ktrngHandler); + // PacketHandlerRegistry.register(ktrngHandler); PacketHandlerRegistry.register(playbackControllerServer); PacketHandlerRegistry.register(startPositionMetadataExtension); PacketHandlerRegistry.register(tabCompletionUtils); PacketHandlerRegistry.register(commandFileCommand); + SavestateMotionStorage motionStorage = new SavestateMotionStorage(); + PacketHandlerRegistry.register(motionStorage); + EventListenerRegistry.register(motionStorage); } @Override @@ -129,16 +140,9 @@ public void onServerInit(MinecraftServer 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); - try { - new SavestateTrackerFile(new File(savestateDirectory, server.getFolderName() + "-info.txt")); // TODO Ew, remove - } catch (IOException e) { - e.printStackTrace(); - } - savestateHandlerServer = new SavestateHandlerServer(server, LOGGER); PacketHandlerRegistry.register(savestateHandlerServer); + PacketHandlerRegistry.register(savestateHandlerServer.getPlayerHandler()); if (!server.isDedicatedServer()) { TASmod.tickratechanger.ticksPerSecond = 0F; @@ -169,6 +173,8 @@ public void onServerStop(MinecraftServer mcserver) { if (savestateHandlerServer != null) { PacketHandlerRegistry.unregister(savestateHandlerServer); // Unregistering the savestatehandler, as a new instance is registered in onServerStart() + PacketHandlerRegistry.unregister(savestateHandlerServer.getPlayerHandler()); + savestateHandlerServer = null; } } diff --git a/src/main/java/com/minecrafttas/tasmod/TASmodClient.java b/src/main/java/com/minecrafttas/tasmod/TASmodClient.java index b8aaa122..04382371 100644 --- a/src/main/java/com/minecrafttas/tasmod/TASmodClient.java +++ b/src/main/java/com/minecrafttas/tasmod/TASmodClient.java @@ -4,11 +4,14 @@ import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Arrays; import org.apache.logging.log4j.Level; import com.minecrafttas.mctcommon.Configuration; +import com.minecrafttas.mctcommon.ConfigurationRegistry; import com.minecrafttas.mctcommon.KeybindManager; import com.minecrafttas.mctcommon.LanguageManager; import com.minecrafttas.mctcommon.events.EventClient.EventClientInit; @@ -33,6 +36,7 @@ import com.minecrafttas.tasmod.registries.TASmodKeybinds; import com.minecrafttas.tasmod.registries.TASmodPackets; import com.minecrafttas.tasmod.savestates.SavestateHandlerClient; +import com.minecrafttas.tasmod.savestates.handlers.SavestatePlayerHandler; import com.minecrafttas.tasmod.tickratechanger.TickrateChangerClient; import com.minecrafttas.tasmod.ticksync.TickSyncClient; import com.minecrafttas.tasmod.util.LoggerMarkers; @@ -50,41 +54,40 @@ import net.minecraft.client.multiplayer.ServerData; import net.minecraft.server.MinecraftServer; -public class TASmodClient implements ClientModInitializer, EventClientInit, EventPlayerJoinedClientSide, EventOpenGui{ - +public class TASmodClient implements ClientModInitializer, EventClientInit, EventPlayerJoinedClientSide, EventOpenGui { public static VirtualInput virtual; public static TickSyncClient ticksyncClient; - + 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"; public static InfoHud hud; - + public static ShieldDownloader shieldDownloader; - + public static TickrateChangerClient tickratechanger = new TickrateChangerClient(); - + public static Scheduler gameLoopSchedulerClient = new Scheduler(); - + public static Scheduler tickSchedulerClient = new Scheduler(); - + public static Scheduler openMainMenuScheduler = new Scheduler(); - + public static Configuration config; - + public static LoadingScreenHandler loadingScreenHandler; - + public static KeybindManager keybindManager; - + public static SavestateHandlerClient savestateHandlerClient = new SavestateHandlerClient(); - + public static Client client; - + public static CreditsMetadataExtension creditsMetadataExtension = new CreditsMetadataExtension(); - + public static StartpositionMetadataExtension startpositionMetadataExtension = new StartpositionMetadataExtension(); /** * The container where all inputs get stored during recording or stored and @@ -93,28 +96,30 @@ public class TASmodClient implements ClientModInitializer, EventClientInit, Even public static PlaybackControllerClient controller = new PlaybackControllerClient(); public static void createTASDir() { - File tasDir=new File(tasdirectory); - if(!tasDir.exists()) { + File tasDir = new File(tasdirectory); + if (!tasDir.exists()) { tasDir.mkdir(); } } - + public static void createSavestatesDir() { - File savestateDir=new File(savestatedirectory); - if(!savestateDir.exists()) { + File savestateDir = new File(savestatedirectory); + if (!savestateDir.exists()) { savestateDir.mkdir(); } } @Override public void onInitializeClient() { - + LanguageManager.registerMod("tasmod"); + registerConfigValues(); + loadConfig(Minecraft.getMinecraft()); - - virtual=new VirtualInput(LOGGER); - + + virtual = new VirtualInput(LOGGER); + // Initialize InfoHud hud = new InfoHud(); // Initialize shield downloader @@ -125,27 +130,28 @@ public void onInitializeClient() { ticksyncClient = new TickSyncClient(); // Initialize keybind manager keybindManager = new KeybindManager(VirtualKeybindings::isKeyDownExceptTextfield); - + registerEventListeners(); - + registerNetworkPacketHandlers(); - + // Starting local server instance try { - TASmod.server = new Server(TASmod.networkingport-1, TASmodPackets.values()); + 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); + PacketHandlerRegistry.register(savestateHandlerClient); + PacketHandlerRegistry.register(new SavestatePlayerHandler(null)); } private void registerEventListeners() { @@ -155,8 +161,8 @@ private void registerEventListeners() { EventListenerRegistry.register(loadingScreenHandler); EventListenerRegistry.register(ticksyncClient); EventListenerRegistry.register(keybindManager); - EventListenerRegistry.register((EventOpenGui)(gui -> { - if(gui instanceof GuiMainMenu) { + EventListenerRegistry.register((EventOpenGui) (gui -> { + if (gui instanceof GuiMainMenu) { openMainMenuScheduler.runAllTasks(); } return gui; @@ -164,26 +170,28 @@ private void registerEventListeners() { EventListenerRegistry.register(controller); EventListenerRegistry.register(creditsMetadataExtension); EventListenerRegistry.register(startpositionMetadataExtension); - + EventListenerRegistry.register(desyncMonitorFileCommandExtension); - + EventListenerRegistry.register(TASmodAPIRegistry.PLAYBACK_METADATA); EventListenerRegistry.register(TASmodAPIRegistry.PLAYBACK_FILE_COMMAND); + EventListenerRegistry.register(new LoggerMarkers()); + EventListenerRegistry.register(savestateHandlerClient); } - + @Override public void onClientInit(Minecraft mc) { registerKeybindings(mc); registerPlaybackMetadata(mc); registerSerialiserFlavors(mc); registerFileCommands(); - + createTASDir(); createSavestatesDir(); } boolean waszero; - + boolean isLoading; @Override @@ -191,29 +199,28 @@ public void onPlayerJoinedClientSide(EntityPlayerSP player) { Minecraft mc = Minecraft.getMinecraft(); ServerData data = mc.getCurrentServerData(); MinecraftServer server = TASmod.getServerInstance(); - + String ip = null; int port; boolean local; - if(server!=null) { + if (server != null) { ip = "localhost"; - port = TASmod.networkingport-1; + port = TASmod.networkingport - 1; local = true; } else { ip = data.serverIP.split(":")[0]; port = TASmod.networkingport; local = false; } - + String connectedIP = null; try { connectedIP = client.getRemote(); } catch (IOException e) { e.printStackTrace(); } - - - if(!(ip+":"+port).equals(connectedIP)) { // TODO Clean this up. Make TASmodNetworkHandler out of this... Maybe with Permission system? + + 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(); @@ -222,7 +229,7 @@ public void onPlayerJoinedClientSide(EntityPlayerSP player) { } final String IP = ip; final int PORT = port; - gameLoopSchedulerClient.add(()->{ + gameLoopSchedulerClient.add(() -> { try { // connect to server and authenticate client = new Client(IP, PORT, TASmodPackets.values(), mc.getSession().getUsername(), local); @@ -253,17 +260,17 @@ 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()) { + if (configAddress != null && !configAddress.isEmpty()) { String[] ipSplit = configAddress.split(":"); IP = ipSplit[0]; try { @@ -274,7 +281,7 @@ private void initializeCustomPacketHandler() { PORT = TASmod.networkingport - 1; } } - + try { // connect to server and authenticate client = new Client(IP, PORT, TASmodPackets.values(), mc.getSession().getUsername(), true); @@ -282,45 +289,54 @@ private void initializeCustomPacketHandler() { 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 static final ConfigurationRegistry CONFIG_REGISTRY = new ConfigurationRegistry(); + + private void registerConfigValues() { + CONFIG_REGISTRY.register(TASmodConfig.values()); + } + private void loadConfig(Minecraft mc) { - File configDir = new File(mc.mcDataDir, "config"); - if(!configDir.exists()) { - configDir.mkdir(); + Path configDir = mc.mcDataDir.toPath().resolve("config"); + if (!Files.exists(configDir)) { + try { + Files.createDirectory(configDir); + } catch (IOException e) { + LOGGER.catching(e); + } } - config = new Configuration("TASmod configuration", new File(configDir, "tasmod.cfg")); - config.register(TASmodConfig.values()); - config.load(); - config.save(); + config = new Configuration("TASmod configuration", configDir.resolve("tasmod.cfg"), CONFIG_REGISTRY); + config.loadFromXML(); + config.saveToXML(); } } diff --git a/src/main/java/com/minecrafttas/tasmod/commands/CommandRecord.java b/src/main/java/com/minecrafttas/tasmod/commands/CommandRecord.java index 56f58e36..7934574f 100644 --- a/src/main/java/com/minecrafttas/tasmod/commands/CommandRecord.java +++ b/src/main/java/com/minecrafttas/tasmod/commands/CommandRecord.java @@ -43,9 +43,9 @@ public void execute(MinecraftServer server, ICommandSender sender, String[] args } if (args.length < 1) { TASmod.playbackControllerServer.toggleRecording(); - TASmod.tickSchedulerServer.add(() ->{ - TASmod.ktrngHandler.broadcastStartSeed(); - }); +// TASmod.tickSchedulerServer.add(() ->{ +// TASmod.ktrngHandler.broadcastStartSeed(); +// }); } else if (args.length > 1) { sender.sendMessage(new TextComponentString(TextFormatting.RED + "Too many arguments. " + getUsage(sender))); } diff --git a/src/main/java/com/minecrafttas/tasmod/commands/CommandSavestate.java b/src/main/java/com/minecrafttas/tasmod/commands/CommandSavestate.java index 0d997da9..7abd7317 100644 --- a/src/main/java/com/minecrafttas/tasmod/commands/CommandSavestate.java +++ b/src/main/java/com/minecrafttas/tasmod/commands/CommandSavestate.java @@ -37,7 +37,6 @@ public int getRequiredPermissionLevel() { @Override public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { - sender.sendMessage(new TextComponentString("Savestates might not work correctly at the moment... rewriting a lot of core features, which might break this...")); if (args.length == 0) { sendHelp(sender); } else if (args.length >= 1) { @@ -63,7 +62,7 @@ public void execute(MinecraftServer server, ICommandSender sender, String[] args } else if (args.length == 3) { int args1 = processIndex(args[1]); int args2 = processIndex(args[2]); - int count = (args2+1) - args1; + int count = (args2 + 1) - args1; TextComponentString confirm = new TextComponentString(TextFormatting.YELLOW + "Are you sure you want to delete " + count + (count == 1 ? " savestate? " : " savestates? ") + TextFormatting.GREEN + "[YES]"); confirm.getStyle().setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, String.format("/savestate deletDis %s %s", args[1], args[2]))); sender.sendMessage(confirm); @@ -105,22 +104,21 @@ private void sendHelp(ICommandSender sender, int i) throws CommandException { if (i > 3) { throw new CommandException("This help page doesn't exist (yet?)", new Object[] {}); } - if(i==1) { - sender.sendMessage(new TextComponentString(TextFormatting.GOLD+"-------------------Savestate Help 1--------------------\n"+TextFormatting.RESET - + "Makes a backup of the minecraft world you are currently playing.\n\n" - + "The mod will keep track of the number of savestates you made in the 'current index' number which is currently "+TextFormatting.AQUA+currentIndex+TextFormatting.RESET - + String.format(". If you make a new savestate via %s/savestate save%s or by pressing %sJ%s by default, ", TextFormatting.AQUA, TextFormatting.RESET, TextFormatting.AQUA, TextFormatting.RESET) - + "the current index will increase by one. " - + String.format("If you load a savestate with %s/savestate load%s or %sK%s by default, it will load the savestate at the current index.\n", TextFormatting.AQUA, TextFormatting.RESET, TextFormatting.AQUA, TextFormatting.RESET))); - }else if(i==2) { + if (i == 1) { + sender.sendMessage(new TextComponentString(TextFormatting.GOLD + "-------------------Savestate Help 1--------------------\n" + TextFormatting.RESET + + "Makes a backup of the minecraft world you are currently playing.\n\n" + + "The mod will keep track of the number of savestates you made in the 'current index' number which is currently " + TextFormatting.AQUA + currentIndex + TextFormatting.RESET + + String.format(". If you make a new savestate via %s/savestate save%s or by pressing %sJ%s by default, ", TextFormatting.AQUA, TextFormatting.RESET, TextFormatting.AQUA, TextFormatting.RESET) + + "the current index will increase by one. " + + String.format("If you load a savestate with %s/savestate load%s or %sK%s by default, it will load the savestate at the current index.\n", TextFormatting.AQUA, TextFormatting.RESET, TextFormatting.AQUA, TextFormatting.RESET))); + } else if (i == 2) { sender.sendMessage(new TextComponentString(String.format("%1$s-------------------Savestate Help 2--------------------\n" + "You can load or save savestates in different indexes by specifying the index: %3$s/savestate %4$s %5$s%2$s\n" + "This will change the %5$scurrent index%2$s to the index you specified.\n\n" + "So, if you have the savestates %3$s1, 2, 3%2$s and your %5$scurrent index%2$s is %3$s3%2$s, %3$s/savestate %4$sload %5$s2%2$s will load the second savestate and will set the %5$scurrent index%2$s to %3$s2%2$s.\n" + "But if you savestate again you will OVERWRITE the third savestate, so keep that in mind!!\n\n" - + "The savestate at index 0 will be the savestate when you started the TAS recording and can't be deleted or overwritten with this command" - , /*1*/TextFormatting.GOLD, /*2*/TextFormatting.RESET, /*3*/TextFormatting.AQUA, /*4*/TextFormatting.GREEN, /*5*/TextFormatting.YELLOW))); - }else if(i==3) { + + "The savestate at index 0 will be the savestate when you started the TAS recording and can't be deleted or overwritten with this command", /*1*/TextFormatting.GOLD, /*2*/TextFormatting.RESET, /*3*/TextFormatting.AQUA, /*4*/TextFormatting.GREEN, /*5*/TextFormatting.YELLOW))); + } else if (i == 3) { sender.sendMessage(new TextComponentString(String.format("%1$s-------------------Savestate Help 3--------------------\n%2$s" + "%3$s/savestate %4$ssave%2$s - Make a savestate at the next index\n" + "%3$s/savestate %4$ssave%5$s %2$s - Make a savestate at the specified index\n" @@ -133,21 +131,21 @@ private void sendHelp(ICommandSender sender, int i) throws CommandException { /*1*/TextFormatting.GOLD, /*2*/TextFormatting.RESET, /*3*/TextFormatting.AQUA, /*4*/TextFormatting.GREEN, /*5*/TextFormatting.YELLOW, /*6*/(currentIndex - 1)))); return; } - TextComponentString nextPage=new TextComponentString(TextFormatting.GOLD+"Click here to go to the next help page ("+(i+1)+")\n"); - nextPage.getStyle().setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/savestate help "+(i+1)+"")); + TextComponentString nextPage = new TextComponentString(TextFormatting.GOLD + "Click here to go to the next help page (" + (i + 1) + ")\n"); + nextPage.getStyle().setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/savestate help " + (i + 1) + "")); sender.sendMessage(nextPage); } @Override public List getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, BlockPos targetPos) { if (args.length == 1) { - return getListOfStringsMatchingLastWord(args, new String[] { "save", "load", "delete", "list", "help"}); + return getListOfStringsMatchingLastWord(args, new String[] { "save", "load", "delete", "list", "help" }); } else if (args.length == 2 && !"list".equals(args[0])) { sender.sendMessage(new TextComponentString("Available indexes: " + TextFormatting.AQUA + TASmod.savestateHandlerServer.getIndexesAsString())); } return super.getTabCompletions(server, sender, args, targetPos); } - + // ====================================================================== private void saveLatest() throws CommandException { diff --git a/src/main/java/com/minecrafttas/tasmod/events/EventClient.java b/src/main/java/com/minecrafttas/tasmod/events/EventClient.java index 8cc771e4..fdd53517 100644 --- a/src/main/java/com/minecrafttas/tasmod/events/EventClient.java +++ b/src/main/java/com/minecrafttas/tasmod/events/EventClient.java @@ -22,6 +22,17 @@ public static interface EventDrawHotbar extends EventBase { public void onDrawHotbar(); } + /** + * Fired when drawing something on screen. Ignores F1 + */ + @FunctionalInterface + public static interface EventDrawHotbarAlways extends EventBase { + /** + * Fired when the gui is drawn on screen. Ignores F1 + */ + public void onDrawHotbarAlways(); + } + /** * Fired at the end of a client tick */ diff --git a/src/main/java/com/minecrafttas/tasmod/events/EventNBT.java b/src/main/java/com/minecrafttas/tasmod/events/EventNBT.java new file mode 100644 index 00000000..b2c7ad01 --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/events/EventNBT.java @@ -0,0 +1,30 @@ +package com.minecrafttas.tasmod.events; + +import com.minecrafttas.mctcommon.events.EventListenerRegistry.EventBase; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.world.storage.WorldInfo; + +public interface EventNBT { + + @FunctionalInterface + public interface EventPlayerRead extends EventBase { + public void onPlayerReadNBT(NBTTagCompound compound, EntityPlayerMP player); + } + + @FunctionalInterface + public interface EventPlayerWrite extends EventBase { + public void onPlayerWriteNBT(NBTTagCompound compound, EntityPlayerMP player); + } + + @FunctionalInterface + public interface EventWorldRead extends EventBase { + public void onWorldReadNBT(NBTTagCompound worldCompound); + } + + @FunctionalInterface + public interface EventWorldWrite extends EventBase { + public NBTTagCompound onWorldWriteNBT(NBTTagCompound compound, WorldInfo worldInfo); + } +} diff --git a/src/main/java/com/minecrafttas/tasmod/events/EventSavestate.java b/src/main/java/com/minecrafttas/tasmod/events/EventSavestate.java index 58791881..70fde1f8 100644 --- a/src/main/java/com/minecrafttas/tasmod/events/EventSavestate.java +++ b/src/main/java/com/minecrafttas/tasmod/events/EventSavestate.java @@ -1,9 +1,12 @@ package com.minecrafttas.tasmod.events; -import java.io.File; +import java.nio.file.Path; import com.minecrafttas.mctcommon.events.EventListenerRegistry.EventBase; +import net.minecraft.client.entity.EntityPlayerSP; +import net.minecraft.server.MinecraftServer; + public interface EventSavestate { /** @@ -19,7 +22,7 @@ interface EventServerSavestate extends EventBase { * @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); + public void onServerSavestate(MinecraftServer server, int index, Path target, Path current); } /** @@ -35,7 +38,7 @@ interface EventServerLoadstate extends EventBase { * @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); + public void onServerLoadstate(MinecraftServer server, int index, Path target, Path current); } /** @@ -49,10 +52,46 @@ interface EventServerCompleteLoadstate extends EventBase { */ public void onServerLoadstateComplete(); } - + + /** + * Fired when saving a savestate + */ @FunctionalInterface interface EventClientSavestate extends EventBase { - + public void onClientSavestate(); } + + /** + * Fired when loading a savestate + */ + @FunctionalInterface + interface EventClientLoadstate extends EventBase { + + public void onClientLoadstate(); + } + + /** + * Fired one tick after a loadstate was carried out + */ + @FunctionalInterface + interface EventClientCompleteLoadstate extends EventBase { + + /** + * Fired one tick after a loadstate was carried out + */ + public void onClientLoadstateComplete(); + } + + /** + * Fired during loadstating, after the player is loaded on the client + */ + @FunctionalInterface + interface EventClientLoadPlayer extends EventBase { + + /** + * Fired during loadstating, after the player is loaded on the client + */ + public void onClientLoadPlayer(EntityPlayerSP player); + } } diff --git a/src/main/java/com/minecrafttas/tasmod/gui/InfoHud.java b/src/main/java/com/minecrafttas/tasmod/gui/InfoHud.java index 20cb8f5c..e3a4f780 100644 --- a/src/main/java/com/minecrafttas/tasmod/gui/InfoHud.java +++ b/src/main/java/com/minecrafttas/tasmod/gui/InfoHud.java @@ -11,6 +11,7 @@ import org.apache.commons.lang3.tuple.Pair; import org.lwjgl.input.Keyboard; +import org.lwjgl.opengl.Display; import org.lwjgl.opengl.GL11; import com.minecrafttas.mctcommon.events.EventClient.EventClientTick; @@ -30,8 +31,8 @@ * any everything can be customized * @author Pancake */ -public class InfoHud extends GuiScreen implements EventClientTick, EventDrawHotbar{ - +public class InfoHud extends GuiScreen implements EventClientTick, EventDrawHotbar { + public static class InfoLabel { public String displayName; public int x; @@ -40,8 +41,7 @@ public static class InfoLabel { public boolean renderRect; public String renderText; private Callable text; - - + public InfoLabel(String displayName, int x, int y, boolean visible, boolean renderRect, Callable text) { this.displayName = displayName; this.visible = visible; @@ -50,7 +50,7 @@ public InfoLabel(String displayName, int x, int y, boolean visible, boolean rend this.renderRect = renderRect; this.text = text; } - + public void tick() { try { renderText = text.call(); @@ -60,45 +60,45 @@ public void tick() { } } } - + /** -1, or the current index in {@link InfoHud#lists} that is being dragged by the mouse */ private int currentlyDraggedIndex = -1; private int xOffset; // drag offsets private int yOffset; - - private int gridSizeX=14; - private int gridSizeY=14; - + + private int gridSizeX = 14; + private int gridSizeY = 14; + public Properties configuration; private boolean resetLayout; public static List lists = new ArrayList<>(); - + private void setDefaults(String string, int y) { setDefaults(string, y, false); } - + private void setDefaults(String string, int y, boolean enabled) { configuration.setProperty(string + "_x", "0"); configuration.setProperty(string + "_y", y + ""); - configuration.setProperty(string + "_visible", enabled?"true":"false"); + configuration.setProperty(string + "_visible", enabled ? "true" : "false"); configuration.setProperty(string + "_rect", "false"); saveConfig(); } - + /** * Returns the object below the mouse */ public void identify(int mouseX, int mouseY) { int index = 0; for (InfoLabel label : lists) { - int x=0; - int y=0; + int x = 0; + int y = 0; try { x = Integer.parseInt(configuration.getProperty(label.displayName + "_x")); y = Integer.parseInt(configuration.getProperty(label.displayName + "_y")); - + Pair newPos = getScreenOffset(x, y, label); - + x = newPos.getLeft(); y = newPos.getRight(); } catch (NumberFormatException e) { @@ -108,7 +108,7 @@ public void identify(int mouseX, int mouseY) { } int w = x + Minecraft.getMinecraft().fontRenderer.getStringWidth(label.renderText); int h = y + 15; - + if (mouseX >= x && mouseX <= w && mouseY >= y && mouseY <= h) { currentlyDraggedIndex = index; xOffset = mouseX - x; @@ -121,8 +121,9 @@ public void identify(int mouseX, int mouseY) { xOffset = -1; yOffset = -1; } - - @Override protected void mouseClicked(int mouseX, int mouseY, int mouseButton) { + + @Override + protected void mouseClicked(int mouseX, int mouseY, int mouseButton) { if (mouseButton == 2) { identify(mouseX, mouseY); if (currentlyDraggedIndex != -1) { @@ -147,74 +148,77 @@ public void identify(int mouseX, int mouseY) { identify(mouseX, mouseY); super.mouseClicked(mouseX, mouseY, mouseButton); } - + @Override protected void mouseReleased(int p_mouseReleased_1_, int p_mouseReleased_2_, int p_mouseReleased_3_) { currentlyDraggedIndex = -1; saveConfig(); super.mouseReleased(p_mouseReleased_1_, p_mouseReleased_2_, p_mouseReleased_3_); } - + @Override protected void mouseClickMove(int mouseX, int mouseY, int k, long millis) { if (currentlyDraggedIndex != -1) { String dragging = lists.get(currentlyDraggedIndex).displayName; - - int mousePosX=mouseX - xOffset; - int mousePosY=mouseY - yOffset; - - if(TASmodClient.virtual.isKeyDown(42)) { - mousePosX=snapToGridX(mousePosX); - mousePosY=snapToGridY(mousePosY); + + int mousePosX = mouseX - xOffset; + int mousePosY = mouseY - yOffset; + + if (TASmodClient.virtual.isKeyDown(42)) { + mousePosX = snapToGridX(mousePosX); + mousePosY = snapToGridY(mousePosY); } - + lists.get(currentlyDraggedIndex).x = mousePosX; lists.get(currentlyDraggedIndex).y = mousePosY; - + configuration.setProperty(dragging + "_x", lists.get(currentlyDraggedIndex).x + ""); configuration.setProperty(dragging + "_y", lists.get(currentlyDraggedIndex).y + ""); } super.mouseClickMove(mouseX, mouseY, k, millis); } - + private int snapToGridX(int x) { return Math.round(x / gridSizeX) * gridSizeX; } - + private int snapToGridY(int y) { return Math.round(y / gridSizeY) * gridSizeY; } - + /** * Saves the Configuration */ private void saveConfig() { - if(!(Minecraft.getMinecraft().currentScreen instanceof InfoHud) || configuration == null) { + if (!(Minecraft.getMinecraft().currentScreen instanceof InfoHud) || configuration == null) { return; } try { File tasmodDir = new File(Minecraft.getMinecraft().mcDataDir, "tasmod"); tasmodDir.mkdir(); File configFile = new File(tasmodDir, "infogui2.cfg"); - if (!configFile.exists()) configFile.createNewFile(); + if (!configFile.exists()) + configFile.createNewFile(); configuration.store(new FileOutputStream(configFile, false), "DO NOT EDIT MANUALLY"); } catch (IOException e) { e.printStackTrace(); } } - + /** * Updates every tick */ @Override public void onClientTick(Minecraft mc) { - if(mc.player!=null) { - if (checkInit()) return; + if (mc.player != null) { + if (checkInit()) + return; } } - + public boolean checkInit() { - if (configuration != null) return false; + if (configuration != null) + return false; /* Check whether already rendered before */ try { configuration = new Properties(); @@ -222,200 +226,253 @@ public boolean checkInit() { File tasmodDir = new File(Minecraft.getMinecraft().mcDataDir, "tasmod"); tasmodDir.mkdir(); File configFile = new File(tasmodDir, "infogui2.cfg"); - if (!configFile.exists()) configFile.createNewFile(); + if (!configFile.exists()) + configFile.createNewFile(); configuration.load(new FileReader(configFile)); - }else { + } else { resetLayout = false; } lists = new ArrayList(); /* ====================== */ String title = "tickrate"; int y = 0; - 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 "Tickrate"; - return String.format("Tickrate: %s", TASmodClient.tickratechanger.ticksPerSecond); - })); - + 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 "Tickrate"; + return String.format("Tickrate: %s", TASmodClient.tickratechanger.ticksPerSecond); + })); + title = "position"; y += 14; - 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 "XYZ"; - return String.format("%.2f %.2f %.2f", Minecraft.getMinecraft().player.posX, Minecraft.getMinecraft().player.posY, Minecraft.getMinecraft().player.posZ); - })); - + 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 "XYZ"; + return String.format("%.2f %.2f %.2f", Minecraft.getMinecraft().player.posX, Minecraft.getMinecraft().player.posY, Minecraft.getMinecraft().player.posZ); + })); + title = "position2"; y += 14; - 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 "Precise XYZ"; - return String.format("%f %f %f", Minecraft.getMinecraft().player.posX, Minecraft.getMinecraft().player.posY, Minecraft.getMinecraft().player.posZ); - })); - + 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 "Precise XYZ"; + return String.format("%s %s %s", Minecraft.getMinecraft().player.posX, Minecraft.getMinecraft().player.posY, Minecraft.getMinecraft().player.posZ); + })); + title = "chunkpos"; y += 14; - 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 "Chunk Position"; - return String.format("%d %d", Minecraft.getMinecraft().player.chunkCoordX, Minecraft.getMinecraft().player.chunkCoordZ); - })); - + 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 "Chunk Position"; + return String.format("%d %d", Minecraft.getMinecraft().player.chunkCoordX, Minecraft.getMinecraft().player.chunkCoordZ); + })); + title = "worldseed"; y += 14; - 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 "Worldseed"; - return "World Seed: " + Minecraft.getMinecraft().world.getWorldInfo().getSeed(); - })); - + 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 "Worldseed"; + return "World Seed: " + Minecraft.getMinecraft().world.getWorldInfo().getSeed(); + })); + y += 14; - - if(TASmod.ktrngHandler.isLoaded()) { - title = "ktrng_randomseed"; - 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 "KTRNG"; - return "RandomSeed: " + TASmod.ktrngHandler.getGlobalSeedClient(); - })); - } - + +// if (TASmod.ktrngHandler.isLoaded()) { +// title = "ktrng_randomseed"; +// 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 "KTRNG"; +// return "RandomSeed: " + TASmod.ktrngHandler.getGlobalSeedClient(); +// })); +// } + title = "facing"; y += 14; - 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 "Facing"; - return String.format("%.2f %.2f", Minecraft.getMinecraft().player.rotationPitch, Minecraft.getMinecraft().player.rotationYaw); - })); - + 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 "Facing"; + return String.format("%.2f %.2f", Minecraft.getMinecraft().player.rotationYaw, Minecraft.getMinecraft().player.rotationPitch); + })); + + title = "camera"; + y += 14; + 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 "Camera"; + return String.format("%.2f %.2f", TASmodClient.virtual.CAMERA_ANGLE.getCurrentYaw(), TASmodClient.virtual.CAMERA_ANGLE.getCurrentPitch()); + })); + title = "cticks"; y += 14; -// 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 "Client Ticks"; -// return "Client Ticks: " + ClientProxy.ticksyncClient.getClienttickcounter(); -// })); - + // 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 "Client Ticks"; + // return "Client Ticks: " + ClientProxy.ticksyncClient.getClienttickcounter(); + // })); + title = "sticks"; y += 14; -// 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 "Server Ticks"; -// return "Server Ticks: " + ClientProxy.ticksyncClient.getServertickcounter(); -// })); - + // 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 "Server Ticks"; + // return "Server Ticks: " + ClientProxy.ticksyncClient.getServertickcounter(); + // })); + title = "nextxyz"; y += 14; -// 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 "Predicted Position"; -// return String.format("%f %f %f", PlayerPositionCalculator.xNew, PlayerPositionCalculator.yNew, PlayerPositionCalculator.zNew); -// })); - + // 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 "Predicted Position"; + // return String.format("%f %f %f", PlayerPositionCalculator.xNew, PlayerPositionCalculator.yNew, PlayerPositionCalculator.zNew); + // })); + title = "state"; y += 14; - 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 "State"; - } else { - TASstate state = TASmodClient.controller.getState(); - ChatFormatting format = ChatFormatting.WHITE; - String out = ""; - if (state == TASstate.PLAYBACK) { - out = "Playback"; - format = ChatFormatting.GREEN; - } else if (state == TASstate.RECORDING) { - out = "Recording"; - format = ChatFormatting.RED; - } else if (state == TASstate.PAUSED) { - out = "Paused"; - format = ChatFormatting.YELLOW; - } else if (state == TASstate.NONE) { - out = ""; - } - return String.format("%s%s", format, out); - } - })); - -// title = "cursor"; -// y += 14; -// 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 "Mouse Position"; -// return String.format("Mouse Cursor: " + TASmodClient.virtual.getNextMouse().getPath().get(0).cursorX + " " + TASmodClient.virtual.getNextMouse().getPath().get(0).cursorY); -// })); TODO Remove? - -// title = "trajectories"; -// y += 14; -// 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 "Trajectories"; -// String message = "Invalid Item"; -// Vec3d vec = TrajectoriesCalculator.calculate(); -// if (vec != null) { -// message = String.format("%.3f %.3f %.3f", vec.x, vec.y, vec.z); -// } -// return String.format("Trajectories: " + message); -// })); - + 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 "State"; + } else { + TASstate state = TASmodClient.controller.getState(); + ChatFormatting format = ChatFormatting.WHITE; + String out = ""; + if (state == TASstate.PLAYBACK) { + out = "Playback"; + format = ChatFormatting.GREEN; + } else if (state == TASstate.RECORDING) { + out = "Recording"; + format = ChatFormatting.RED; + } else if (state == TASstate.PAUSED) { + out = "Paused"; + format = ChatFormatting.YELLOW; + } else if (state == TASstate.NONE) { + out = ""; + } + return String.format("%s%s", format, out); + } + })); + + // title = "cursor"; + // y += 14; + // 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 "Mouse Position"; + // return String.format("Mouse Cursor: " + TASmodClient.virtual.getNextMouse().getPath().get(0).cursorX + " " + TASmodClient.virtual.getNextMouse().getPath().get(0).cursorY); + // })); TODO Remove? + + // title = "trajectories"; + // y += 14; + // 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 "Trajectories"; + // String message = "Invalid Item"; + // Vec3d vec = TrajectoriesCalculator.calculate(); + // if (vec != null) { + // message = String.format("%.3f %.3f %.3f", vec.x, vec.y, vec.z); + // } + // return String.format("Trajectories: " + message); + // })); + title = "velocity"; y += 14; - 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 "Velocity"; - return "Velocity: " + Minecraft.getMinecraft().player.motionX + " " + Minecraft.getMinecraft().player.motionY + " " + Minecraft.getMinecraft().player.motionZ; - })); - + 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 "Velocity"; + return Minecraft.getMinecraft().player.motionX + " " + Minecraft.getMinecraft().player.motionY + " " + Minecraft.getMinecraft().player.motionZ; + })); + title = "desyncstatus"; y += 14; - 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"; - DesyncMonitorFileCommandExtension dMonitor=TASmodClient.desyncMonitorFileCommandExtension; - return dMonitor.getStatus(Minecraft.getMinecraft().player); - })); - + 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"; + DesyncMonitorFileCommandExtension dMonitor = TASmodClient.desyncMonitorFileCommandExtension; + return dMonitor.getStatus(Minecraft.getMinecraft().player); + })); + title = "desyncstatusMotion"; y += 14; - 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"; - DesyncMonitorFileCommandExtension dMonitor=TASmodClient.desyncMonitorFileCommandExtension; - return dMonitor.getMotion(); - })); - + 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"; + DesyncMonitorFileCommandExtension dMonitor = TASmodClient.desyncMonitorFileCommandExtension; + return dMonitor.getMotion(); + })); + title = "desyncstatusPos"; y += 14; - 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"; - DesyncMonitorFileCommandExtension dMonitor=TASmodClient.desyncMonitorFileCommandExtension; - return dMonitor.getPos(); - })); - + 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"; + DesyncMonitorFileCommandExtension dMonitor = TASmodClient.desyncMonitorFileCommandExtension; + return dMonitor.getPos(); + })); + 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 Long.toString(TASmodClient.controller.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 Long.toString(TASmodClient.controller.index()); + })); + y = height - 14; title = "keystrokes"; - 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 "Keystrokes"; - return keystrokes(); - })); - + 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 "Keystrokes"; + return keystrokes(); + })); + } catch (Exception e) { e.printStackTrace(); } return true; } - + /** * Render the Info Hud only */ @@ -428,33 +485,33 @@ public void onDrawHotbar() { // skip rendering of control byte is set if (!TASmodClient.optionsFileCommandExtension.shouldRenderHud() && TASmodClient.controller.isPlayingback()) return; - int xpos=40; - int ypos=190; + int xpos = 40; + int ypos = 190; for (InfoLabel label : lists) { label.tick(); - + int lx = label.x; int ly = label.y; Pair newPos = getScreenOffset(lx, ly, label); - + lx = newPos.getLeft(); ly = newPos.getRight(); - + if (label.visible) { drawRectWithText(label.renderText, lx, ly, label.renderRect); } else if (Minecraft.getMinecraft().currentScreen != null) { if (Minecraft.getMinecraft().currentScreen.getClass().getSimpleName().contains("InfoHud")) { - Minecraft.getMinecraft().fontRenderer.drawStringWithShadow(label.renderText, label.x + 2, label.y + 3, 0x60FFFFFF); + Minecraft.getMinecraft().fontRenderer.drawStringWithShadow(label.renderText, label.x + 2, label.y + 3, 0x60FFFFFF); } } - if(Minecraft.getMinecraft().currentScreen instanceof InfoHud) { - Minecraft.getMinecraft().fontRenderer.drawStringWithShadow("Leftclick to move", width-ypos, xpos- 30, 0x60FF00); - Minecraft.getMinecraft().fontRenderer.drawStringWithShadow("Rightclick to enable", width-ypos, xpos-20, 0x60FF00); - Minecraft.getMinecraft().fontRenderer.drawStringWithShadow("Middleclick to add black background", width-ypos, xpos-10, 0x60FF00); - Minecraft.getMinecraft().fontRenderer.drawStringWithShadow("Hold Shift to snap to grid", width-ypos, xpos, 0x60FF00); + if (Minecraft.getMinecraft().currentScreen instanceof InfoHud) { + Minecraft.getMinecraft().fontRenderer.drawStringWithShadow("Leftclick to move", width - ypos, xpos - 30, 0x60FF00); + Minecraft.getMinecraft().fontRenderer.drawStringWithShadow("Rightclick to enable", width - ypos, xpos - 20, 0x60FF00); + Minecraft.getMinecraft().fontRenderer.drawStringWithShadow("Middleclick to add black background", width - ypos, xpos - 10, 0x60FF00); + Minecraft.getMinecraft().fontRenderer.drawStringWithShadow("Hold Shift to snap to grid", width - ypos, xpos, 0x60FF00); Minecraft.getMinecraft().fontRenderer.drawStringWithShadow("CTRL+Shift+R to reset the layout", width - ypos, xpos + 10, 0xEE8100); - + if (isCtrlKeyDown() && isShiftKeyDown() && TASmodClient.virtual.isKeyDown(Keyboard.KEY_R)) { resetLayout = true; configuration = null; @@ -462,49 +519,53 @@ public void onDrawHotbar() { } } ScaledResolution scaled = new ScaledResolution(Minecraft.getMinecraft()); - drawCenteredString(Minecraft.getMinecraft().fontRenderer, "TASmod is still in development! Major issues may arise!", scaled.getScaledWidth() / 2, scaled.getScaledHeight() - 50, 0xFF8400); -// drawCenteredString(Minecraft.getMinecraft().fontRenderer, Float.toString(TASmod.tickratechanger.ticksPerSecond), scaled.getScaledWidth() / 2, scaled.getScaledHeight() - 36, 0xFFFFFF); + // drawCenteredString(Minecraft.getMinecraft().fontRenderer, "TASmod is still in development! Major issues may arise!", scaled.getScaledWidth() / 2, scaled.getScaledHeight() - 50, 0xFF8400); + drawString(Minecraft.getMinecraft().fontRenderer, TASmod.version, scaled.getScaledWidth() - Minecraft.getMinecraft().fontRenderer.getStringWidth(TASmod.version) - 2, scaled.getScaledHeight() - 10, 0xFFFFFF); + // drawCenteredString(Minecraft.getMinecraft().fontRenderer, Float.toString(TASmod.tickratechanger.ticksPerSecond), scaled.getScaledWidth() / 2, scaled.getScaledHeight() - 36, 0xFFFFFF); } - + /** * Renders a Box with Text in it */ private void drawRectWithText(String text, int x, int y, boolean rect) { - if (rect) drawRect(x, y, x + Minecraft.getMinecraft().fontRenderer.getStringWidth(text) + 4, y + 14, 0x60000000); + if (rect) + drawRect(x, y, x + Minecraft.getMinecraft().fontRenderer.getStringWidth(text) + 4, y + 14, 0x60000000); Minecraft.getMinecraft().fontRenderer.drawStringWithShadow(text, x + 2, y + 3, 0xFFFFFF); GL11.glEnable(3042 /*GL_BLEND*/); } - + private String keystrokes() { -// if (Display.isActive()) { //TODO Update -// String out1 = ""+ChatFormatting.WHITE; -// for (String mouse : TASmodClient.virtual.getCurrentMousePresses()) { -// out1 = out1.concat(mouse + " "); -// } -// out1=out1.concat(""+ChatFormatting.GREEN); -// for (String mouse : TASmodClient.virtual.getNextMousePresses()) { -// out1 = out1.concat(mouse + " "); -// } -// -// String out2 = ""+ChatFormatting.WHITE; -// for (String key : TASmodClient.virtual.getCurrentKeyboardPresses()) { -// out2 = out2.concat(key + " "); -// } -// out2=out2.concat(""+ChatFormatting.GREEN); -// for (String key : TASmodClient.virtual.getNextKeyboardPresses()) { -// out2 = out2.concat(key + " "); -// } -// return out1+out2; -// } - return ""; + + String out1 = "" + ChatFormatting.WHITE; + for (String mouse : TASmodClient.virtual.getCurrentMousePresses()) { + out1 = out1.concat(mouse + " "); + } + if (Display.isActive() || TASmodClient.controller.isPlayingback()) { + out1 = out1.concat("" + ChatFormatting.GREEN); + for (String mouse : TASmodClient.virtual.getNextMousePresses()) { + out1 = out1.concat(mouse + " "); + } + } + + String out2 = "" + ChatFormatting.WHITE; + for (String key : TASmodClient.virtual.getCurrentKeyboardPresses()) { + out2 = out2.concat(key + " "); + } + if (Display.isActive() || TASmodClient.controller.isPlayingback()) { + out2 = out2.concat("" + ChatFormatting.GREEN); + for (String key : TASmodClient.virtual.getNextKeyboardPresses()) { + out2 = out2.concat(key + " "); + } + } + return out1 + out2; } - - private Pair getScreenOffset(int x, int y, InfoLabel label){ + + private Pair getScreenOffset(int x, int y, InfoLabel label) { ScaledResolution scaled = new ScaledResolution(Minecraft.getMinecraft()); - + int marginX = 5; int marginY = 5; - + if (getBBRight(x, label.renderText) > scaled.getScaledWidth()) { int offset = getBBRight(x, label.renderText); x = x - (offset - scaled.getScaledWidth()) - marginX; @@ -514,7 +575,7 @@ private Pair getScreenOffset(int x, int y, InfoLabel label){ int offset = getBBDown(y); y = y - (offset - scaled.getScaledHeight()) - marginY; } - + return Pair.of(x, y); } @@ -525,5 +586,5 @@ private int getBBRight(int x, String text) { private int getBBDown(int y) { return y + 14; } - + } \ No newline at end of file diff --git a/src/main/java/com/minecrafttas/tasmod/ktrng/KillTheRNGHandler.java b/src/main/java/com/minecrafttas/tasmod/ktrng/KillTheRNGHandler.java index b2669838..8b4c53b7 100644 --- a/src/main/java/com/minecrafttas/tasmod/ktrng/KillTheRNGHandler.java +++ b/src/main/java/com/minecrafttas/tasmod/ktrng/KillTheRNGHandler.java @@ -163,7 +163,7 @@ public void setInitialSeed(long initialSeed) { e.printStackTrace(); } } else { - TASmod.ktrngHandler.setGlobalSeedClient(initialSeed); + // TASmod.ktrngHandler.setGlobalSeedClient(initialSeed); } } @@ -190,7 +190,7 @@ public void onServerPacket(PacketID id, ByteBuffer buf, String username) throws case KILLTHERNG_STARTSEED: TASmod.tickSchedulerServer.add(() -> { - TASmod.ktrngHandler.setGlobalSeedServer(seed); + // TASmod.ktrngHandler.setGlobalSeedServer(seed); }); break; diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/MixinInGameHud.java b/src/main/java/com/minecrafttas/tasmod/mixin/MixinInGameHud.java deleted file mode 100644 index 50acb8f2..00000000 --- a/src/main/java/com/minecrafttas/tasmod/mixin/MixinInGameHud.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.minecrafttas.tasmod.mixin; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.Gui; -import net.minecraft.client.gui.GuiIngame; -import net.minecraft.client.gui.ScaledResolution; -import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.util.ResourceLocation; - -@Mixin(GuiIngame.class) -public abstract class MixinInGameHud { - - ResourceLocation potion = new ResourceLocation("tasmod:textures/potion.png"); - - /** - * Renders the potion into the gui - * @param ci - */ - @Inject(method="renderExpBar", at=@At(value="HEAD"), remap = false) - public void mixinRenderExperienceBar(CallbackInfo ci) { - ScaledResolution scaledresolution = new ScaledResolution(Minecraft.getMinecraft()); - Minecraft.getMinecraft().getTextureManager().bindTexture(potion); - int m = (scaledresolution.getScaledWidth() / 2)-6; - int n = scaledresolution.getScaledHeight() - 31 - 19; - int skale=20; - GlStateManager.enableBlend(); - GlStateManager.enableAlpha(); - GlStateManager.color(1, 1, 1, 0.3F); - Gui.drawModalRectWithCustomSizedTexture(m-3, n, 0F, 0F, skale, skale, skale, skale); - GlStateManager.disableBlend(); - GlStateManager.disableAlpha(); - } -} diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/MixinMinecraft.java b/src/main/java/com/minecrafttas/tasmod/mixin/MixinMinecraft.java index a4e77c57..450efdc7 100644 --- a/src/main/java/com/minecrafttas/tasmod/mixin/MixinMinecraft.java +++ b/src/main/java/com/minecrafttas/tasmod/mixin/MixinMinecraft.java @@ -1,8 +1,5 @@ package com.minecrafttas.tasmod.mixin; -import java.io.IOException; - -import com.minecrafttas.mctcommon.events.EventListenerRegistry; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -12,9 +9,9 @@ import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import com.minecrafttas.mctcommon.events.EventListenerRegistry; import com.minecrafttas.tasmod.TASmodClient; import com.minecrafttas.tasmod.events.EventClient.EventClientTickPost; -import com.minecrafttas.tasmod.savestates.SavestateHandlerServer; import com.minecrafttas.tasmod.util.Ducks.SubtickDuck; import net.minecraft.client.Minecraft; @@ -25,12 +22,11 @@ @Mixin(Minecraft.class) public abstract class MixinMinecraft { - // ===================================================================================================================================== @Shadow private GuiScreen currentScreen; - + @Inject(method = "runGameLoop", at = @At(value = "HEAD")) public void injectRunGameLoop(CallbackInfo ci) { TASmodClient.gameLoopSchedulerClient.runAllTasks(); @@ -58,7 +54,7 @@ public void redirectRunTick(Minecraft mc) { TASmodClient.tickratechanger.advanceTick = false; TASmodClient.tickratechanger.changeClientTickrate(0F); } - EventListenerRegistry.fireEvent(EventClientTickPost.class, (Minecraft)(Object)this); + EventListenerRegistry.fireEvent(EventClientTickPost.class, (Minecraft) (Object) this); } @Shadow @@ -75,17 +71,6 @@ public void inject_shutdownMinecraftApplet(CallbackInfo ci) { e.printStackTrace(); } } - - @Inject(method = "runTick", at = @At(value = "HEAD")) - public void injectRunTick(CallbackInfo ci) throws IOException { - if (SavestateHandlerServer.wasLoading) { - SavestateHandlerServer.wasLoading = false; - - if(Minecraft.getMinecraft().player!=null) { //The player can be null when loading a savestate and quitting to the main menu - SavestateHandlerServer.playerLoadSavestateEventClient(); // TODO Replace with event - } - } - } @ModifyConstant(method = "runTickMouse", constant = @Constant(longValue = 200L)) public long fixMouseWheel(long twohundredLong) { diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/MixinMinecraftServer.java b/src/main/java/com/minecrafttas/tasmod/mixin/MixinMinecraftServer.java index 7146a961..3ff76f6e 100644 --- a/src/main/java/com/minecrafttas/tasmod/mixin/MixinMinecraftServer.java +++ b/src/main/java/com/minecrafttas/tasmod/mixin/MixinMinecraftServer.java @@ -3,7 +3,6 @@ import java.util.Queue; import java.util.concurrent.FutureTask; -import com.minecrafttas.mctcommon.events.EventListenerRegistry; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -11,8 +10,10 @@ import org.spongepowered.asm.mixin.injection.ModifyConstant; import org.spongepowered.asm.mixin.injection.Redirect; +import com.minecrafttas.mctcommon.events.EventListenerRegistry; import com.minecrafttas.tasmod.TASmod; import com.minecrafttas.tasmod.events.EventServer.EventServerTickPost; +import com.minecrafttas.tasmod.savestates.SavestateHandlerServer.SavestateState; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; @@ -33,7 +34,7 @@ public long modifyMSPT(long fiftyLong) { @Redirect(method = "run", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;tick()V")) public void redirectTick(MinecraftServer server) { - + } @Shadow @@ -61,35 +62,35 @@ public long redirectMathMax(long oneLong, long i) { @Shadow private boolean serverIsRunning; - + @Redirect(method = "run", at = @At(value = "INVOKE", target = "Ljava/lang/Thread;sleep(J)V")) public void redirectThreadSleep(long msToTick) { - + /* The server should tick if: * (shouldTick in ticksync is true OR there are no players connected to the custom server) AND the tickrate is not zero. That or advance tick is true*/ - if( (TASmod.ticksyncServer.shouldTick() && TASmod.tickratechanger.ticksPerSecond != 0) || TASmod.tickratechanger.advanceTick) { + if ((TASmod.ticksyncServer.shouldTick() && TASmod.tickratechanger.ticksPerSecond != 0) || TASmod.tickratechanger.advanceTick) { long timeBeforeTick = System.currentTimeMillis(); - + this.tick(); TASmod.tickSchedulerServer.runAllTasks(); - + if (TASmod.tickratechanger.advanceTick) { TASmod.tickratechanger.changeServerTickrate(0F); TASmod.tickratechanger.advanceTick = false; } - EventListenerRegistry.fireEvent(EventServerTickPost.class, (MinecraftServer)(Object)this); - + EventListenerRegistry.fireEvent(EventServerTickPost.class, (MinecraftServer) (Object) this); + long tickDuration = System.currentTimeMillis() - timeBeforeTick; - + // ================================================== - + try { Thread.sleep(Math.max(1L, TASmod.tickratechanger.millisecondsPerTick - tickDuration)); } catch (InterruptedException e) { e.printStackTrace(); } } else { // This is when the server should not tick... This is to ensure network tick stuff is working - if(TASmod.tickratechanger.ticksPerSecond == 0) { + if (TASmod.tickratechanger.ticksPerSecond == 0) { faketick++; if (faketick >= 50) { faketick = 0; @@ -99,20 +100,25 @@ public void redirectThreadSleep(long msToTick) { } } } - + try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } - - synchronized (this.futureTaskQueue) { - while (!this.futureTaskQueue.isEmpty()) { - try { - ((FutureTask) this.futureTaskQueue.poll()).run(); - } catch (Throwable var9) { - var9.printStackTrace(); + + TASmod.gameLoopSchedulerServer.runAllTasks(); + + boolean stopTaskQueue = TASmod.savestateHandlerServer != null && TASmod.savestateHandlerServer.state == SavestateState.LOADING; + if (!stopTaskQueue) { + synchronized (this.futureTaskQueue) { + while (!this.futureTaskQueue.isEmpty()) { + try { + ((FutureTask) this.futureTaskQueue.poll()).run(); + } catch (Throwable var9) { + var9.printStackTrace(); + } } } } diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/events/MixinEntityPlayerMP.java b/src/main/java/com/minecrafttas/tasmod/mixin/events/MixinEntityPlayerMP.java new file mode 100644 index 00000000..ee6bd9af --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/mixin/events/MixinEntityPlayerMP.java @@ -0,0 +1,33 @@ +package com.minecrafttas.tasmod.mixin.events; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import com.minecrafttas.mctcommon.events.EventListenerRegistry; +import com.minecrafttas.tasmod.events.EventNBT; +import com.minecrafttas.tasmod.events.EventNBT.EventPlayerRead; +import com.minecrafttas.tasmod.events.EventNBT.EventPlayerWrite; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.nbt.NBTTagCompound; + +/** + * Implements {@link EventPlayerRead} and {@link EventPlayerWrite} events + * + * @author Scribble + */ +@Mixin(EntityPlayerMP.class) +public class MixinEntityPlayerMP { + + @Inject(method = "readEntityFromNBT", at = @At(value = "RETURN")) + public void readClientMotion(NBTTagCompound compound, CallbackInfo ci) { + EventListenerRegistry.fireEvent(EventNBT.EventPlayerRead.class, compound, (EntityPlayerMP) (Object) this); + } + + @Inject(method = "writeEntityToNBT", at = @At(value = "RETURN")) + public void writeClientMotion(NBTTagCompound compound, CallbackInfo ci) { + EventListenerRegistry.fireEvent(EventNBT.EventPlayerWrite.class, compound, (EntityPlayerMP) (Object) this); + } +} diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/events/MixinEntityRenderer.java b/src/main/java/com/minecrafttas/tasmod/mixin/events/MixinEntityRenderer.java new file mode 100644 index 00000000..f61e3de6 --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/mixin/events/MixinEntityRenderer.java @@ -0,0 +1,35 @@ +package com.minecrafttas.tasmod.mixin.events; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import com.minecrafttas.mctcommon.events.EventListenerRegistry; +import com.minecrafttas.tasmod.events.EventClient.EventDrawHotbarAlways; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.ScaledResolution; +import net.minecraft.client.renderer.EntityRenderer; +import net.minecraft.client.renderer.GlStateManager; + +@Mixin(EntityRenderer.class) +public class MixinEntityRenderer { + + @Shadow + private Minecraft mc; + + @Inject(method = "updateCameraAndRender", at = @At(value = "INVOKE", target = "Lnet/minecraft/profiler/Profiler;endStartSection(Ljava/lang/String;)V")) + public void inject_onRenderGameOverlay(CallbackInfo ci) { + ScaledResolution scaledResolution = new ScaledResolution(this.mc); + GlStateManager.clear(256); + GlStateManager.matrixMode(5889); + GlStateManager.loadIdentity(); + GlStateManager.ortho(0.0, scaledResolution.getScaledWidth_double(), scaledResolution.getScaledHeight_double(), 0.0, 1000.0, 3000.0); + GlStateManager.matrixMode(5888); + GlStateManager.loadIdentity(); + GlStateManager.translate(0.0F, 0.0F, -2000.0F); + EventListenerRegistry.fireEvent(EventDrawHotbarAlways.class); + } +} diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/events/MixinGuiIngame.java b/src/main/java/com/minecrafttas/tasmod/mixin/events/MixinGuiIngame.java index ebeeb56f..dfea37f3 100644 --- a/src/main/java/com/minecrafttas/tasmod/mixin/events/MixinGuiIngame.java +++ b/src/main/java/com/minecrafttas/tasmod/mixin/events/MixinGuiIngame.java @@ -1,18 +1,18 @@ package com.minecrafttas.tasmod.mixin.events; -import com.minecrafttas.mctcommon.events.EventListenerRegistry; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import com.minecrafttas.mctcommon.events.EventListenerRegistry; import com.minecrafttas.tasmod.events.EventClient.EventDrawHotbar; import net.minecraft.client.gui.GuiIngame; @Mixin(GuiIngame.class) public class MixinGuiIngame { - + @Inject(method = "renderHotbar", at = @At("HEAD")) public void inject_renderHotbar(CallbackInfo ci) { EventListenerRegistry.fireEvent(EventDrawHotbar.class); diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/events/MixinGuiMainMenu.java b/src/main/java/com/minecrafttas/tasmod/mixin/events/MixinGuiMainMenu.java index ef5ea361..6a3e847b 100644 --- a/src/main/java/com/minecrafttas/tasmod/mixin/events/MixinGuiMainMenu.java +++ b/src/main/java/com/minecrafttas/tasmod/mixin/events/MixinGuiMainMenu.java @@ -14,7 +14,7 @@ public class MixinGuiMainMenu extends GuiScreen { @Redirect(method = "actionPerformed", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;displayGuiScreen(Lnet/minecraft/client/gui/GuiScreen;)V", ordinal = 3)) - public void redirectOpenGuiMultiplayer(Minecraft mc, GuiScreen screen) { + public void redirect_openGuiMultiplayer(Minecraft mc, GuiScreen screen) { mc.displayGuiScreen(new GuiMultiplayerWarn((GuiMainMenu)(Object)this)); } } diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/events/MixinSaveFormatOld.java b/src/main/java/com/minecrafttas/tasmod/mixin/events/MixinSaveFormatOld.java new file mode 100644 index 00000000..f1ab8f2b --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/mixin/events/MixinSaveFormatOld.java @@ -0,0 +1,21 @@ +package com.minecrafttas.tasmod.mixin.events; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyArgs; + +import com.minecrafttas.mctcommon.events.EventListenerRegistry; +import com.minecrafttas.tasmod.events.EventNBT; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.world.storage.SaveFormatOld; + +@Mixin(SaveFormatOld.class) +public class MixinSaveFormatOld { + + @ModifyArgs(method = "getWorldData", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/datafix/DataFixer;process(Lnet/minecraft/util/datafix/FixTypes;Lnet/minecraft/nbt/NBTTagCompound;)Lnet/minecraft/nbt/NBTTagCompound;")) + public NBTTagCompound modifyargs_getWorldData(NBTTagCompound compound) { + EventListenerRegistry.fireEvent(EventNBT.EventWorldRead.class, compound); + return compound; + } +} diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/events/MixinSaveHandler.java b/src/main/java/com/minecrafttas/tasmod/mixin/events/MixinSaveHandler.java new file mode 100644 index 00000000..acd856db --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/mixin/events/MixinSaveHandler.java @@ -0,0 +1,30 @@ +package com.minecrafttas.tasmod.mixin.events; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyArg; + +import com.llamalad7.mixinextras.sugar.Share; +import com.llamalad7.mixinextras.sugar.ref.LocalRef; +import com.minecrafttas.mctcommon.events.EventListenerRegistry; +import com.minecrafttas.tasmod.events.EventNBT; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.world.storage.SaveHandler; +import net.minecraft.world.storage.WorldInfo; + +@Mixin(SaveHandler.class) +public class MixinSaveHandler { + + @Inject(method = "saveWorldInfoWithPlayer", at = @At(value = "HEAD")) + public void inject_onSaveWorldInfo(WorldInfo worldInfo, NBTTagCompound singlePlayerData, @Share(value = "worldInfo") LocalRef sharedWorldInfo) { + sharedWorldInfo.set(worldInfo); + } + + @ModifyArg(method = "saveWorldInfoWithPlayer", at = @At(value = "INVOKE", target = "Lnet/minecraft/nbt/NBTTagCompound;setTag(Ljava/lang/String;Lnet/minecraft/nbt/NBTTagCompound;)V"), index = 1) + public NBTTagCompound modifyarg_onSaveWorldInfo(NBTTagCompound singlePlayerCompound, @Share("worldInfo") LocalRef sharedWorldInfo) { + WorldInfo worldInfo = sharedWorldInfo.get(); + return (NBTTagCompound) EventListenerRegistry.fireEvent(EventNBT.EventWorldWrite.class, singlePlayerCompound, worldInfo); + } +} 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 761dbff6..4ad6feb8 100644 --- a/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinEntityRenderer.java +++ b/src/main/java/com/minecrafttas/tasmod/mixin/playbackhooks/MixinEntityRenderer.java @@ -81,13 +81,13 @@ public void playback_injectAtStartSection(float partialTicks, long nanoTime, Cal } mc.getTutorial().handleMouse(mc.mouseHelper); - TASmodClient.virtual.CAMERA_ANGLE.updateNextCameraAngle((float) -((double)deltaPitch * 0.15D * invertMouse), (float) ((double)deltaYaw * 0.15D), TASmodClient.tickratechanger.ticksPerSecond != 0); + TASmodClient.virtual.CAMERA_ANGLE.updateNextCameraAngle((float) -((double) deltaPitch * 0.15D * invertMouse), (float) ((double) deltaYaw * 0.15D), TASmodClient.tickratechanger.ticksPerSecond != 0); } } @Redirect(method = "updateCameraAndRender", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/entity/EntityPlayerSP;turn(FF)V")) - public void playback_stopVanilla(EntityPlayerSP player, float deltaYaw, float deltaPitch){ - if(TASmodClient.tickratechanger.ticksPerSecond == 0){ + public void playback_turnPlayer(EntityPlayerSP player, float deltaYaw, float deltaPitch) { + if (TASmodClient.tickratechanger.ticksPerSecond == 0 && !TASmodClient.controller.isPlayingback()) { player.turn(deltaYaw, deltaPitch); } } @@ -100,35 +100,35 @@ public void playback_stopVanilla(EntityPlayerSP player, float deltaYaw, float de */ @Override public void runUpdate(float partialTicks) { - if(mc.player == null){ - return; - } - // Update the currentCameraAngle - TASmodClient.virtual.CAMERA_ANGLE.nextCameraTick(); - - // Store current rotation to be used as prevRotationPitch/Yaw - float prevPitch = mc.player.rotationPitch; - float prevYaw = mc.player.rotationYaw; - - // Get the new pitch from the virtual input - Float newPitch = TASmodClient.virtual.CAMERA_ANGLE.getCurrentPitch(); - Float newYaw = TASmodClient.virtual.CAMERA_ANGLE.getCurrentYaw(); - - // If the pitch or yaw is null (usually on initialize or when the player joins the world), - // set nextCameraAngle to the current absolute camera coordinates. - // This ensures that the camera position is loaded correctly - if(newPitch == null || newYaw == null) { - TASmodClient.virtual.CAMERA_ANGLE.setCamera(prevPitch, prevYaw); - return; - } + if (mc.player == null) { + return; + } + // Update the currentCameraAngle + TASmodClient.virtual.CAMERA_ANGLE.nextCameraTick(); + + // Store current rotation to be used as prevRotationPitch/Yaw + float prevPitch = mc.player.rotationPitch; + float prevYaw = mc.player.rotationYaw; + + // Get the new pitch from the virtual input + Float newPitch = TASmodClient.virtual.CAMERA_ANGLE.getCurrentPitch(); + Float newYaw = TASmodClient.virtual.CAMERA_ANGLE.getCurrentYaw(); + + // If the pitch or yaw is null (usually on initialize or when the player joins the world), + // set nextCameraAngle to the current absolute camera coordinates. + // This ensures that the camera position is loaded correctly + if (newPitch == null || newYaw == null) { + TASmodClient.virtual.CAMERA_ANGLE.setCamera(prevPitch, prevYaw); + return; + } - // Update the rotation of the player - mc.player.rotationPitch = newPitch; - mc.player.rotationYaw = newYaw; + // Update the rotation of the player + mc.player.rotationPitch = newPitch; + mc.player.rotationYaw = newYaw; - // Update the previous rotation of the player - mc.player.prevRotationPitch = prevPitch; - mc.player.prevRotationYaw = prevYaw; + // Update the previous rotation of the player + mc.player.prevRotationPitch = prevPitch; + mc.player.prevRotationYaw = prevYaw; } /** diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/savestates/AccessorEntityLivingBase.java b/src/main/java/com/minecrafttas/tasmod/mixin/savestates/AccessorEntityLivingBase.java new file mode 100644 index 00000000..271cf872 --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/mixin/savestates/AccessorEntityLivingBase.java @@ -0,0 +1,22 @@ +package com.minecrafttas.tasmod.mixin.savestates; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +import com.minecrafttas.tasmod.savestates.SavestateHandlerClient; + +import net.minecraft.entity.EntityLivingBase; + +@Mixin(EntityLivingBase.class) +public interface AccessorEntityLivingBase { + + /** + *

Clears potion particles. + *

Used to clear potion particles on the client still persisting
+ * after loading a savestate across dimensions + * + * @see SavestateHandlerClient#loadPlayer(net.minecraft.nbt.NBTTagCompound) + */ + @Invoker("resetPotionEffectMetadata") + public void clearPotionEffects(); +} diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/savestates/AccessorPlayerChunkMap.java b/src/main/java/com/minecrafttas/tasmod/mixin/savestates/AccessorPlayerChunkMap.java new file mode 100644 index 00000000..bd85e03c --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/mixin/savestates/AccessorPlayerChunkMap.java @@ -0,0 +1,22 @@ +package com.minecrafttas.tasmod.mixin.savestates; + +import java.util.List; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import com.minecrafttas.tasmod.savestates.handlers.SavestateWorldHandler; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.server.management.PlayerChunkMap; + +@Mixin(PlayerChunkMap.class) +public interface AccessorPlayerChunkMap { + + /** + * @return The players from the specified chunk map + * @see SavestateWorldHandler#addPlayerToChunkMap() + */ + @Accessor + public List getPlayers(); +} diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinChunkProviderClient.java b/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinChunkProviderClient.java index d3ab135b..7859b968 100644 --- a/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinChunkProviderClient.java +++ b/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinChunkProviderClient.java @@ -11,21 +11,30 @@ import net.minecraft.client.multiplayer.ChunkProviderClient; import net.minecraft.world.chunk.Chunk; +/** + * Adds {@link #unloadAllChunks()} to the {@link ChunkProviderClient} + * + * @author Scribble + */ @Mixin(ChunkProviderClient.class) -public class MixinChunkProviderClient implements ChunkProviderDuck{ - @Shadow @Final +public class MixinChunkProviderClient implements ChunkProviderDuck { + @Shadow + @Final private Long2ObjectMap chunkMapping; + /** + *

Unloads chunk data on the client. + *

This step is necessary as not doing this, will create phantom blocks on the client, with strange behaviour. + */ @Override public void unloadAllChunks() { - ObjectIterator objectiterator = this.chunkMapping.values().iterator(); + ObjectIterator objectiterator = this.chunkMapping.values().iterator(); - while (objectiterator.hasNext()) - { - Chunk chunk = (Chunk)objectiterator.next(); - chunk.onUnload(); - } - chunkMapping.clear(); - } + while (objectiterator.hasNext()) { + Chunk chunk = (Chunk) objectiterator.next(); + chunk.onUnload(); + } + chunkMapping.clear(); + } } diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinChunkProviderServer.java b/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinChunkProviderServer.java index e686c472..b0e829dc 100644 --- a/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinChunkProviderServer.java +++ b/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinChunkProviderServer.java @@ -11,27 +11,43 @@ import net.minecraft.world.chunk.Chunk; import net.minecraft.world.gen.ChunkProviderServer; +/** + * Adds {@link #unloadAllChunks()} to the {@link ChunkProviderServer} + * + * @author Scribble + */ @Mixin(ChunkProviderServer.class) -public abstract class MixinChunkProviderServer implements ChunkProviderDuck{ - - @Shadow @Final +public abstract class MixinChunkProviderServer implements ChunkProviderDuck { + + /** + * The very inadequately named loadedChunkList + */ + @Shadow + @Final private Long2ObjectMap id2ChunkMap; + /** + *

Saves and unloads chunk data. + *

Oddly, there is no definitive "unload" method in the ChunkProviderServer,
+ * you can only queue the chunks to be unloaded later ({@link ChunkProviderServer#queueUnload(Chunk)}) + *

This method adds that functionality, so savestates can be loaded even in tickrate 0. + */ @Override public void unloadAllChunks() { - ObjectIterator objectiterator = this.id2ChunkMap.values().iterator(); - - while (objectiterator.hasNext()) - { - Chunk chunk = (Chunk)objectiterator.next(); - this.saveChunkData(chunk); - this.saveChunkExtraData(chunk); - chunk.onUnload(); - } - id2ChunkMap.clear(); - } + ObjectIterator objectiterator = this.id2ChunkMap.values().iterator(); + + while (objectiterator.hasNext()) { + Chunk chunk = (Chunk) objectiterator.next(); + this.saveChunkData(chunk); + this.saveChunkExtraData(chunk); + chunk.onUnload(); + } + id2ChunkMap.clear(); + } + @Shadow abstract void saveChunkExtraData(Chunk chunk); + @Shadow abstract void saveChunkData(Chunk chunk); diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinEntityPlayerMP.java b/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinEntityPlayerMP.java deleted file mode 100644 index 1de0ce97..00000000 --- a/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinEntityPlayerMP.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.minecrafttas.tasmod.mixin.savestates; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import com.minecrafttas.tasmod.savestates.SavestateHandlerServer.PlayerHandler; - -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.nbt.NBTTagCompound; - -@Mixin(EntityPlayerMP.class) -public class MixinEntityPlayerMP { - - @Inject(method = "writeEntityToNBT", at = @At(value = "RETURN")) - public void writeClientMotion(NBTTagCompound compound, CallbackInfo ci) { - NBTTagCompound nbttagcompound = new NBTTagCompound(); - PlayerHandler.MotionData saver = PlayerHandler.getMotion().get((EntityPlayerMP) (Object) this); - if (saver != null) { - nbttagcompound.setDouble("x", saver.getClientX()); - nbttagcompound.setDouble("y", saver.getClientY()); - nbttagcompound.setDouble("z", saver.getClientZ()); - nbttagcompound.setFloat("RelativeX", saver.getClientrX()); - nbttagcompound.setFloat("RelativeY", saver.getClientrY()); - nbttagcompound.setFloat("RelativeZ", saver.getClientrZ()); - nbttagcompound.setBoolean("Sprinting", saver.isSprinting()); - nbttagcompound.setFloat("JumpFactor", saver.getJumpMovementVector()); - compound.setTag("clientMotion", nbttagcompound); - } else { - nbttagcompound.setDouble("x", 0D); - nbttagcompound.setDouble("y", 0D); - nbttagcompound.setDouble("z", 0D); - nbttagcompound.setFloat("RelativeX", 0F); - nbttagcompound.setFloat("RelativeY", 0F); - nbttagcompound.setFloat("RelativeZ", 0F); - compound.setTag("clientMotion", nbttagcompound); - } - } - - @Inject(method = "readEntityFromNBT", at = @At(value = "RETURN")) - public void readClientMotion(NBTTagCompound compound, CallbackInfo ci) { - NBTTagCompound nbttagcompound = compound.getCompoundTag("clientMotion"); - - double clientmotionX = nbttagcompound.getDouble("x"); - double clientmotionY = nbttagcompound.getDouble("y"); - double clientmotionZ = nbttagcompound.getDouble("z"); - float clientmotionrX = nbttagcompound.getFloat("RelativeX"); - float clientmotionrY = nbttagcompound.getFloat("RelativeY"); - float clientmotionrZ = nbttagcompound.getFloat("RelativeZ"); - boolean sprinting = nbttagcompound.getBoolean("Sprinting"); - float jumpVector = nbttagcompound.getFloat("JumpFactor"); - - PlayerHandler.MotionData saver = new PlayerHandler.MotionData(clientmotionX, clientmotionY, clientmotionZ, clientmotionrX, clientmotionrY, clientmotionrZ, sprinting, jumpVector); - PlayerHandler.getMotion().put((EntityPlayerMP) (Object) this, saver); - - } -} diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinNetHandlerPlayServer.java b/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinNetHandlerPlayServer.java index 40d61059..d6a4a802 100644 --- a/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinNetHandlerPlayServer.java +++ b/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinNetHandlerPlayServer.java @@ -13,8 +13,11 @@ @Mixin(NetHandlerPlayServer.class) public class MixinNetHandlerPlayServer { + /** + * Disables the "Player moved wrongly!" message during a savestate + */ @Redirect(method = "processPlayer", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/EntityPlayerMP;isInvulnerableDimensionChange()Z")) public boolean redirect_processPlayer(EntityPlayerMP parentIn) { - return !parentIn.isInvulnerableDimensionChange() && TASmod.savestateHandlerServer.state!=SavestateState.LOADING; + return !parentIn.isInvulnerableDimensionChange() && TASmod.savestateHandlerServer.state != SavestateState.LOADING; } } diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinScoreboard.java b/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinScoreboard.java new file mode 100644 index 00000000..dc19b8b9 --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinScoreboard.java @@ -0,0 +1,50 @@ +package com.minecrafttas.tasmod.mixin.savestates; + +import java.util.List; +import java.util.Map; + +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import com.minecrafttas.tasmod.util.Ducks.ScoreboardDuck; + +import net.minecraft.scoreboard.IScoreCriteria; +import net.minecraft.scoreboard.Score; +import net.minecraft.scoreboard.ScoreObjective; +import net.minecraft.scoreboard.ScorePlayerTeam; +import net.minecraft.scoreboard.Scoreboard; + +@Mixin(Scoreboard.class) +public class MixinScoreboard implements ScoreboardDuck { + @Shadow + @Final + private Map scoreObjectives; + @Shadow + @Final + private Map> scoreObjectiveCriterias; + @Shadow + @Final + private Map> entitiesScoreObjectives; + @Shadow + @Final + private ScoreObjective[] objectiveDisplaySlots; + @Shadow + @Final + private Map teams; + @Shadow + @Final + private Map teamMemberships; + + @Override + public void clearScoreboard() { + scoreObjectives.clear(); + scoreObjectiveCriterias.clear(); + entitiesScoreObjectives.clear(); + for (int i = 0; i < objectiveDisplaySlots.length; i++) { + objectiveDisplaySlots[i] = null; + } + teams.clear(); + teamMemberships.clear(); + } +} diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinWorldClient.java b/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinWorldClient.java new file mode 100644 index 00000000..7027c68a --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinWorldClient.java @@ -0,0 +1,26 @@ +package com.minecrafttas.tasmod.mixin.savestates; + +import java.util.Set; + +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import com.minecrafttas.tasmod.util.Ducks.WorldClientDuck; + +import net.minecraft.client.multiplayer.WorldClient; +import net.minecraft.entity.Entity; + +@Mixin(WorldClient.class) +public class MixinWorldClient implements WorldClientDuck { + + @Shadow + @Final + private Set entityList; + + @Override + public void clearEntityList() { + entityList.clear(); + } + +} diff --git a/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinWorldServer.java b/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinWorldServer.java new file mode 100644 index 00000000..5572281b --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/mixin/savestates/MixinWorldServer.java @@ -0,0 +1,44 @@ +package com.minecrafttas.tasmod.mixin.savestates; + +import java.util.Iterator; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import com.minecrafttas.tasmod.util.Ducks.WorldServerDuck; + +import net.minecraft.server.management.PlayerChunkMap; +import net.minecraft.world.WorldServer; +import net.minecraft.world.chunk.Chunk; + +/** + * Adds the {@link #sendChunksToClient()} method to to the WorldServer + * + * @author Scribble + */ +@Mixin(WorldServer.class) +public abstract class MixinWorldServer implements WorldServerDuck { + + @Shadow + private PlayerChunkMap playerChunkMap; + + /** + *

Tricks the {@link #playerChunkMap} into sending the loaded chunks to the client.
+ * In theory, the playerChunkMap just needs to be ticked once,
+ * in order for the {@link PlayerChunkMap#pendingSendToPlayers} chunks to be sent to the client. + *

This fails however because the {@link net.minecraft.server.management.PlayerChunkMapEntry#sendToPlayers() PlayerChunkMapEntry#sendToPlayers()} method, responsible for sending, has a {@link net.minecraft.world.chunk.Chunk#isPopulated() Chunk.isPopulated()} check.
+ *

To make this check return true, the chunk needs to be ticked once (as seen in {@link net.minecraft.world.chunk.Chunk#onTick(boolean) Chunk.onTick()}).
+ * In vanilla, this usually happens in the {@link WorldServer#updateBlocks()} method,
+ * but calling this method here updates a lot of other things as well, which we do not want. + *

That's why we iterate through the chunks to tick each once, then send them off via the playerChunkMap. + */ + @Override + public void sendChunksToClient() { + for (Iterator iterator2 = this.playerChunkMap.getChunkIterator(); iterator2.hasNext();) { + Chunk chunk = (Chunk) iterator2.next(); + chunk.enqueueRelightChecks(); + chunk.onTick(false); + } + this.playerChunkMap.tick(); + } +} diff --git a/src/main/java/com/minecrafttas/tasmod/networking/TASmodBufferBuilder.java b/src/main/java/com/minecrafttas/tasmod/networking/TASmodBufferBuilder.java index 053fd8ff..82e2b540 100644 --- a/src/main/java/com/minecrafttas/tasmod/networking/TASmodBufferBuilder.java +++ b/src/main/java/com/minecrafttas/tasmod/networking/TASmodBufferBuilder.java @@ -10,61 +10,61 @@ 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.savestates.storage.SavestateMotionStorage.MotionData; import com.minecrafttas.tasmod.tickratechanger.TickrateChangerServer.TickratePauseState; import net.minecraft.nbt.CompressedStreamTools; import net.minecraft.nbt.NBTSizeTracker; import net.minecraft.nbt.NBTTagCompound; -public class TASmodBufferBuilder extends ByteBufferBuilder{ +public class TASmodBufferBuilder extends ByteBufferBuilder { public TASmodBufferBuilder(int id) { super(id); } - + public TASmodBufferBuilder(PacketID packet) { super(packet); } - + public TASmodBufferBuilder(ByteBuffer buf) { super(buf); } - + public TASmodBufferBuilder writeTASState(TASstate state) { - this.writeShort((short)state.ordinal()); + this.writeShort((short) state.ordinal()); return this; } - + public TASmodBufferBuilder writeNBTTagCompound(NBTTagCompound compound) { - + ByteArrayOutputStream out = new ByteArrayOutputStream(); - + DataOutputStream dataout = new DataOutputStream(out); - + try { CompressedStreamTools.write(compound, dataout); } catch (IOException e) { e.printStackTrace(); } - + this.writeByteArray(out.toByteArray()); - + try { out.close(); dataout.close(); } catch (IOException e) { e.printStackTrace(); } - + return this; } - + public TASmodBufferBuilder writeTickratePauseState(TickratePauseState state) { writeShort((short) state.ordinal()); return this; } - + public TASmodBufferBuilder writeMotionData(MotionData data) { writeDouble(data.getClientX()); writeDouble(data.getClientY()); @@ -76,28 +76,28 @@ public TASmodBufferBuilder writeMotionData(MotionData data) { writeBoolean(data.isSprinting()); return this; } - + public static TASstate readTASState(ByteBuffer buf) { return TASstate.values()[buf.getShort()]; } - + public static NBTTagCompound readNBTTagCompound(ByteBuffer buf) throws IOException { ByteArrayInputStream input = new ByteArrayInputStream(readByteArray(buf)); - + DataInputStream datain = new DataInputStream(input); - + NBTTagCompound compound = CompressedStreamTools.read(datain, NBTSizeTracker.INFINITE); - + input.close(); datain.close(); - + return compound; } - + public static TickratePauseState readTickratePauseState(ByteBuffer buf) { return TickratePauseState.values()[buf.getShort()]; } - + public static MotionData readMotionData(ByteBuffer buf) { double x = TASmodBufferBuilder.readDouble(buf); double y = TASmodBufferBuilder.readDouble(buf); @@ -107,8 +107,8 @@ public static MotionData readMotionData(ByteBuffer buf) { float rz = TASmodBufferBuilder.readFloat(buf); float jumpMovementVector = TASmodBufferBuilder.readFloat(buf); boolean sprinting = TASmodBufferBuilder.readBoolean(buf); - + return new MotionData(x, y, z, rx, ry, rz, sprinting, jumpMovementVector); } - + } diff --git a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/PlaybackFileCommandsRegistry.java b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/PlaybackFileCommandsRegistry.java index d70e4f5e..f5fd3c3f 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/PlaybackFileCommandsRegistry.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/PlaybackFileCommandsRegistry.java @@ -174,6 +174,6 @@ private void saveConfig() { nameList.add(element.getExtensionName()); }); config.set(TASmodConfig.EnabledFileCommands, String.join(", ", nameList)); - config.save(); + config.saveToXML(); } } 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 index a89fef45..7c3fed4d 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/filecommands/integrated/DesyncMonitorFileCommandExtension.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/filecommands/integrated/DesyncMonitorFileCommandExtension.java @@ -52,7 +52,7 @@ public String[] getFileCommandNames() { @Override public void onControllerStateChange(TASstate newstate, TASstate oldstate) { - if(newstate==TASstate.RECORDING && monitorContainer.isEmpty()) { + if (newstate == TASstate.RECORDING && monitorContainer.isEmpty()) { recordNull(0); } } @@ -179,13 +179,7 @@ public String getPos() { 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; + lastPos = String.join(" ", values); } return lastPos; } @@ -200,13 +194,7 @@ public String getMotion() { 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; + lastMotion = String.join(" ", values); } return lastMotion; } @@ -247,14 +235,14 @@ public MonitorContainer(long index) { 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]); + out[i] = String.format(Locale.ENGLISH, "%s", 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]); + return String.format(Locale.ENGLISH, "%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) { diff --git a/src/main/java/com/minecrafttas/tasmod/playback/metadata/PlaybackMetadata.java b/src/main/java/com/minecrafttas/tasmod/playback/metadata/PlaybackMetadata.java index 3ad74cd8..fe4da30c 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/metadata/PlaybackMetadata.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/metadata/PlaybackMetadata.java @@ -16,7 +16,7 @@ public class PlaybackMetadata { private String extensionName; private LinkedHashMap data; - + private static String SEPERATOR = ":"; public PlaybackMetadata(PlaybackMetadataExtension extension) { @@ -81,7 +81,7 @@ public boolean equals(Object obj) { public static PlaybackMetadata fromStringList(String extensionName, List list) { PlaybackMetadata out = new PlaybackMetadata(extensionName); - final Pattern pattern = Pattern.compile("(\\w+)\\"+SEPERATOR+"(.+)"); + final Pattern pattern = Pattern.compile("(\\w+)\\" + SEPERATOR + "(.+)"); for (String data : list) { Matcher matcher = pattern.matcher(data); @@ -94,19 +94,20 @@ public static PlaybackMetadata fromStringList(String extensionName, List return out; } - + public static PlaybackMetadata fromHashMap(String extensionName, LinkedHashMap data) { return new PlaybackMetadata(extensionName, new LinkedHashMap<>(data)); } - + public static abstract class PlaybackMetadataExtension implements Registerable { - + /** * Currently unused.
* Maybe in the future, TASes have to be created with /create, then you can interactively set the values...
*/ - public void onCreate() {}; - + public void onCreate() { + }; + /** * Runs, when the TASfile is being stored to a file.
* Create a new {@link PlaybackMetadata} with PlaybackMetadata metadata = new PlaybackMetadata(this);.
@@ -115,14 +116,14 @@ public static abstract class PlaybackMetadataExtension implements Registerable { * @return The {@link PlaybackMetadata} to be saved in the TASfile */ public abstract PlaybackMetadata onStore(); - + /** * Runs when the TASfile is being loaded from a file
* * @param metadata The metadata for this extension to read from */ public abstract void onLoad(PlaybackMetadata metadata); - + /** * Runs when the PlaybackController is cleared */ diff --git a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiser.java b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiser.java index e8945a7f..d2a61054 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiser.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiser.java @@ -87,8 +87,6 @@ public static void saveToFile(File file, BigArrayList container, if (defaultFlavor == null || defaultFlavor.isEmpty()) throw new PlaybackSaveException("No default flavor specified... Please specify a flavor name first"); flavorName = defaultFlavor; - } else { - defaultFlavor = flavorName; } FileThread writerThread; @@ -101,6 +99,12 @@ public static void saveToFile(File file, BigArrayList container, SerialiserFlavorBase flavor = TASmodAPIRegistry.SERIALISER_FLAVOR.getFlavor(flavorName); + if (flavor == null) { + throw new PlaybackSaveException("Flavor %s doesn't exist", flavorName); + } + + defaultFlavor = flavorName; + List header = flavor.serialiseHeader(); for (String line : header) { writerThread.addLine(line); @@ -124,6 +128,10 @@ public static void saveToFile(File file, BigArrayList container, * @throws IOException If the file could not be read */ public static BigArrayList loadFromFile(File file) throws PlaybackLoadException, IOException { + return loadFromFile(file, true); + } + + public static BigArrayList loadFromFile(File file, boolean processExtensions) throws PlaybackLoadException, IOException { if (file == null) { throw new PlaybackLoadException("Load from file failed. No file specified"); } @@ -133,6 +141,8 @@ public static BigArrayList loadFromFile(File file) throws Playbac SerialiserFlavorBase flavor = readFlavor(file); + flavor.setProcessExtensions(processExtensions); + return loadFromFile(file, flavor); } @@ -146,7 +156,11 @@ public static BigArrayList loadFromFile(File file) throws Playbac * @throws IOException If the file could not be read */ public static BigArrayList loadFromFile(File file, String flavorName) throws PlaybackLoadException, IOException { - + return loadFromFile(file, flavorName, true); + } + + public static BigArrayList loadFromFile(File file, String flavorName, boolean processExtensions) throws PlaybackLoadException, IOException { + // If the flavor is null or empty, try to determine the flavor by reading the header if (flavorName == null || flavorName.isEmpty()) { return loadFromFile(file); @@ -165,6 +179,8 @@ public static BigArrayList loadFromFile(File file, String flavorN throw new PlaybackLoadException("Detected flavor %s in the TASfile, which does not match the specified flavor: %s"); } + flavor.setProcessExtensions(processExtensions); + return loadFromFile(file, flavor); } diff --git a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiserOld.java b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiserOld.java index 9a36dcf2..589f3afd 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiserOld.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/PlaybackSerialiserOld.java @@ -12,7 +12,6 @@ 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; @@ -209,7 +208,7 @@ public PlaybackControllerClient fromEntireFileV1(File file) throws IOException { 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(); + long startSeed/*=TASmod.ktrngHandler.getGlobalSeedClient()*/; // Clear the current container before reading new data controller.clear(); diff --git a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java index f671068a..592bcf69 100644 --- a/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java +++ b/src/main/java/com/minecrafttas/tasmod/playback/tasfile/flavor/SerialiserFlavorBase.java @@ -43,6 +43,11 @@ public abstract class SerialiserFlavorBase implements Registerable { protected TickContainer previousTickContainer = null; + /** + * If true, process extension data like {@link PlaybackMetadata PlaybackMetadata} and {@link PlaybackFileCommand PlaybackFileCommands} + */ + protected boolean processExtensions = true; + protected String headerStart() { return createCenteredHeading("TASfile", '#', 50); } @@ -89,12 +94,17 @@ protected void serialiseFlavorName(List out) { protected void serialiseFileCommandNames(List out) { List stringlist = new ArrayList<>(); List extensionList = TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.getEnabled(); - extensionList.forEach(extension -> stringlist.add(extension.getExtensionName())); + if (processExtensions) { + extensionList.forEach(extension -> stringlist.add(extension.getExtensionName())); + } out.add("FileCommand-Extensions: " + String.join(", ", stringlist)); out.add(""); } protected void serialiseMetadata(List out) { + if (!processExtensions) + return; + List metadataList = TASmodAPIRegistry.PLAYBACK_METADATA.handleOnStore(); for (PlaybackMetadata metadata : metadataList) { @@ -122,7 +132,7 @@ public BigArrayList serialise(BigArrayList inputs, long t break; } currentTick = i; - TickContainer container = inputs.get(i); + TickContainer container = inputs.get(i).clone(); serialiseContainer(out, container); previousTickContainer = container; } @@ -152,6 +162,8 @@ protected void serialiseContainer(BigArrayList out, TickContainer contai } protected String serialiseFileCommand(PlaybackFileCommand fileCommand) { + if (!processExtensions) + return ""; return String.format("$%s(%s);", fileCommand.getName(), String.join(", ", fileCommand.getArgs())); } @@ -193,15 +205,12 @@ protected List serialiseMouse(VirtualMouse mouse) { 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()); + out.add(String.format("%s;%s", subtick.getYaw(), subtick.getPitch())); previousCamera = subtick; } @@ -299,7 +308,7 @@ protected String getOrEmpty(String string) { * Joins strings together but ignores empty strings * * @param delimiter The delimiter of the joined string - * @param args The strings to join + * @param args The strings to join * @return Joined string */ protected String joinNotEmpty(String delimiter, Iterable args) { @@ -368,10 +377,17 @@ public List extractHeader(BigArrayList lines) { } protected void deserialiseFileCommandNames(List headerLines) { + if (!processExtensions) // Stops FileCommandProcessing + return; + for (String line : headerLines) { Matcher matcher = extract("FileCommand-Extensions: ?(.*)", line); if (matcher.find()) { + + if (!processExtensions) + return; + String extensionStrings = matcher.group(1); String[] extensionNames = extensionStrings.split(", ?"); @@ -383,6 +399,9 @@ protected void deserialiseFileCommandNames(List headerLines) { } protected void deserialiseMetadata(List headerLines) { + if (!processExtensions) + return; + List out = new ArrayList<>(); String metadataName = null; @@ -429,9 +448,9 @@ public BigArrayList deserialise(BigArrayList lines, long // Extract the tick and set the index i = extractContainer(container, lines, i); currentLine = i; - currentTick++; // Extract container deserialiseContainer(out, container); + currentTick++; } previousTickContainer = null; return out; @@ -619,8 +638,10 @@ protected void deserialiseContainer(BigArrayList out, 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)); + + if (processExtensions) + deserialisedFileCommands.add(new PlaybackFileCommand(name, args)); + comment = matcher.replaceFirst(""); matcher.reset(comment); } @@ -686,8 +711,10 @@ protected VirtualKeyboard deserialiseKeyboard(List keyboardStrings) { String[] keys = matcher.group(1).split(","); char[] chars = matcher.group(2).toCharArray(); - int[] keycodes = deserialiseVirtualKey(keys, VirtualKey.ZERO); + int[] keycodes = deserialiseVirtualKeyboardKey(keys); out.updateFromState(keycodes, chars); + } else { + throw new PlaybackLoadException(currentLine, currentTick, currentSubtick, "Keyboard could not be read. Probably a missing semicolon: %s", line); } currentSubtick++; } @@ -707,7 +734,7 @@ protected VirtualMouse deserialiseMouse(List mouseStrings) { String[] buttons = matcher.group(1).split(","); String[] functions = matcher.group(2).split(","); - int[] keycodes = deserialiseVirtualKey(buttons, VirtualKey.MOUSEMOVED); + int[] keycodes = deserialiseVirtualMouseKey(buttons); int scrollwheel; Integer cursorX; Integer cursorY; @@ -717,13 +744,15 @@ protected VirtualMouse deserialiseMouse(List mouseStrings) { cursorX = deserialiseRelativeInt("cursorX", functions[1], previousCursorX); cursorY = deserialiseRelativeInt("cursorY", functions[2], previousCursorY); } else { - throw new PlaybackLoadException(currentLine, currentTick, currentSubtick, "Mouse functions do not have the correct length"); + throw new PlaybackLoadException(currentLine, currentTick, currentSubtick, "Mouse can't be read. Probably a missing comma: %s", line); } out.updateFromState(keycodes, scrollwheel, cursorX, cursorY); previousCursorX = cursorX; previousCursorY = cursorY; + } else { + throw new PlaybackLoadException(currentLine, currentTick, currentSubtick, "Mouse is missing a semicolon"); } currentSubtick++; } @@ -741,57 +770,92 @@ protected VirtualCameraAngle deserialiseCameraAngle(List cameraAngleStri Matcher matcher = extract("(.+?);(.+)", line); if (matcher.find()) { - String cameraPitchString = matcher.group(1); - String cameraYawString = matcher.group(2); + String cameraYawString = matcher.group(1); + String cameraPitchString = matcher.group(2); - Float cameraPitch = null; Float cameraYaw = null; - - if (!"null".equals(cameraPitchString)) - cameraPitch = deserialiseRelativeFloat("camera pitch", cameraPitchString, previousPitch); + Float cameraPitch = null; if (!"null".equals(cameraYawString)) cameraYaw = deserialiseRelativeFloat("camera yaw", cameraYawString, previousYaw); + if (!"null".equals(cameraPitchString)) + cameraPitch = deserialiseRelativeFloat("camera pitch", cameraPitchString, previousPitch); + out.updateFromState(cameraPitch, cameraYaw); + } else { + throw new PlaybackLoadException(currentLine, currentTick, currentSubtick, "Camera is missing a semicolon"); } currentSubtick++; } return out; } - protected int[] deserialiseVirtualKey(String[] keyString, VirtualKey defaultKey) { + protected int[] deserialiseVirtualKeyboardKey(String[] keyString) { int[] out = new int[keyString.length]; for (int i = 0; i < keyString.length; i++) { String key = keyString[i]; + out[i] = deserialiseVirtualKey(key, VirtualKey.ZERO, (vkey) -> { + if (vkey < 0) { + throw new PlaybackLoadException(currentLine, currentTick, currentSubtick, "Keyboard section contains a mouse key: %s", VirtualKey.get(vkey)); + } + }); + } + return out; + } - /* If no key is pressed, then a zero key will be used for the state. - * This zero key is either VirtualKey.ZERO on a keyboard or VirtualKey.MOUSEMOVED on a mouse, - * hence the parameter */ - if (key.isEmpty()) { - out[i] = defaultKey.getKeycode(); - continue; - } - - /* Instead of keynames such as W, A, S, KEY_1, NUMPAD3 you can also write the numerical keycodes - * into the tasfile, e.g. 17, 30, 31, 2, 81. This enables TASmod to support every current and future - * keycodes, even if no name was given to the key in VirtualKey.*/ - if (isNumeric(key)) { - out[i] = Integer.parseInt(key); - continue; - } + protected int[] deserialiseVirtualMouseKey(String[] keyString) { + int[] out = new int[keyString.length]; - out[i] = VirtualKey.getKeycode(key); + for (int i = 0; i < keyString.length; i++) { + String key = keyString[i]; + out[i] = deserialiseVirtualKey(key, VirtualKey.MOUSEMOVED, (vkey) -> { + if (vkey >= 0) { + throw new PlaybackLoadException(currentLine, currentTick, currentSubtick, "Mouse section contains a keyboard key: %s", VirtualKey.get(vkey)); + } + }); } return out; } + protected int deserialiseVirtualKey(String key, VirtualKey defaultKey, WrongKeyCheck keyValidator) { + + Integer vkey = null; + /* If no key is pressed, then a zero key will be used for the state. + * This zero key is either VirtualKey.ZERO on a keyboard or VirtualKey.MOUSEMOVED on a mouse, + * hence the parameter */ + if (key.isEmpty()) { + vkey = defaultKey.getKeycode(); + } + /* Instead of keynames such as W, A, S, KEY_1, NUMPAD3 you can also write the numerical keycodes + * into the tasfile, e.g. 17, 30, 31, 2, 81. This enables TASmod to support every current and future + * keycodes, even if no name was given to the key in VirtualKey.*/ + else if (isNumeric(key)) { + vkey = Integer.parseInt(key); + } else { + vkey = VirtualKey.getKeycode(key); + } + + if (vkey == null) { + throw new PlaybackLoadException(currentLine, currentTick, currentSubtick, "The keycode %s does not exist", key); + } + + keyValidator.checkKey(vkey); + + return vkey; + } + + @FunctionalInterface + protected interface WrongKeyCheck { + public void checkKey(int key) throws PlaybackLoadException; + } + protected int parseInt(String name, String intstring) { try { return Integer.parseInt(intstring); } catch (NumberFormatException e) { - throw new PlaybackLoadException(currentLine, currentTick, currentSubtick, e, "Can't parse integer in %s", name); + throw new PlaybackLoadException(currentLine, currentTick, currentSubtick, e, "The %s could not be processed. This should be a number: %s", name, intstring); } } @@ -815,7 +879,7 @@ protected float parseFloat(String name, String floatstring) { try { return Float.parseFloat(floatstring); } catch (NumberFormatException e) { - throw new PlaybackLoadException(currentLine, currentTick, currentSubtick, e, "Can't parse float in %s", name); + throw new PlaybackLoadException(currentLine, currentTick, currentSubtick, e, "The %s could not be processed. This should be a decimal number: %s", name, floatstring); } } @@ -843,7 +907,8 @@ protected void splitInputs(List lines, List serialisedKeyboard, String previousCamera = null; if (previousTickContainer != null) { - previousCamera = previousTickContainer.getCameraAngle().toString2(); + VirtualCameraAngle camera = previousTickContainer.getCameraAngle(); + previousCamera = String.format("%s;%s", camera.getYaw(), camera.getPitch()); } for (String line : lines) { @@ -1016,4 +1081,8 @@ public boolean equals(Object obj) { } return super.equals(obj); } + + public void setProcessExtensions(boolean processExtensions) { + this.processExtensions = processExtensions; + } } diff --git a/src/main/java/com/minecrafttas/tasmod/registries/TASmodAPIRegistry.java b/src/main/java/com/minecrafttas/tasmod/registries/TASmodAPIRegistry.java index 86e68530..62804042 100644 --- a/src/main/java/com/minecrafttas/tasmod/registries/TASmodAPIRegistry.java +++ b/src/main/java/com/minecrafttas/tasmod/registries/TASmodAPIRegistry.java @@ -25,7 +25,7 @@ public class TASmodAPIRegistry { * */ 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.
*
@@ -35,5 +35,4 @@ public class TASmodAPIRegistry { * 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 index 166582c3..645c518c 100644 --- a/src/main/java/com/minecrafttas/tasmod/registries/TASmodConfig.java +++ b/src/main/java/com/minecrafttas/tasmod/registries/TASmodConfig.java @@ -1,7 +1,12 @@ package com.minecrafttas.tasmod.registries; -import com.minecrafttas.mctcommon.Configuration.ConfigOptions; +import com.minecrafttas.mctcommon.ConfigurationRegistry.ConfigOptions; +/** + * The config options that will be stored in .minecraft/config/tasmod.cfg + * + * @author Scribble + */ public enum TASmodConfig implements ConfigOptions { FileToOpen("fileToOpen", ""), ServerConnection("serverConnection", ""), @@ -9,12 +14,12 @@ public enum TASmodConfig implements ConfigOptions { private String configKey; private String defaultValue; - + private TASmodConfig(String configKey, String defaultValue) { this.configKey = configKey; this.defaultValue = defaultValue; } - + @Override public String getDefaultValue() { return defaultValue; @@ -29,5 +34,4 @@ public String getConfigKey() { 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 index 5abafea7..6743870c 100644 --- a/src/main/java/com/minecrafttas/tasmod/registries/TASmodKeybinds.java +++ b/src/main/java/com/minecrafttas/tasmod/registries/TASmodKeybinds.java @@ -11,16 +11,12 @@ 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) { @@ -28,7 +24,6 @@ public enum TASmodKeybinds { } }), 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) { @@ -37,7 +32,6 @@ public enum TASmodKeybinds { }), 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 { diff --git a/src/main/java/com/minecrafttas/tasmod/registries/TASmodPackets.java b/src/main/java/com/minecrafttas/tasmod/registries/TASmodPackets.java index 31c4cbd1..cc2fdb26 100644 --- a/src/main/java/com/minecrafttas/tasmod/registries/TASmodPackets.java +++ b/src/main/java/com/minecrafttas/tasmod/registries/TASmodPackets.java @@ -8,9 +8,11 @@ import com.minecrafttas.tasmod.playback.PlaybackControllerClient.TASstate; 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.savestates.storage.SavestateMotionStorage.MotionData; import com.minecrafttas.tasmod.tickratechanger.TickrateChangerServer.TickratePauseState; +import com.minecrafttas.tasmod.util.Ducks.ScoreboardDuck; +import net.minecraft.client.Minecraft; import net.minecraft.nbt.NBTTagCompound; /** @@ -81,12 +83,28 @@ public enum TASmodPackets implements PacketID { * Client->Server {@link MotionData} motionData An Object containing all necessary motion data
*/ SAVESTATE_REQUEST_MOTION, + /** + *

Used for setting the client motion data after it was loaded from a savestate + *

Side: Client
+ * ARGS:
+ * Server->Client {@link MotionData} motionData An Object containing all necessary motion data
+ */ + SAVESTATE_SET_MOTION, /** *

Unloads the chunks on the client side *

SIDE: Client
* ARGS: none */ SAVESTATE_UNLOAD_CHUNKS, + /** + *

Clears the scoreboard on the client side + *

SIDE: Client
+ * ARGS: none + */ + SAVESTATE_CLEAR_SCOREBOARD(Side.CLIENT, (buf, clientID) -> { + Minecraft mc = Minecraft.getMinecraft(); + ((ScoreboardDuck) mc.world.getScoreboard()).clearScoreboard(); + }), /** *

Notifies the client to clear all inputs from the input buffer in {@link PlaybackControllerClient} *

SIDE: Both
@@ -189,6 +207,16 @@ public enum TASmodPackets implements PacketID { break; } }), + /** + *

Clears the current gui screen on the client + * + *

Side: CLIENT
+ * ARGS: none + */ + CLEAR_SCREEN(Side.CLIENT, (buf, clientID) -> { + Minecraft mc = Minecraft.getMinecraft(); + mc.displayGuiScreen(null); + }), /** *

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

SIDE: Both
@@ -207,7 +235,7 @@ public enum TASmodPackets implements PacketID { COMMAND_FLAVORLIST, /** *

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

SIDE: Bith
+ *

SIDE: Both
* ARGS:
* Server->Client None
* Client->Server String The string of file command names, seperated with | diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerClient.java b/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerClient.java index 1b7b9ac5..159a5259 100644 --- a/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerClient.java +++ b/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerClient.java @@ -7,23 +7,28 @@ import java.nio.ByteBuffer; import com.dselent.bigarraylist.BigArrayList; +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.ClientPacketHandler; import com.minecrafttas.mctcommon.networking.interfaces.PacketID; import com.minecrafttas.tasmod.TASmodClient; +import com.minecrafttas.tasmod.events.EventSavestate; +import com.minecrafttas.tasmod.mixin.savestates.AccessorEntityLivingBase; import com.minecrafttas.tasmod.mixin.savestates.MixinChunkProviderClient; import com.minecrafttas.tasmod.networking.TASmodBufferBuilder; 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.TASmodAPIRegistry; 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; import com.minecrafttas.tasmod.util.Ducks.ChunkProviderDuck; +import com.minecrafttas.tasmod.util.Ducks.SubtickDuck; +import com.minecrafttas.tasmod.util.Ducks.WorldClientDuck; import com.minecrafttas.tasmod.util.LoggerMarkers; import com.mojang.realmsclient.gui.ChatFormatting; @@ -32,7 +37,6 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.client.multiplayer.ChunkProviderClient; -import net.minecraft.entity.player.EntityPlayer; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.math.MathHelper; import net.minecraft.util.text.TextComponentString; @@ -44,7 +48,7 @@ * * @author Scribble */ -public class SavestateHandlerClient implements ClientPacketHandler { +public class SavestateHandlerClient implements ClientPacketHandler, EventSavestate.EventClientCompleteLoadstate, EventSavestate.EventClientLoadPlayer { public final static File savestateDirectory = new File(TASmodClient.tasdirectory + File.separator + "savestates"); @@ -65,8 +69,8 @@ public class SavestateHandlerClient implements ClientPacketHandler { *
* Side: Client */ - @Environment(EnvType.CLIENT) - public static void keepPlayerInLoadedEntityList(net.minecraft.entity.player.EntityPlayer player) { + @Override + public void onClientLoadPlayer(EntityPlayerSP player) { LOGGER.trace(LoggerMarkers.Savestate, "Keep player {} in loaded entity list", player.getName()); Minecraft.getMinecraft().world.unloadedEntityList.remove(player); } @@ -87,8 +91,9 @@ public static void keepPlayerInLoadedEntityList(net.minecraft.entity.player.Enti *
* Side: Client */ - @Environment(EnvType.CLIENT) - public static void addPlayerToClientChunk(EntityPlayer player) { + @Override + public void onClientLoadstateComplete() { + EntityPlayerSP player = Minecraft.getMinecraft().player; LOGGER.trace(LoggerMarkers.Savestate, "Add player {} to loaded entity list", player.getName()); int i = MathHelper.floor(player.posX / 16.0D); int j = MathHelper.floor(player.posZ / 16.0D); @@ -164,7 +169,7 @@ public static void loadstate(String nameOfSavestate) throws Exception { BigArrayList savestateContainerList; if (targetfile.exists()) { - savestateContainerList = PlaybackSerialiser.loadFromFile(targetfile); + savestateContainerList = PlaybackSerialiser.loadFromFile(targetfile, state != TASstate.PLAYBACK); } else { controller.setTASStateClient(TASstate.NONE, false); Minecraft.getMinecraft().player.sendMessage(new TextComponentString(ChatFormatting.YELLOW + "Inputs could not be loaded for this savestate,")); @@ -175,7 +180,7 @@ public static void loadstate(String nameOfSavestate) throws Exception { /* * Imagine a recording that is 20 tick long with VV showing the current index of the controller: - * VV + * VV * 0 20 * <====================> * @@ -186,7 +191,7 @@ public static void loadstate(String nameOfSavestate) throws Exception { * * 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 + * VV * 0 10 * <==========> * @@ -194,7 +199,6 @@ public static void loadstate(String nameOfSavestate) throws Exception { if (state == TASstate.RECORDING) { long index = savestateContainerList.size() - 1; - preload(savestateContainerList, index); controller.setInputs(savestateContainerList, index); /* @@ -207,7 +211,7 @@ public static void loadstate(String nameOfSavestate) throws Exception { * 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 + * VV * 0 13 20 * <====================> * @@ -220,7 +224,7 @@ public static void loadstate(String nameOfSavestate) throws Exception { * 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 + * VV * 0 10 20 * <====================> * */ @@ -244,11 +248,17 @@ public static void loadstate(String nameOfSavestate) throws Exception { controller.setInputs(savestateContainerList, index); } } + + TASmodClient.tickSchedulerClient.add(() -> { + EventListenerRegistry.fireEvent(EventSavestate.EventClientCompleteLoadstate.class); + }); } private static void preload(BigArrayList containerList, long index) { TickContainer containerToPreload = containerList.get(index); TASmodClient.virtual.preloadInput(containerToPreload.getKeyboard(), containerToPreload.getMouse(), containerToPreload.getCameraAngle()); + + TASmodAPIRegistry.PLAYBACK_FILE_COMMAND.onPlaybackTick(index, containerToPreload); } public static void loadPlayer(NBTTagCompound compound) { @@ -256,32 +266,47 @@ public static void loadPlayer(NBTTagCompound compound) { Minecraft mc = Minecraft.getMinecraft(); EntityPlayerSP player = mc.player; + // Clear any accidental applied potion particles on the client + ((AccessorEntityLivingBase) player).clearPotionEffects(); + + /* + * TODO + * The following 20 lines are all one + * gross workaround for correctly applying the player motion + * to the client... + * + * The motion is applied + * to the player in a previous step and unfortunately + * player.readFromNBT(compound) overwrites the + * previously applied motion... + * + * So this workaround makes sure that the motion is not overwritten + * Fixing this, requires restructuring the steps for loadstating + * and since I plan to do this anyway at some point, I will + * leave this here and be done for today*/ + double x = player.motionX; + double y = player.motionY; + double z = player.motionZ; + + float rx = player.moveForward; + float ry = player.moveStrafing; + float rz = player.moveVertical; + + boolean sprinting = player.isSprinting(); + float jumpVector = player.jumpMovementFactor; + player.readFromNBT(compound); - NBTTagCompound motion = compound.getCompoundTag("clientMotion"); - 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"); - double x = motion.getDouble("x"); - double y = motion.getDouble("y"); - double z = motion.getDouble("z"); - 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); - player.jumpMovementFactor = jumpVector; - } + player.motionX = x; + player.motionY = y; + player.motionZ = z; + + player.moveForward = rx; + player.moveVertical = ry; + player.moveStrafing = rz; + + player.setSprinting(sprinting); + player.jumpMovementFactor = jumpVector; LOGGER.trace(LoggerMarkers.Savestate, "Setting client gamemode"); // #86 @@ -289,11 +314,12 @@ public static void loadPlayer(NBTTagCompound compound) { GameType type = GameType.getByID(gamemode); mc.playerController.setGameType(type); - // #?? Player rotation does not change when loading a savestate - // CameraInterpolationEvents.rotationPitch = player.rotationPitch; - // CameraInterpolationEvents.rotationYaw = player.rotationYaw + 180f; + // Set the camera rotation to the player rotation + TASmodClient.virtual.CAMERA_ANGLE.setCamera(player.rotationPitch, player.rotationYaw); + SubtickDuck entityRenderer = (SubtickDuck) Minecraft.getMinecraft().entityRenderer; + entityRenderer.runUpdate(0); - SavestateHandlerClient.keepPlayerInLoadedEntityList(player); + EventListenerRegistry.fireEvent(EventSavestate.EventClientLoadPlayer.class, player); } /** @@ -312,7 +338,8 @@ public static void unloadAllClientChunks() { ChunkProviderClient chunkProvider = mc.world.getChunkProvider(); ((ChunkProviderDuck) chunkProvider).unloadAllChunks(); - Minecraft.getMinecraft().renderGlobal.loadRenderers(); + mc.renderGlobal.loadRenderers(); + ((WorldClientDuck) mc.world).clearEntityList(); } @Override @@ -321,8 +348,6 @@ public PacketID[] getAcceptedPacketIDs() { //@formatter:off TASmodPackets.SAVESTATE_SAVE, TASmodPackets.SAVESTATE_LOAD, - TASmodPackets.SAVESTATE_PLAYER, - TASmodPackets.SAVESTATE_REQUEST_MOTION, TASmodPackets.SAVESTATE_SCREEN, TASmodPackets.SAVESTATE_UNLOAD_CHUNKS }; //@formatter:on @@ -331,66 +356,41 @@ public PacketID[] getAcceptedPacketIDs() { @Override public void onClientPacket(PacketID id, ByteBuffer buf, String username) throws PacketNotImplementedException, WrongSideException, Exception { TASmodPackets packet = (TASmodPackets) id; - String name = null; Minecraft mc = Minecraft.getMinecraft(); switch (packet) { case SAVESTATE_SAVE: - // Create client savestate - name = TASmodBufferBuilder.readString(buf); - try { - SavestateHandlerClient.savestate(name); - } catch (SavestateException e) { - LOGGER.error(e.getMessage()); - } catch (IOException e) { - e.printStackTrace(); - } + String savestateName = TASmodBufferBuilder.readString(buf); + Minecraft.getMinecraft().addScheduledTask(() -> { + + // Create client savestate + try { + SavestateHandlerClient.savestate(savestateName); + } catch (SavestateException e) { + LOGGER.error(e.getMessage()); + } catch (IOException e) { + e.printStackTrace(); + } + }); break; case SAVESTATE_LOAD: // Load client savestate - name = TASmodBufferBuilder.readString(buf); - try { - SavestateHandlerClient.loadstate(name); - } catch (IOException e) { - e.printStackTrace(); - } - break; - case SAVESTATE_PLAYER: - NBTTagCompound compound; - try { - compound = TASmodBufferBuilder.readNBTTagCompound(buf); - } catch (IOException e) { - e.printStackTrace(); - return; - } - /* - * Fair warning: Do NOT read the buffer inside an addScheduledTask. Read it - * before that. The buffer will have the wrong limit, when the task is executed. - * This is probably due to the buffers being reused. - */ + String loadstateName = TASmodBufferBuilder.readString(buf); Minecraft.getMinecraft().addScheduledTask(() -> { - SavestateHandlerClient.loadPlayer(compound); - }); - break; - - case SAVESTATE_REQUEST_MOTION: - EntityPlayerSP player = Minecraft.getMinecraft().player; - if (player != null) { - if (!(Minecraft.getMinecraft().currentScreen instanceof GuiSavestateSavingScreen)) { - Minecraft.getMinecraft().displayGuiScreen(new GuiSavestateSavingScreen()); + try { + SavestateHandlerClient.loadstate(loadstateName); + } catch (IOException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); } - MotionData motionData = 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(motionData)); - } + }); break; case SAVESTATE_SCREEN: - // Open/Close Savestate screen - boolean open = TASmodBufferBuilder.readBoolean(buf); - if (open) { + // Open Savestate screen + Minecraft.getMinecraft().addScheduledTask(() -> { mc.displayGuiScreen(new GuiSavestateSavingScreen()); - } else { - mc.displayGuiScreen(null); - } + }); break; case SAVESTATE_UNLOAD_CHUNKS: @@ -402,7 +402,5 @@ public void onClientPacket(PacketID id, ByteBuffer buf, String username) throws default: throw new PacketNotImplementedException(packet, this.getClass(), Side.CLIENT); } - } - } diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerServer.java b/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerServer.java index 3809fb8a..fdb69c59 100644 --- a/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerServer.java +++ b/src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerServer.java @@ -1,24 +1,26 @@ package com.minecrafttas.tasmod.savestates; import static com.minecrafttas.tasmod.TASmod.LOGGER; +import static com.minecrafttas.tasmod.registries.TASmodPackets.CLEAR_SCREEN; import java.io.File; -import java.io.FileFilter; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Map; -import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Stream; import org.apache.commons.io.FileUtils; import org.apache.logging.log4j.Logger; -import com.google.common.collect.Maps; import com.minecrafttas.mctcommon.events.EventListenerRegistry; import com.minecrafttas.mctcommon.networking.Client.Side; import com.minecrafttas.mctcommon.networking.exception.PacketNotImplementedException; @@ -29,39 +31,28 @@ 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.registries.TASmodPackets; -import com.minecrafttas.tasmod.savestates.SavestateHandlerServer.PlayerHandler.MotionData; import com.minecrafttas.tasmod.savestates.exceptions.LoadstateException; import com.minecrafttas.tasmod.savestates.exceptions.SavestateDeleteException; import com.minecrafttas.tasmod.savestates.exceptions.SavestateException; import com.minecrafttas.tasmod.savestates.files.SavestateDataFile; import com.minecrafttas.tasmod.savestates.files.SavestateDataFile.DataValues; import com.minecrafttas.tasmod.savestates.files.SavestateTrackerFile; -import com.minecrafttas.tasmod.util.Ducks.ChunkProviderDuck; +import com.minecrafttas.tasmod.savestates.handlers.SavestatePlayerHandler; +import com.minecrafttas.tasmod.savestates.handlers.SavestateWorldHandler; import com.minecrafttas.tasmod.util.LoggerMarkers; +import com.minecrafttas.tasmod.util.Scheduler.Task; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.Minecraft; -import net.minecraft.entity.Entity; -import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.server.MinecraftServer; import net.minecraft.server.management.PlayerList; -import net.minecraft.util.math.MathHelper; +import net.minecraft.util.text.Style; import net.minecraft.util.text.TextComponentString; import net.minecraft.util.text.TextFormatting; -import net.minecraft.world.NextTickListEntry; -import net.minecraft.world.World; import net.minecraft.world.WorldServer; -import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.storage.AnvilChunkLoader; -import net.minecraft.world.gen.ChunkProviderServer; -import net.minecraft.world.storage.SaveHandler; -import net.minecraft.world.storage.WorldInfo; /** * Creates and loads savestates on both client and server without closing the @@ -76,18 +67,28 @@ */ public class SavestateHandlerServer implements ServerPacketHandler { - private MinecraftServer server; - private File savestateDirectory; + private final MinecraftServer server; + private Path savestateDirectory; public SavestateState state = SavestateState.NONE; + public static enum SavestateState { + SAVING, + LOADING, + NONE + } + private final List indexList = new ArrayList<>(); private int latestIndex = 0; private int currentIndex; + private final SavestatePlayerHandler playerHandler; + private final SavestateWorldHandler worldHandler; + + public static final Path storageDir = Paths.get("tasmod/"); + private final Logger logger; - public static boolean wasLoading; /** * Creates a savestate handler on the specified server @@ -98,6 +99,9 @@ public class SavestateHandlerServer implements ServerPacketHandler { public SavestateHandlerServer(MinecraftServer server, Logger logger) { this.server = server; this.logger = logger; + this.playerHandler = new SavestatePlayerHandler(server); + this.worldHandler = new SavestateWorldHandler(server); + createSavestateDirectory(); refresh(); loadCurrentIndexFromFile(); @@ -150,7 +154,7 @@ public void saveState(int savestateIndex, boolean tickrate0, boolean changeIndex try { // Open GuiSavestateScreen - TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.SAVESTATE_SCREEN).writeBoolean(true)); + TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.SAVESTATE_SCREEN)); } catch (Exception e) { e.printStackTrace(); } @@ -164,12 +168,6 @@ public void saveState(int savestateIndex, boolean tickrate0, boolean changeIndex // Enable tickrate 0 TASmod.tickratechanger.pauseGame(true); - // Update the server variable - server = TASmod.getServerInstance(); - - // Get the motion from the client - PlayerHandler.requestMotionFromClient(); - // Save the world! server.getPlayerList().saveAllPlayerData(); server.saveAllWorlds(false); @@ -192,14 +190,14 @@ public void saveState(int savestateIndex, boolean tickrate0, boolean changeIndex // Get the current and target directory for copying String worldname = server.getFolderName(); - File currentfolder = new File(savestateDirectory, ".." + File.separator + worldname); - File targetfolder = getSavestateFile(indexToSave); + Path currentfolder = savestateDirectory.resolve(".." + File.separator + worldname); + Path targetfolder = getSavestateFile(indexToSave); - EventListenerRegistry.fireEvent(EventSavestate.EventServerSavestate.class, indexToSave, targetfolder, currentfolder); + EventListenerRegistry.fireEvent(EventSavestate.EventServerSavestate.class, server, indexToSave, targetfolder, currentfolder); - if (targetfolder.exists()) { + if (Files.exists(targetfolder)) { logger.warn(LoggerMarkers.Savestate, "WARNING! Overwriting the savestate with the index {}", indexToSave); - FileUtils.deleteDirectory(targetfolder); + deleteFolder(targetfolder); } /* @@ -230,19 +228,18 @@ public void saveState(int savestateIndex, boolean tickrate0, boolean changeIndex saveSavestateDataFile(false); // Copy the directory - FileUtils.copyDirectory(currentfolder, targetfolder); + copyFolder(currentfolder, targetfolder); // Incrementing info file - SavestateTrackerFile tracker = new SavestateTrackerFile(new File(savestateDirectory, worldname + "-info.txt")); - tracker.increaseSavestates(); - tracker.saveFile(); + SavestateTrackerFile tracker = new SavestateTrackerFile(savestateDirectory.resolve(worldname + "-info.txt")); + tracker.increaseSaveStateCount(); // Send a notification that the savestate has been loaded server.getPlayerList().sendMessage(new TextComponentString(TextFormatting.GREEN + "Savestate " + indexToSave + " saved")); try { // close GuiSavestateScreen - TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.SAVESTATE_SCREEN).writeBoolean(false)); + TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.CLEAR_SCREEN)); } catch (Exception e) { e.printStackTrace(); } @@ -315,14 +312,11 @@ public void loadState(int savestateIndex, boolean tickrate0, boolean changeIndex // Enable tickrate 0 TASmod.tickratechanger.pauseGame(true); - // Update the server instance - server = TASmod.getServerInstance(); - refresh(); int indexToLoad = savestateIndex < 0 ? currentIndex : savestateIndex; - if (getSavestateFile(indexToLoad).exists()) { + if (Files.exists(getSavestateFile(indexToLoad))) { // Updating current index if (changeIndex) { setCurrentIndex(indexToLoad); @@ -335,10 +329,10 @@ public void loadState(int savestateIndex, boolean tickrate0, boolean changeIndex // Get the current and target directory for copying String worldname = server.getFolderName(); - File currentfolder = new File(savestateDirectory, ".." + File.separator + worldname); - File targetfolder = getSavestateFile(indexToLoad); + Path currentfolder = savestateDirectory.resolve(".." + File.separator + worldname); + Path targetfolder = getSavestateFile(indexToLoad); - EventListenerRegistry.fireEvent(EventSavestate.EventServerLoadstate.class, indexToLoad, targetfolder, currentfolder); + EventListenerRegistry.fireEvent(EventSavestate.EventServerLoadstate.class, server, indexToLoad, targetfolder, currentfolder); /* * Prevents loading an InputSavestate when loading index 0 (Index 0 is the @@ -356,9 +350,7 @@ public void loadState(int savestateIndex, boolean tickrate0, boolean changeIndex // Disabeling level saving for all worlds in case the auto save kicks in during // world unload - for (WorldServer world : server.worlds) { - world.disableLevelSaving = true; - } + worldHandler.disableLevelSaving(); try { // unload chunks on client @@ -368,46 +360,50 @@ public void loadState(int savestateIndex, boolean tickrate0, boolean changeIndex } // Unload chunks on the server - ChunkHandler.disconnectPlayersFromChunkMap(server); - ChunkHandler.unloadAllServerChunks(server); - ChunkHandler.flushSaveHandler(server); + worldHandler.disconnectPlayersFromChunkMap(); + worldHandler.unloadAllServerChunks(); + worldHandler.flushSaveHandler(); // Delete and copy directories - FileUtils.deleteDirectory(currentfolder); - FileUtils.copyDirectory(targetfolder, currentfolder); + deleteFolder(currentfolder); + copyFolder(targetfolder, currentfolder); // Loads savestate data from the file like name and ktrng seed if ktrng is loaded loadSavestateDataFile(); + playerHandler.clearScoreboard(); + + // Load the world from disk +// server.loadAllWorlds(worldname, worldname, 0, WorldType.DEFAULT, ""); + worldHandler.loadAllWorlds(worldname, worldname); + // Update the player and the client - PlayerHandler.loadAndSendMotionToPlayer(server); - // Update the session.lock file so minecraft behaves and saves the world - ChunkHandler.updateSessionLock(server); + playerHandler.loadAndSendMotionToPlayer(); + // Load the chunks and send them to the client - ChunkHandler.addPlayersToChunkMap(server); + worldHandler.addPlayersToChunkMap(); - // Enable level saving again - for (WorldServer world : server.worlds) { - world.disableLevelSaving = false; - } + // Reenable level saving + worldHandler.enableLevelSaving(); // Incrementing info file - SavestateTrackerFile tracker = new SavestateTrackerFile(new File(savestateDirectory, worldname + "-info.txt")); - tracker.increaseRerecords(); - tracker.saveFile(); + SavestateTrackerFile tracker = new SavestateTrackerFile(savestateDirectory.resolve(worldname + "-info.txt")); + tracker.increaseLoadstateCount(); // Send a notification that the savestate has been loaded server.getPlayerList().sendMessage(new TextComponentString(TextFormatting.GREEN + "Savestate " + indexToLoad + " loaded")); // Add players to the chunk server.getPlayerList().getPlayers().forEach(player -> { - ChunkHandler.addPlayerToServerChunk(player); + worldHandler.addPlayerToServerChunk(player); }); - WorldServer[] worlds = server.worlds; + worldHandler.sendChunksToClient(); - for (WorldServer world : worlds) { - world.tick(); + try { + TASmod.server.sendToAll(new TASmodBufferBuilder(CLEAR_SCREEN)); + } catch (Exception e) { + LOGGER.catching(e); } if (!tickrate0) { @@ -429,13 +425,21 @@ public void loadState(int savestateIndex, boolean tickrate0, boolean changeIndex */ private void createSavestateDirectory() { logger.trace(LoggerMarkers.Savestate, "Creating savestate directory"); + + Path dataDirectory = server.getDataDirectory().toPath(); + if (!server.isDedicatedServer()) { - savestateDirectory = new File(server.getDataDirectory() + File.separator + "saves" + File.separator + "savestates" + File.separator); + savestateDirectory = dataDirectory.resolve("saves/savestates"); } else { - savestateDirectory = new File(server.getDataDirectory() + File.separator + "savestates" + File.separator); + savestateDirectory = dataDirectory.resolve("savestates"); } - if (!savestateDirectory.exists()) { - savestateDirectory.mkdir(); + if (!Files.exists(savestateDirectory)) { + try { + Files.createDirectory(savestateDirectory); + } catch (IOException e) { + logger.error("Could not create savestate directory"); + logger.catching(e); + } } } @@ -445,31 +449,42 @@ private void createSavestateDirectory() { private void refresh() { logger.trace(LoggerMarkers.Savestate, "Refreshing savestate list"); indexList.clear(); - File[] files = savestateDirectory.listFiles(new FileFilter() { + if (!Files.isDirectory(savestateDirectory)) { + logger.error("Savestate directory is not a directory! {}", savestateDirectory.toAbsolutePath().toString()); + return; + } - @Override - public boolean accept(File pathname) { - return pathname.getName().startsWith(server.getFolderName() + "-Savestate"); - } + Stream files = null; + try { + files = Files.list(savestateDirectory); + } catch (IOException e) { + logger.error("Can't refresh savestatelist"); + logger.catching(e); + return; + } + Stream filteredfiles = files.filter(file -> file.getFileName().toString().startsWith(server.getFolderName() + "-Savestate")); - }); - int index = 0; - for (File file : files) { + filteredfiles.forEach(file -> { + int index = 0; try { Pattern patt = Pattern.compile("\\d+$"); - Matcher matcher = patt.matcher(file.getName()); + Matcher matcher = patt.matcher(file.getFileName().toString()); if (matcher.find()) { index = Integer.parseInt(matcher.group(0)); } else { - logger.warn(String.format("Could not process the savestate %s", file.getName())); - continue; + logger.warn(String.format("Could not process the savestate %s", file.getFileName())); + return; } } catch (NumberFormatException e) { logger.warn(String.format("Could not process the savestate %s", e.getMessage())); - continue; + return; } indexList.add(index); - } + }); + + filteredfiles.close(); + files.close(); + Collections.sort(indexList); if (!indexList.isEmpty()) { latestIndex = indexList.get(indexList.size() - 1); @@ -482,8 +497,8 @@ public boolean accept(File pathname) { * @param index The index of the savestate file that we want to get * @return The file of the savestate from the specified index */ - private File getSavestateFile(int index) { - return new File(savestateDirectory, getSavestateName(index)); + private Path getSavestateFile(int index) { + return savestateDirectory.resolve(getSavestateName(index)); } /** @@ -514,17 +529,19 @@ public void deleteSavestate(int index) throws SavestateDeleteException { if (index == 0) { throw new SavestateDeleteException("Cannot delete protected savestate 0"); } - File toDelete = getSavestateFile(index); - if (toDelete.exists()) { - try { - FileUtils.deleteDirectory(toDelete); - } catch (IOException e) { - e.printStackTrace(); - throw new SavestateDeleteException("Something went wrong while trying to delete the savestate " + index); - } + + Path toDelete = getSavestateFile(index); + if (Files.exists(getSavestateFile(index))) { +// try { + deleteFolder(toDelete); +// } catch (IOException e) { +// e.printStackTrace(); +// throw new SavestateDeleteException("Something went wrong while trying to delete the savestate " + index); +// } } else { throw new SavestateDeleteException(TextFormatting.YELLOW + "Savestate " + index + " doesn't exist, so it can't be deleted"); } + refresh(); if (!indexList.contains(currentIndex)) { setCurrentIndex(latestIndex); @@ -582,25 +599,31 @@ public String getIndexesAsString() { */ private void saveSavestateDataFile(boolean legacy) { logger.trace(LoggerMarkers.Savestate, "Saving savestate data file"); - File tasmodDir = new File(savestateDirectory, "../" + server.getFolderName() + "/tasmod/"); - if (!tasmodDir.exists()) { - tasmodDir.mkdir(); + Path tasmodDir = savestateDirectory.resolve("../" + server.getFolderName() + "/tasmod/"); + if (!Files.exists(tasmodDir)) { + try { + Files.createDirectories(tasmodDir); + } catch (IOException e) { + e.printStackTrace(); + } } - File savestateDat = new File(tasmodDir, "savestateData.txt"); - if (savestateDat.exists()) { - savestateDat.delete(); + Path savestateDat = tasmodDir.resolve("savestateData.txt"); + try { + Files.deleteIfExists(savestateDat); + } catch (IOException e) { + e.printStackTrace(); } - SavestateDataFile file = new SavestateDataFile(); + SavestateDataFile file = new SavestateDataFile(savestateDat); file.set(DataValues.INDEX, Integer.toString(currentIndex)); - if (!legacy) { - if (TASmod.ktrngHandler.isLoaded()) { - file.set(DataValues.SEED, Long.toString(TASmod.ktrngHandler.getGlobalSeedServer())); - } - } + // if (!legacy) { + // if (TASmod.ktrngHandler.isLoaded()) { + // file.set(DataValues.SEED, Long.toString(TASmod.ktrngHandler.getGlobalSeedServer())); + // } + // } file.save(savestateDat); } @@ -612,25 +635,25 @@ private void saveSavestateDataFile(boolean legacy) { */ private void loadSavestateDataFile() { logger.trace(LoggerMarkers.Savestate, "Loading savestate data file"); - File tasmodDir = new File(savestateDirectory, "../" + server.getFolderName() + "/tasmod/"); - File savestateDat = new File(tasmodDir, "savestateData.txt"); + Path tasmodDir = savestateDirectory.resolve("../" + server.getFolderName()).resolve(storageDir); + Path savestateDat = tasmodDir.resolve("savestateData.txt"); - if (!savestateDat.exists()) { + if (!Files.exists(savestateDat)) { return; } - SavestateDataFile datafile = new SavestateDataFile(); + SavestateDataFile datafile = new SavestateDataFile(savestateDirectory); datafile.load(savestateDat); - if (TASmod.ktrngHandler.isLoaded()) { - String seedString = datafile.get(DataValues.SEED); - if (seedString != null) { - TASmod.ktrngHandler.sendGlobalSeedToServer(Long.parseLong(seedString)); - } else { - logger.warn("KTRNG seed not loaded because it was not found in savestateData.txt!"); - } - } + // if (TASmod.ktrngHandler.isLoaded()) { + // String seedString = datafile.get(DataValues.SEED); + // if (seedString != null) { + // TASmod.ktrngHandler.sendGlobalSeedToServer(Long.parseLong(seedString)); + // } else { + // logger.warn("KTRNG seed not loaded because it was not found in savestateData.txt!"); + // } + // } } /** @@ -642,23 +665,31 @@ private void loadSavestateDataFile() { public void loadCurrentIndexFromFile() { logger.trace(LoggerMarkers.Savestate, "Loading current index from file"); int index = -1; - File tasmodDir = new File(savestateDirectory, "../" + server.getFolderName() + "/tasmod/"); - if (!tasmodDir.exists()) { - tasmodDir.mkdir(); + Path tasmodDir = savestateDirectory.resolve("../" + server.getFolderName()).resolve(storageDir); + if (!Files.exists(tasmodDir)) { + try { + Files.createDirectory(tasmodDir); + } catch (IOException e) { + e.printStackTrace(); + } } - File savestateDat = new File(tasmodDir, "savestate.data"); - if (savestateDat.exists()) { + Path savestateDat = tasmodDir.resolve("savestate.data"); + if (Files.exists(savestateDat)) { index = legacyIndexFile(savestateDat); setCurrentIndex(index); saveSavestateDataFile(true); - savestateDat.delete(); + try { + Files.delete(savestateDat); + } catch (IOException e) { + e.printStackTrace(); + } return; } - savestateDat = new File(tasmodDir, "savestateData.txt"); - if (savestateDat.exists()) { - SavestateDataFile file = new SavestateDataFile(); + savestateDat = tasmodDir.resolve("savestateData.txt"); + if (Files.exists(savestateDat)) { + SavestateDataFile file = new SavestateDataFile(savestateDat); file.load(savestateDat); index = Integer.parseInt(file.get(DataValues.INDEX)); @@ -676,31 +707,28 @@ private void setCurrentIndex(int index) { logger.debug(LoggerMarkers.Savestate, "Setting the savestate index to {}", currentIndex); } + public SavestatePlayerHandler getPlayerHandler() { + return playerHandler; + } + public int getCurrentIndex() { return currentIndex; } public void onLoadstateComplete() { logger.trace(LoggerMarkers.Savestate, "Running loadstate complete event"); - PlayerList playerList = TASmod.getServerInstance().getPlayerList(); + PlayerList playerList = server.getPlayerList(); for (EntityPlayerMP player : playerList.getPlayers()) { NBTTagCompound nbttagcompound = playerList.readPlayerDataFromFile(player); - PlayerHandler.reattachEntityToPlayer(nbttagcompound, player.getServerWorld(), player); + playerHandler.reattachEntityToPlayer(nbttagcompound, player.getServerWorld(), player); } - // Updating redstone component timers to the new world time (#136) - ChunkHandler.updateWorldServerTickListEntries(); - } - - @Environment(EnvType.CLIENT) - public static void playerLoadSavestateEventClient() { - SavestateHandlerClient.addPlayerToClientChunk(Minecraft.getMinecraft().player); } - private int legacyIndexFile(File savestateDat) { + private int legacyIndexFile(Path savestateDat) { int index = -1; List lines = new ArrayList(); try { - lines = FileUtils.readLines(savestateDat, StandardCharsets.UTF_8); + lines = FileUtils.readLines(savestateDat.toFile(), StandardCharsets.UTF_8); } catch (IOException e) { logger.warn("No savestate.data file found in current world folder, ignoring it"); } @@ -718,21 +746,15 @@ private int legacyIndexFile(File savestateDat) { return index; } - public static enum SavestateState { - SAVING, - LOADING, - NONE - } - @Override public PacketID[] getAcceptedPacketIDs() { return new TASmodPackets[] { - TASmodPackets.SAVESTATE_SAVE, - TASmodPackets.SAVESTATE_LOAD, - TASmodPackets.SAVESTATE_PLAYER, - TASmodPackets.SAVESTATE_REQUEST_MOTION, - TASmodPackets.SAVESTATE_SCREEN, + //@formatter:off + TASmodPackets.SAVESTATE_SAVE, + TASmodPackets.SAVESTATE_LOAD, + TASmodPackets.SAVESTATE_SCREEN, TASmodPackets.SAVESTATE_UNLOAD_CHUNKS + //@formatter:on }; } @@ -742,31 +764,39 @@ public void onServerPacket(PacketID id, ByteBuffer buf, String username) throws TASmodPackets packet = (TASmodPackets) id; EntityPlayerMP player = TASmod.getServerInstance().getPlayerList().getPlayerByUsername(username); - Integer index = null; switch (packet) { case SAVESTATE_SAVE: - index = TASmodBufferBuilder.readInt(buf); - try { - TASmod.savestateHandlerServer.saveState(index, true); - } catch (SavestateException e) { - if (player != null) - player.sendMessage(new TextComponentString(TextFormatting.RED + "Failed to create a savestate: " + e.getMessage())); + Integer index = TASmodBufferBuilder.readInt(buf); - LOGGER.error(LoggerMarkers.Savestate, "Failed to create a savestate: " + e.getMessage()); - } catch (Exception e) { - if (player != null) - player.sendMessage(new TextComponentString(TextFormatting.RED + "Failed to create a savestate: " + e.getCause().toString())); + Task savestateTask = () -> { + try { + TASmod.savestateHandlerServer.saveState(index, true); + } catch (SavestateException e) { + if (player != null) + player.sendMessage(new TextComponentString(TextFormatting.RED + "Failed to create a savestate: " + e.getMessage())); - LOGGER.error(e); - } finally { - TASmod.savestateHandlerServer.state = SavestateState.NONE; - } + LOGGER.error(LoggerMarkers.Savestate, "Failed to create a savestate"); + LOGGER.catching(e); + } catch (Exception e) { + if (player != null) + player.sendMessage(new TextComponentString(TextFormatting.RED + "Failed to create a savestate: " + e.getClass().getName().toString() + ": " + e.getMessage())); + + LOGGER.catching(e); + } finally { + TASmod.savestateHandlerServer.state = SavestateState.NONE; + } + }; + + if (TASmod.tickratechanger.ticksPerSecond == 0) + TASmod.gameLoopSchedulerServer.add(savestateTask); + else + TASmod.tickSchedulerServer.add(savestateTask); break; case SAVESTATE_LOAD: int indexing = TASmodBufferBuilder.readInt(buf); - player.getServerWorld().addScheduledTask(() -> { + Task loadstateTask = () -> { try { TASmod.savestateHandlerServer.loadState(indexing, true); } catch (LoadstateException e) { @@ -776,20 +806,21 @@ public void onServerPacket(PacketID id, ByteBuffer buf, String username) throws LOGGER.error(LoggerMarkers.Savestate, "Failed to create a savestate: " + e.getMessage()); TASmod.savestateHandlerServer.state = SavestateState.NONE; } catch (Exception e) { - if (player != null) - player.sendMessage(new TextComponentString(TextFormatting.RED + "Failed to load a savestate: " + e.getCause().toString())); + if (player != null) { + Throwable cause = e.getCause(); + if (cause == null) { + cause = e; + } + player.sendMessage(new TextComponentString(String.format("Failed to load a savestate: %s", cause.getMessage())).setStyle(new Style().setColor(TextFormatting.RED))); + } - LOGGER.error(e); + LOGGER.throwing(e); TASmod.savestateHandlerServer.state = SavestateState.NONE; } - }); + }; + TASmod.gameLoopSchedulerServer.add(loadstateTask); break; - case SAVESTATE_REQUEST_MOTION: - MotionData data = TASmodBufferBuilder.readMotionData(buf); - PlayerHandler.getMotion().put(player, data); - break; - case SAVESTATE_PLAYER: case SAVESTATE_SCREEN: case SAVESTATE_UNLOAD_CHUNKS: throw new WrongSideException(id, Side.SERVER); @@ -798,316 +829,44 @@ public void onServerPacket(PacketID id, ByteBuffer buf, String username) throws } } - /** - * Contains player related classes - */ - public static class PlayerHandler { - - public static class MotionData { - private double clientX; - private double clientY; - private double clientZ; - private float clientrX; - private float clientrY; - private float clientrZ; - private boolean sprinting; - private float jumpMovementVector; - - public MotionData(double x, double y, double z, float rx, float ry, float rz, boolean sprinting, float jumpMovementVector) { - clientX = x; - clientY = y; - clientZ = z; - clientrX = rx; - clientrY = ry; - clientrZ = rz; - this.sprinting = sprinting; - this.jumpMovementVector = jumpMovementVector; - } - - public double getClientX() { - return clientX; - } - - public double getClientY() { - return clientY; - } - - public double getClientZ() { - return clientZ; - } - - public float getClientrX() { - return clientrX; - } - - public float getClientrY() { - return clientrY; - } - - public float getClientrZ() { - return clientrZ; - } - - public boolean isSprinting() { - return sprinting; - } - - public float getJumpMovementVector() { - return jumpMovementVector; - } - } - - /** - * Tries to reattach the player to an entity, if the player was riding it it while savestating. - * - * Side: Server - * @param nbttagcompound where the ridden entity is saved - * @param worldserver that needs to spawn the entity - * @param playerIn that needs to ride the entity - */ - public static void reattachEntityToPlayer(NBTTagCompound nbttagcompound, World worldserver, Entity playerIn) { - if (nbttagcompound != null && nbttagcompound.hasKey("RootVehicle", 10)) { - NBTTagCompound nbttagcompound1 = nbttagcompound.getCompoundTag("RootVehicle"); - Entity entity1 = AnvilChunkLoader.readWorldEntity(nbttagcompound1.getCompoundTag("Entity"), worldserver, true); - - if (entity1 == null) { - for (Entity entity : worldserver.loadedEntityList) { - if (entity.getUniqueID().equals(nbttagcompound1.getUniqueId("Attach"))) - entity1 = entity; - } - } - - if (entity1 != null) { - UUID uuid = nbttagcompound1.getUniqueId("Attach"); - - if (entity1.getUniqueID().equals(uuid)) { - playerIn.startRiding(entity1, true); - } else { - for (Entity entity : entity1.getRecursivePassengers()) { - if (entity.getUniqueID().equals(uuid)) { - playerIn.startRiding(entity, true); - break; - } - } - } - - if (!playerIn.isRiding()) { - LOGGER.warn("Couldn't reattach entity to player"); - worldserver.removeEntityDangerously(entity1); - - for (Entity entity2 : entity1.getRecursivePassengers()) { - worldserver.removeEntityDangerously(entity2); - } - } - } - } else { - if (playerIn.isRiding()) { - playerIn.dismountRidingEntity(); - } - } - } - - /** - * Loads all worlds and players from the disk. Also sends the playerdata to the client in {@linkplain SavestateHandlerClient#onClientPacket(PacketID, ByteBuffer, String)} - * - * Side: Server - */ - public static void loadAndSendMotionToPlayer(MinecraftServer server) { - - List players = server.getPlayerList().getPlayers(); - PlayerList list = server.getPlayerList(); - - WorldServer[] worlds = server.worlds; - for (WorldServer world : worlds) { - WorldInfo info = world.getSaveHandler().loadWorldInfo(); - world.worldInfo = info; - } - for (EntityPlayerMP player : players) { - - int dimensionPrev = player.dimension; - - NBTTagCompound nbttagcompound = server.getPlayerList().readPlayerDataFromFile(player); - - int dimensionNow = 0; - if (nbttagcompound.hasKey("Dimension")) { - dimensionNow = nbttagcompound.getInteger("Dimension"); - } - - if (dimensionNow != dimensionPrev) { - list.changePlayerDimension(player, dimensionNow); - } else { - player.getServerWorld().unloadedEntityList.remove(player); - } - - player.readFromNBT(nbttagcompound); - - LOGGER.debug(LoggerMarkers.Savestate, "Sending motion to {}", player.getName()); - + public static void copyFolder(Path src, Path dest) { + try { + Files.walk(src).forEach(s -> { try { - TASmod.server.sendTo(player, new TASmodBufferBuilder(TASmodPackets.SAVESTATE_PLAYER).writeNBTTagCompound(nbttagcompound)); + Path d = dest.resolve(src.relativize(s)); + if (Files.isDirectory(s)) { + if (!Files.exists(d)) + Files.createDirectory(d); + return; + } + Files.copy(s, d, StandardCopyOption.REPLACE_EXISTING); } catch (Exception e) { e.printStackTrace(); } - } - } - - public static void requestMotionFromClient() { - LOGGER.trace(LoggerMarkers.Savestate, "Request motion from client"); - PlayerHandler.motion.clear(); - try { - // request client motion - TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.SAVESTATE_REQUEST_MOTION)); - } catch (Exception e) { - e.printStackTrace(); - } - } - - public static Map getMotion() { - return PlayerHandler.motion; + }); + } catch (Exception e) { + e.printStackTrace(); } - - public static Map motion = Maps.newHashMap(); - } - /** - * Contains static chunk actions, which can be triggered indiviadually for testing - */ - public static class ChunkHandler { - - /** - * Updates ticklist entries to the current world time, allowing them to not be stuck in a pressed state #136 - */ - public static void updateWorldServerTickListEntries() { - LOGGER.trace(LoggerMarkers.Savestate, "Update server tick list entries"); - MinecraftServer server = TASmod.getServerInstance(); - for (WorldServer world : server.worlds) { - for (NextTickListEntry nextticklistentry : world.pendingTickListEntriesHashSet) { - nextticklistentry.setScheduledTime(world.getTotalWorldTime()); - } - } - } - - /** - * Just like {@link SavestateHandlerClient#addPlayerToClientChunk(EntityPlayer)}, adds the player to the chunk on the server. - * This prevents the player from being able to place block inside of him - * - * Side: Server - */ - public static void addPlayerToServerChunk(EntityPlayerMP player) { - LOGGER.trace(LoggerMarkers.Savestate, "Add player {} to server chunk", player.getName()); - int i = MathHelper.floor(player.posX / 16.0D); - int j = MathHelper.floor(player.posZ / 16.0D); - WorldServer world = player.getServerWorld(); - Chunk chunk = world.getChunkFromChunkCoords(i, j); - for (int k = 0; k < chunk.getEntityLists().length; k++) { - if (chunk.getEntityLists()[k].contains(player)) { + public static void deleteFolder(Path toDelete) { + try { + Files.walk(toDelete).forEach(s -> { + if (toDelete.equals(s)) return; + if (Files.isDirectory(s)) { + deleteFolder(s); + } else { + try { + Files.delete(s); + } catch (IOException e) { + e.printStackTrace(); + } } - } - chunk.addEntity(player); - } - - /** - * The session lock is minecrafts failsafe system when it comes to saving. It prevents writing to the world folder from 2 different locations
- *
- * That works by storing system time to a session.lock file, when the server started. The integrated server also saves the time when it started in a variable.
- *
- * Those two times are then compared every time minecraft tries to save and fails if the times are different.
- *
- * Since we never close the integrated server, but copy an "old" session.lock file with the savestate, the session.lock will always mismatch.
- * Thus we need to update the session lock once the loadstating is completed
- *
- * TLDR:
- * Updates the session lock to allow for vanilla saving again
- *
- * Side: Server - */ - public static void updateSessionLock(MinecraftServer server) { - LOGGER.trace(LoggerMarkers.Savestate, "Update the session lock"); - WorldServer[] worlds = server.worlds; - for (WorldServer world : worlds) { - ((SaveHandler) world.getSaveHandler()).setSessionLock(); - } - } - - /** - * Tells the save handler to save all changes to disk and remove all references to the region files, making them editable on disc
- *
- * Side: Server - */ - public static void flushSaveHandler(MinecraftServer server) { - LOGGER.trace(LoggerMarkers.Savestate, "Flush the save handler"); - //Vanilla - WorldServer[] worlds = server.worlds; - for (WorldServer world : worlds) { - world.getSaveHandler().flush(); - } - } - - /** - * The player chunk map keeps track of which chunks need to be sent to the client.
- * This adds the player to the chunk map so the server knows it can send the information to the client
- *
- * Side: Server - * @see #disconnectPlayersFromChunkMap(MinecraftServer) - */ - public static void addPlayersToChunkMap(MinecraftServer server) { - List players = server.getPlayerList().getPlayers(); - WorldServer[] worlds = server.worlds; - for (EntityPlayerMP player : players) { - LOGGER.trace(LoggerMarkers.Savestate, "Add player {} to the chunk map", player.getName()); - switch (player.dimension) { - case -1: - worlds[1].getPlayerChunkMap().addPlayer(player); - worlds[1].getChunkProvider().provideChunk((int) player.posX >> 4, (int) player.posZ >> 4); - break; - case 0: - worlds[0].getPlayerChunkMap().addPlayer(player); - worlds[0].getChunkProvider().provideChunk((int) player.posX >> 4, (int) player.posZ >> 4); - break; - case 1: - worlds[2].getPlayerChunkMap().addPlayer(player); - worlds[2].getChunkProvider().provideChunk((int) player.posX >> 4, (int) player.posZ >> 4); - break; - } - } - } - - /** - * The player chunk map keeps track of which chunks need to be sent to the client.
- * Removing the player stops the server from sending chunks to the client.
- *
- * Side: Server - * @see #addPlayersToChunkMap(MinecraftServer) - */ - public static void disconnectPlayersFromChunkMap(MinecraftServer server) { - List players = server.getPlayerList().getPlayers(); - WorldServer[] worlds = server.worlds; - for (WorldServer world : worlds) { - for (EntityPlayerMP player : players) { - LOGGER.trace(LoggerMarkers.Savestate, "Disconnect player {} from the chunk map", player.getName()); - world.getPlayerChunkMap().removePlayer(player); - } - } - } - - /** - * Unloads all chunks on the server
- *
- * Side: Server - * @see MixinChunkProviderServer#unloadAllChunks() - */ - public static void unloadAllServerChunks(MinecraftServer server) { - LOGGER.trace(LoggerMarkers.Savestate, "Unloading all server chunks"); - WorldServer[] worlds = server.worlds; - - for (WorldServer world : worlds) { - ChunkProviderServer chunkProvider = world.getChunkProvider(); - - ((ChunkProviderDuck) chunkProvider).unloadAllChunks(); - } - + }); + Files.delete(toDelete); + } catch (Exception e) { + e.printStackTrace(); } } } diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/exceptions/LoadstateException.java b/src/main/java/com/minecrafttas/tasmod/savestates/exceptions/LoadstateException.java index aa6419f6..febeeb26 100644 --- a/src/main/java/com/minecrafttas/tasmod/savestates/exceptions/LoadstateException.java +++ b/src/main/java/com/minecrafttas/tasmod/savestates/exceptions/LoadstateException.java @@ -1,6 +1,6 @@ package com.minecrafttas.tasmod.savestates.exceptions; -public class LoadstateException extends Exception{ +public class LoadstateException extends RuntimeException { /** * */ @@ -9,4 +9,8 @@ public class LoadstateException extends Exception{ public LoadstateException(String s) { super(s); } + + public LoadstateException(Throwable t, String msg, Object... args) { + super(String.format(msg, args), t); + } } diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/exceptions/SavestateException.java b/src/main/java/com/minecrafttas/tasmod/savestates/exceptions/SavestateException.java index bfe1ff33..73d17e06 100644 --- a/src/main/java/com/minecrafttas/tasmod/savestates/exceptions/SavestateException.java +++ b/src/main/java/com/minecrafttas/tasmod/savestates/exceptions/SavestateException.java @@ -1,12 +1,23 @@ package com.minecrafttas.tasmod.savestates.exceptions; -public class SavestateException extends Exception{ - /** - * - */ - private static final long serialVersionUID = 3011404784481488693L; - - public SavestateException(String s) { - super(s); +public class SavestateException extends RuntimeException { + + public SavestateException() { + } + + public SavestateException(String msg) { + super(msg); + } + + public SavestateException(String msg, Object... args) { + super(String.format(msg, args)); + } + + public SavestateException(Throwable t, String msg) { + super(msg, t); + } + + public SavestateException(Throwable t, String msg, Object... args) { + super(String.format(msg, args), t); } } diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/files/SavestateDataFile.java b/src/main/java/com/minecrafttas/tasmod/savestates/files/SavestateDataFile.java index c41b4f8f..7b6d6c6a 100644 --- a/src/main/java/com/minecrafttas/tasmod/savestates/files/SavestateDataFile.java +++ b/src/main/java/com/minecrafttas/tasmod/savestates/files/SavestateDataFile.java @@ -1,65 +1,36 @@ package com.minecrafttas.tasmod.savestates.files; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.io.Reader; -import java.io.Writer; -import java.util.InvalidPropertiesFormatException; -import java.util.Properties; +import java.nio.file.Path; + +import com.minecrafttas.mctcommon.file.AbstractDataFile; + +public class SavestateDataFile extends AbstractDataFile { + + public SavestateDataFile(Path file) { + super(file, "savestatedata", "Data for this savestate from TASmod"); + } -public class SavestateDataFile { - public enum DataValues { INDEX("currentIndex"), NAME("savestateName"), SEED("ktrngSeed"); - - + private String configname; - + private DataValues(String configname) { - this.configname=configname; + this.configname = configname; } - + public String getConfigName() { return configname; } } - - Properties properties = new Properties(); - + public void set(DataValues key, String val) { properties.setProperty(key.getConfigName(), val); } - + public String get(DataValues key) { return properties.getProperty(key.getConfigName()); } - - public void save(File file) { - try { - Writer writer = new FileWriter(file); - properties.store(writer, "Data for this savestate from TASmod"); - writer.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - public void load(File file) { - try { - Reader reader = new FileReader(file); - properties.load(reader); - reader.close(); - } catch (InvalidPropertiesFormatException e) { - e.printStackTrace(); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - } } diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/files/SavestateTrackerFile.java b/src/main/java/com/minecrafttas/tasmod/savestates/files/SavestateTrackerFile.java index 95a64fdd..6bec0bb3 100644 --- a/src/main/java/com/minecrafttas/tasmod/savestates/files/SavestateTrackerFile.java +++ b/src/main/java/com/minecrafttas/tasmod/savestates/files/SavestateTrackerFile.java @@ -1,65 +1,74 @@ package com.minecrafttas.tasmod.savestates.files; -import java.io.File; import java.io.IOException; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; +import java.nio.file.Files; +import java.nio.file.Path; -import org.apache.commons.io.FileUtils; +import com.minecrafttas.mctcommon.file.AbstractDataFile; +import com.minecrafttas.tasmod.TASmod; /** * Used to keep track of savestates and rerecordings * @author Scribble * */ -public class SavestateTrackerFile { +public class SavestateTrackerFile extends AbstractDataFile { - private final File saveLocation; - - private final String top="# This file was generated by TASmod/LoTAS and diplays info about the usage of savestates\n\n"; // This shouldn't matter... static is fine! - public static int savestatecount; - public static int loadstatecount; - public SavestateTrackerFile(File saveLocation) throws IOException { - this.saveLocation=saveLocation; - if(!saveLocation.exists()) { - savestatecount=0; - loadstatecount=0; + public SavestateTrackerFile(Path saveLocation) throws IOException { + super(saveLocation, "savestate tracker", "This file was generated by TASmod/LoTAS and diplays info about the usage of savestates"); + + if (!Files.exists(saveLocation)) { + setSavestateCount(0); + setLoadstateCount(0); + save(); } else { - loadFile(); + load(); } } - - private void loadFile() throws IOException { - List lines=FileUtils.readLines(saveLocation, Charset.defaultCharset()); - - lines.forEach(line->{ - if(line.startsWith("Total Savestates")) { - savestatecount=Integer.parseInt(line.split("=")[1]); - }else if(line.startsWith("Total Rerecords")){ - loadstatecount=Integer.parseInt(line.split("=")[1]); - } - }); + + public int getSavestateCount() { + load(); + return loadCount("Total Savestates"); } - public void increaseSavestates() { - savestatecount++; + private void setSavestateCount(int savestatecount) { + setCount("Total Savestates", savestatecount); } - - public void increaseRerecords() { - loadstatecount++; + + public void increaseSaveStateCount() { + setSavestateCount(getSavestateCount() + 1); + save(); } - - public void saveFile() throws IOException { - List lines= new ArrayList(); - - lines.add(top); - - lines.add("Total Savestates="+savestatecount+"\nTotal Rerecords="+loadstatecount); - - FileUtils.writeLines(saveLocation, lines); + + public int getLoadstateCount() { + load(); + return loadCount("Total Loadstates"); + } + + public void setLoadstateCount(int loadstatecount) { + setCount("Total Loadstates", loadstatecount); + save(); + } + + public void increaseLoadstateCount() { + setLoadstateCount(getLoadstateCount() + 1); + } + + private int loadCount(String key) { + String value = properties.getProperty(key, "0"); + int count = 0; + try { + count = Integer.parseInt(value); + } catch (NumberFormatException e) { + TASmod.LOGGER.error("The {} could not be read in {}. The value {} is not a number", key, name, e.getMessage()); + } + return count; + } + + private void setCount(String key, int count) { + properties.setProperty(key, Integer.toString(count)); } } diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/handlers/SavestatePlayerHandler.java b/src/main/java/com/minecrafttas/tasmod/savestates/handlers/SavestatePlayerHandler.java new file mode 100644 index 00000000..cdab03e2 --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/savestates/handlers/SavestatePlayerHandler.java @@ -0,0 +1,229 @@ +package com.minecrafttas.tasmod.savestates.handlers; + +import static com.minecrafttas.tasmod.TASmod.LOGGER; +import static com.minecrafttas.tasmod.registries.TASmodPackets.SAVESTATE_PLAYER; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.List; +import java.util.UUID; + +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.networking.TASmodBufferBuilder; +import com.minecrafttas.tasmod.registries.TASmodPackets; +import com.minecrafttas.tasmod.savestates.SavestateHandlerClient; +import com.minecrafttas.tasmod.util.LoggerMarkers; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.Minecraft; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.play.server.SPacketRespawn; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.management.PlayerList; +import net.minecraft.world.World; +import net.minecraft.world.WorldServer; +import net.minecraft.world.chunk.storage.AnvilChunkLoader; + +/** + * Handles player related savestating methods + */ +public class SavestatePlayerHandler implements ClientPacketHandler, ServerPacketHandler { + + private final MinecraftServer server; + + public SavestatePlayerHandler(MinecraftServer server) { + this.server = server; + } + + /** + * Tries to reattach the player to an entity, if the player was riding it it while savestating. + * + * Side: Server + * @param nbttagcompound where the ridden entity is saved + * @param worldserver that needs to spawn the entity + * @param playerIn that needs to ride the entity + */ + public void reattachEntityToPlayer(NBTTagCompound nbttagcompound, World worldserver, Entity playerIn) { + if (nbttagcompound != null && nbttagcompound.hasKey("RootVehicle", 10)) { + NBTTagCompound nbttagcompound1 = nbttagcompound.getCompoundTag("RootVehicle"); + Entity entity1 = AnvilChunkLoader.readWorldEntity(nbttagcompound1.getCompoundTag("Entity"), worldserver, true); + + if (entity1 == null) { + for (Entity entity : worldserver.loadedEntityList) { + if (entity.getUniqueID().equals(nbttagcompound1.getUniqueId("Attach"))) + entity1 = entity; + } + } + + if (entity1 != null) { + UUID uuid = nbttagcompound1.getUniqueId("Attach"); + + if (entity1.getUniqueID().equals(uuid)) { + playerIn.startRiding(entity1, true); + } else { + for (Entity entity : entity1.getRecursivePassengers()) { + if (entity.getUniqueID().equals(uuid)) { + playerIn.startRiding(entity, true); + break; + } + } + } + + if (!playerIn.isRiding()) { + LOGGER.warn("Couldn't reattach entity to player"); + worldserver.removeEntityDangerously(entity1); + + for (Entity entity2 : entity1.getRecursivePassengers()) { + worldserver.removeEntityDangerously(entity2); + } + } + } + } else { + if (playerIn.isRiding()) { + playerIn.dismountRidingEntity(); + } + } + } + + /** + * Loads all worlds and players from the disk. Also sends the playerdata to the client in {@linkplain SavestateHandlerClient#onClientPacket(PacketID, ByteBuffer, String)} + * + * Side: Server + */ + public void loadAndSendMotionToPlayer() { + + PlayerList list = server.getPlayerList(); + List players = list.getPlayers(); + + for (EntityPlayerMP player : players) { + + int dimensionFrom = player.dimension; + + player.setWorld(server.getWorld(dimensionFrom)); + + NBTTagCompound nbttagcompound = server.getPlayerList().readPlayerDataFromFile(player); + + int dimensionTo = 0; + if (nbttagcompound.hasKey("Dimension")) { + dimensionTo = nbttagcompound.getInteger("Dimension"); + } + + if (dimensionTo != dimensionFrom) { + changeDimensionDangerously(player, dimensionTo); + } else { + player.getServerWorld().unloadedEntityList.remove(player); + } + + player.clearActivePotions(); + + player.readFromNBT(nbttagcompound); + player.setWorld(this.server.getWorld(player.dimension)); + player.interactionManager.setWorld((WorldServer) player.world); + + LOGGER.debug(LoggerMarkers.Savestate, "Sending motion to {}", player.getName()); + + try { + TASmod.server.sendTo(player, new TASmodBufferBuilder(TASmodPackets.SAVESTATE_PLAYER).writeNBTTagCompound(nbttagcompound)); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + /** + *

Changes the dimension of the player without loading chunks. + * + * @param player The player that should change the dimension + * @param dimensionTo The dimension where the player should be put + */ + public void changeDimensionDangerously(EntityPlayerMP player, int dimensionTo) { + int dimensionFrom = player.dimension; + WorldServer worldServerFrom = this.server.getWorld(dimensionFrom); +// WorldServer worldServerTo = this.server.getWorld(dimensionTo); + + //@formatter:off + player.connection + .sendPacket( + new SPacketRespawn( + dimensionTo, + player.world.getDifficulty(), + player.world.getWorldInfo().getTerrainType(), + player.interactionManager.getGameType() + ) + ); + //@formatter:on + worldServerFrom.removeEntityDangerously(player); + player.isDead = false; +// worldServerTo.spawnEntity(player); +// worldServerTo.updateEntityWithOptionalForce(player, false); +// player.setWorld(worldServerTo); +// player.interactionManager.setWorld(worldServerTo); + } + + public void clearScoreboard() { + try { + TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.SAVESTATE_CLEAR_SCOREBOARD)); + } catch (Exception e) { + LOGGER.catching(e); + } + } + + @Override + public PacketID[] getAcceptedPacketIDs() { + return new PacketID[] { + //@formatter:off + SAVESTATE_PLAYER + //@formatter:on + }; + } + + @Override + public void onServerPacket(PacketID id, ByteBuffer buf, String username) throws PacketNotImplementedException, WrongSideException, Exception { + TASmodPackets packet = (TASmodPackets) id; + + switch (packet) { + case SAVESTATE_PLAYER: + throw new WrongSideException(packet, Side.SERVER); + default: + break; + } + } + + @Environment(EnvType.CLIENT) + @Override + public void onClientPacket(PacketID id, ByteBuffer buf, String username) throws PacketNotImplementedException, WrongSideException, Exception { + TASmodPackets packet = (TASmodPackets) id; + + switch (packet) { + case SAVESTATE_PLAYER: + NBTTagCompound compound; + try { + compound = TASmodBufferBuilder.readNBTTagCompound(buf); + } catch (IOException e) { + e.printStackTrace(); + return; + } + /* + * Fair warning: Do NOT read the buffer inside an addScheduledTask. Read it + * before that. The buffer will have the wrong limit, when the task is executed. + * This is probably due to the buffers being reused. + */ + Minecraft.getMinecraft().addScheduledTask(() -> { + SavestateHandlerClient.loadPlayer(compound); + }); + break; + + default: + break; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/handlers/SavestateWorldHandler.java b/src/main/java/com/minecrafttas/tasmod/savestates/handlers/SavestateWorldHandler.java new file mode 100644 index 00000000..c8de811a --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/savestates/handlers/SavestateWorldHandler.java @@ -0,0 +1,249 @@ +package com.minecrafttas.tasmod.savestates.handlers; + +import static com.minecrafttas.tasmod.TASmod.LOGGER; + +import java.util.List; + +import com.minecrafttas.tasmod.mixin.savestates.AccessorPlayerChunkMap; +import com.minecrafttas.tasmod.mixin.savestates.MixinChunkProviderServer; +import com.minecrafttas.tasmod.savestates.SavestateHandlerClient; +import com.minecrafttas.tasmod.util.Ducks.ChunkProviderDuck; +import com.minecrafttas.tasmod.util.Ducks.WorldServerDuck; +import com.minecrafttas.tasmod.util.LoggerMarkers; + +import net.minecraft.client.Minecraft; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.management.PlayerChunkMap; +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.ServerWorldEventHandler; +import net.minecraft.world.WorldServer; +import net.minecraft.world.WorldServerMulti; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.gen.ChunkProviderServer; +import net.minecraft.world.storage.ISaveHandler; +import net.minecraft.world.storage.SaveHandler; +import net.minecraft.world.storage.WorldInfo; + +/** + * Contains static chunk actions, which can be triggered individually for testing + */ +public class SavestateWorldHandler { + + private final MinecraftServer server; + + public SavestateWorldHandler(MinecraftServer server) { + this.server = server; + } + + /** + * Disables automatic saving across all worlds + */ + public void disableLevelSaving() { + for (WorldServer world : server.worlds) { + world.disableLevelSaving = true; + } + } + + /** + * Enables automatic saving across all worlds + */ + public void enableLevelSaving() { + for (WorldServer world : server.worlds) { + world.disableLevelSaving = false; + } + } + + /** + * Just like {@link SavestateHandlerClient#addPlayerToClientChunk(EntityPlayer)}, adds the player to the chunk on the server. + * This prevents the player from being able to place block inside of him + * + * Side: Server + */ + public void addPlayerToServerChunk(EntityPlayerMP player) { + LOGGER.trace(LoggerMarkers.Savestate, "Add player {} to server chunk", player.getName()); + int i = MathHelper.floor(player.posX / 16.0D); + int j = MathHelper.floor(player.posZ / 16.0D); + WorldServer world = player.getServerWorld(); + Chunk chunk = world.getChunkFromChunkCoords(i, j); + for (int k = 0; k < chunk.getEntityLists().length; k++) { + if (chunk.getEntityLists()[k].contains(player)) { + return; + } + } + chunk.addEntity(player); + } + + /** + *

The session lock is Minecraft's failsafe system when it comes to saving. It prevents writing to the world folder from 2 different locations
+ * This works by storing the {@link SaveHandler#initializationTime initializationTime} of the server to a session.lock file ({@link SaveHandler#setSessionLock()}), when the server started. + *

+ * When the server writes to the world folder, the current {@link SaveHandler#initializationTime initializationTime} and the session.lock time are compared ({@link SaveHandler#checkSessionLock()}).
+ * If the times match, the savehandler is allowed to continue writing to the world folder., + *

+ * Since we never close the server during a loadstate and a different session.lock from an older initialization is being copied into the folder,
+ * the 2 values will always mismatch after a loadstate.
+ * Thus we need to update the session.lock file once the loadstating is completed.
+ *
+ * Side: Server + */ + public void updateSessionLock() { + LOGGER.trace(LoggerMarkers.Savestate, "Update the session lock"); + WorldServer[] worlds = server.worlds; + for (WorldServer world : worlds) { + ((SaveHandler) world.getSaveHandler()).setSessionLock(); + } + } + + /** + * Tells the save handler to save all changes to disk and remove all references to the region files, making them editable on disc
+ *
+ * Side: Server + */ + public void flushSaveHandler() { + LOGGER.trace(LoggerMarkers.Savestate, "Flush the save handler"); + //Vanilla + WorldServer[] worlds = server.worlds; + for (WorldServer world : worlds) { + world.getSaveHandler().flush(); + } + } + + /** + * The player chunk map keeps track of which chunks need to be sent to the client.
+ * This adds the player to the chunk map so the server knows it can send the information to the client
+ *
+ * Side: Server + * @see #disconnectPlayersFromChunkMap(MinecraftServer) + */ + public void addPlayersToChunkMap() { + List players = server.getPlayerList().getPlayers(); + WorldServer[] worlds = server.worlds; + for (EntityPlayerMP player : players) { + LOGGER.trace(LoggerMarkers.Savestate, "Add player {} to the chunk map", player.getName()); + switch (player.dimension) { + case -1: + addPlayerToChunkMap(worlds[1], player); + break; + case 0: + addPlayerToChunkMap(worlds[0], player); + break; + case 1: + addPlayerToChunkMap(worlds[2], player); + break; + default: + if (worlds.length > player.dimension) + addPlayerToChunkMap(worlds[player.dimension + 1], player); + break; + } + } + } + + /** + * Adds a single player to a chunkMap + * @param world + * @param player + */ + private void addPlayerToChunkMap(WorldServer world, EntityPlayerMP player) { + int playerChunkPosX = (int) player.posX >> 4; + int playerChunkPosY = (int) player.posZ >> 4; + PlayerChunkMap playerChunkMap = world.getPlayerChunkMap(); + + List players = ((AccessorPlayerChunkMap) playerChunkMap).getPlayers(); + + if (players.contains(player)) { + LOGGER.debug(LoggerMarkers.Savestate, "Not adding player {} to chunkmap, player already exists", player.getName()); + } else { + playerChunkMap.addPlayer(player); + } + world.getChunkProvider().provideChunk(playerChunkPosX, playerChunkPosY); + + world.spawnEntity(player); + } + + /** + * The player chunk map keeps track of which chunks need to be sent to the client.
+ * Removing the player stops the server from sending chunks to the client.
+ *
+ * Side: Server + * @see #addPlayersToChunkMap(MinecraftServer) + */ + public void disconnectPlayersFromChunkMap() { + List players = server.getPlayerList().getPlayers(); + WorldServer[] worlds = server.worlds; + for (WorldServer world : worlds) { + for (EntityPlayerMP player : players) { + LOGGER.trace(LoggerMarkers.Savestate, "Disconnect player {} from the chunk map", player.getName()); + world.getPlayerChunkMap().removePlayer(player); + } + } + } + + /** + * Unloads all chunks on the server
+ *
+ * Side: Server + * @see MixinChunkProviderServer#unloadAllChunks() + */ + public void unloadAllServerChunks() { + LOGGER.trace(LoggerMarkers.Savestate, "Unloading all server chunks"); + WorldServer[] worlds = server.worlds; + + for (WorldServer world : worlds) { + ChunkProviderServer chunkProvider = world.getChunkProvider(); + + ((ChunkProviderDuck) chunkProvider).unloadAllChunks(); + } + } + + /** + * Tick and send chunks to the client + */ + public void sendChunksToClient() { + WorldServer[] worlds = server.worlds; + + for (WorldServer world : worlds) { + WorldServerDuck worldTick = (WorldServerDuck) world; + worldTick.sendChunksToClient(); + } + } + + public void loadAllWorlds(String string, String string2) { + server.convertMapIfNeeded(string); + server.worlds = new WorldServer[3]; + server.timeOfLastDimensionTick = new long[server.worlds.length][100]; + ISaveHandler iSaveHandler = server.getActiveAnvilConverter().getSaveLoader(string, true); + server.setResourcePackFromWorld(server.getFolderName(), iSaveHandler); + WorldInfo worldInfo = iSaveHandler.loadWorldInfo(); + if (worldInfo == null) { +// worldInfo = new WorldInfo(server.worldSettings, string2); + } else { + worldInfo.setWorldName(string2); + } + + for (int i = 0; i < server.worlds.length; i++) { + int j = 0; + if (i == 1) { + j = -1; + } + + if (i == 2) { + j = 1; + } + + if (i == 0) { + server.worlds[i] = (WorldServer) new WorldServer(server, iSaveHandler, worldInfo, j, server.profiler).init(); + } else { + server.worlds[i] = (WorldServer) new WorldServerMulti(server, iSaveHandler, j, server.worlds[0], server.profiler).init(); + } + + server.worlds[i].addEventListener(new ServerWorldEventHandler(server, server.worlds[i])); + } + + server.getPlayerList().setPlayerManager(server.worlds); + if (server.worlds[0].getWorldInfo().getDifficulty() == null) { + server.setDifficultyForAllWorlds(Minecraft.getMinecraft().gameSettings.difficulty); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/storage/AbstractExtendStorage.java b/src/main/java/com/minecrafttas/tasmod/savestates/storage/AbstractExtendStorage.java new file mode 100644 index 00000000..d67ebc2b --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/savestates/storage/AbstractExtendStorage.java @@ -0,0 +1,11 @@ +package com.minecrafttas.tasmod.savestates.storage; + +import org.apache.logging.log4j.Logger; + +import com.minecrafttas.tasmod.TASmod; +import com.minecrafttas.tasmod.events.EventSavestate.EventServerLoadstate; +import com.minecrafttas.tasmod.events.EventSavestate.EventServerSavestate; + +public abstract class AbstractExtendStorage implements EventServerSavestate, EventServerLoadstate { + protected Logger logger = TASmod.LOGGER; +} diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/storage/SavestateMotionStorage.java b/src/main/java/com/minecrafttas/tasmod/savestates/storage/SavestateMotionStorage.java new file mode 100644 index 00000000..c79ec326 --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/savestates/storage/SavestateMotionStorage.java @@ -0,0 +1,287 @@ +package com.minecrafttas.tasmod.savestates.storage; + +import static com.minecrafttas.tasmod.TASmod.LOGGER; +import static com.minecrafttas.tasmod.registries.TASmodPackets.SAVESTATE_REQUEST_MOTION; +import static com.minecrafttas.tasmod.registries.TASmodPackets.SAVESTATE_SET_MOTION; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +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.registries.TASmodPackets; +import com.minecrafttas.tasmod.savestates.SavestateHandlerServer; +import com.minecrafttas.tasmod.savestates.exceptions.LoadstateException; +import com.minecrafttas.tasmod.savestates.exceptions.SavestateException; +import com.minecrafttas.tasmod.savestates.gui.GuiSavestateSavingScreen; +import com.minecrafttas.tasmod.util.LoggerMarkers; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.EntityPlayerSP; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.management.PlayerList; + +public class SavestateMotionStorage extends AbstractExtendStorage implements ClientPacketHandler, ServerPacketHandler { + + private static final Path fileName = Paths.get("clientMotion.json"); + private final Gson json; + + private final Map> futures; + + public SavestateMotionStorage() { + json = new GsonBuilder().setPrettyPrinting().create(); + futures = new HashMap<>(); + } + + @Override + public void onServerSavestate(MinecraftServer server, int index, Path target, Path current) { + LOGGER.trace(LoggerMarkers.Savestate, "Request motion from client"); + + this.futures.clear(); + + List playerList = server.getPlayerList().getPlayers(); + playerList.forEach(player -> { + futures.put(player, new CompletableFuture<>()); + }); + + try { + // request client motion + TASmod.server.sendToAll(new TASmodBufferBuilder(SAVESTATE_REQUEST_MOTION)); + } catch (Exception e) { + e.printStackTrace(); + } + + JsonObject playerJsonObject = new JsonObject(); + + futures.forEach((player, future) -> { + try { + MotionData data = future.get(5L, TimeUnit.SECONDS); + + String uuid = player.getUniqueID().toString(); + if (player.getName().equals(server.getServerOwner())) { + uuid = "singleplayer"; + } + playerJsonObject.add(uuid, json.toJsonTree(data)); + + } catch (TimeoutException e) { + throw new SavestateException(e, "Writing client motion for %s timed out!", player.getName()); + } catch (ExecutionException | InterruptedException e) { + throw new SavestateException(e, "Writing client motion for %s", player.getName()); + } + }); + + saveJson(current, playerJsonObject); + } + + private void saveJson(Path current, JsonObject data) { + Path saveFile = current.resolve(SavestateHandlerServer.storageDir).resolve(fileName); + + String out = json.toJson(data); + + try { + Files.write(saveFile, out.getBytes()); + } catch (IOException e) { + throw new SavestateException(e, "Could not write to the file system"); + } + } + + @Override + public void onServerLoadstate(MinecraftServer server, int index, Path target, Path current) { + JsonObject playerJsonObject = loadMotionData(target); + PlayerList list = server.getPlayerList(); + + for (Entry motionDataJsonElement : playerJsonObject.entrySet()) { + String playerUUID = motionDataJsonElement.getKey(); + MotionData motionData = json.fromJson(motionDataJsonElement.getValue(), MotionData.class); + + EntityPlayerMP player; + if (playerUUID.equals("singleplayer")) { + String ownerName = server.getServerOwner(); + if (ownerName == null) { + continue; + } + player = list.getPlayerByUsername(ownerName); + } else { + player = list.getPlayerByUUID(UUID.fromString(playerUUID)); + } + + if (player == null) { + continue; + } + + try { + TASmod.server.sendTo(player, new TASmodBufferBuilder(SAVESTATE_SET_MOTION).writeMotionData(motionData)); + } catch (Exception e) { + logger.catching(e); + } + } + } + + private JsonObject loadMotionData(Path target) { + Path saveFile = target.resolve(SavestateHandlerServer.storageDir).resolve(fileName); + String in; + try { + in = new String(Files.readAllBytes(saveFile)); + } catch (IOException e) { + throw new LoadstateException(e, "Could not read from the file system"); + } + return json.fromJson(in, JsonObject.class); + } + + @Override + public PacketID[] getAcceptedPacketIDs() { + return new PacketID[] { SAVESTATE_REQUEST_MOTION, SAVESTATE_SET_MOTION }; + } + + @Environment(EnvType.CLIENT) + @Override + public void onClientPacket(PacketID id, ByteBuffer buf, String username) throws PacketNotImplementedException, WrongSideException, Exception { + TASmodPackets packet = (TASmodPackets) id; + Minecraft mc = Minecraft.getMinecraft(); + EntityPlayerSP player = mc.player; + + switch (packet) { + case SAVESTATE_REQUEST_MOTION: + + if (player != null) { + if (!(mc.currentScreen instanceof GuiSavestateSavingScreen)) { + mc.displayGuiScreen(new GuiSavestateSavingScreen()); + } + //@formatter:off + MotionData motionData = new MotionData( + player.motionX, + player.motionY, + player.motionZ, + player.moveForward, + player.moveVertical, + player.moveStrafing, + player.isSprinting(), + player.jumpMovementFactor + ); + //@formatter:on + TASmodClient.client.send(new TASmodBufferBuilder(TASmodPackets.SAVESTATE_REQUEST_MOTION).writeMotionData(motionData)); + } + break; + case SAVESTATE_SET_MOTION: + LOGGER.trace(LoggerMarkers.Savestate, "Loading client motion"); + + MotionData data = TASmodBufferBuilder.readMotionData(buf); + player.motionX = data.motionX; + player.motionY = data.motionY; + player.motionZ = data.motionZ; + + player.moveForward = data.deltaX; + player.moveVertical = data.deltaY; + player.moveStrafing = data.deltaZ; + + player.setSprinting(data.sprinting); + player.jumpMovementFactor = data.jumpMovementFactor; + break; + default: + break; + } + } + + @Override + public void onServerPacket(PacketID id, ByteBuffer buf, String username) throws PacketNotImplementedException, WrongSideException, Exception { + TASmodPackets packet = (TASmodPackets) id; + EntityPlayerMP player = TASmod.getServerInstance().getPlayerList().getPlayerByUsername(username); + + switch (packet) { + case SAVESTATE_REQUEST_MOTION: + MotionData data = TASmodBufferBuilder.readMotionData(buf); + CompletableFuture future = this.futures.get(player); + future.complete(data); + break; + case SAVESTATE_SET_MOTION: + throw new WrongSideException(packet, Side.SERVER); + default: + break; + } + } + + public static class MotionData { + + private double motionX; + private double motionY; + private double motionZ; + private float deltaX; + private float deltaY; + private float deltaZ; + private boolean sprinting; + private float jumpMovementFactor; + + public MotionData(double x, double y, double z, float rx, float ry, float rz, boolean sprinting, float jumpMovementVector) { + motionX = x; + motionY = y; + motionZ = z; + deltaX = rx; + deltaY = ry; + deltaZ = rz; + this.sprinting = sprinting; + this.jumpMovementFactor = jumpMovementVector; + } + + public MotionData() { + this(0D, 0D, 0D, 0f, 0f, 0f, false, 0f); + } + + public double getClientX() { + return motionX; + } + + public double getClientY() { + return motionY; + } + + public double getClientZ() { + return motionZ; + } + + public float getClientrX() { + return deltaX; + } + + public float getClientrY() { + return deltaY; + } + + public float getClientrZ() { + return deltaZ; + } + + public boolean isSprinting() { + return sprinting; + } + + public float getJumpMovementVector() { + return jumpMovementFactor; + } + } +} diff --git a/src/main/java/com/minecrafttas/tasmod/savestates/storage/package-info.java b/src/main/java/com/minecrafttas/tasmod/savestates/storage/package-info.java new file mode 100644 index 00000000..ec5baf8f --- /dev/null +++ b/src/main/java/com/minecrafttas/tasmod/savestates/storage/package-info.java @@ -0,0 +1,8 @@ +package com.minecrafttas.tasmod.savestates.storage; +/** + * Package for extending the vanilla storage. + * + * Some things do not get stored by the vanilla storage, + * causing discrepancies for savestates + * + */ diff --git a/src/main/java/com/minecrafttas/tasmod/util/Ducks.java b/src/main/java/com/minecrafttas/tasmod/util/Ducks.java index 533affea..ea310379 100644 --- a/src/main/java/com/minecrafttas/tasmod/util/Ducks.java +++ b/src/main/java/com/minecrafttas/tasmod/util/Ducks.java @@ -19,19 +19,38 @@ * @author Pancake */ public class Ducks { - + /** * Quacks the chunk provider to unload all chunks */ public static interface ChunkProviderDuck { + /** + * Unloads chunks in the chunk providers + * @see com.minecrafttas.tasmod.mixin.savestates.MixinChunkProviderServer#unloadAllChunks() MixinChunkProviderServer#unloadAllChunks() + * @see com.minecrafttas.tasmod.mixin.savestates.MixinChunkProviderClient#unloadAllChunks() MixinChunkProviderClient#unloadAllChunks() + */ public void unloadAllChunks(); } - + + /** + * Quacks the worldserver to implement custom chunk ticking behavior + * + * @author Scribble + */ + public static interface WorldServerDuck { + + /** + * Sends the chunks to the client + * @see com.minecrafttas.tasmod.mixin.savestates.MixinWorldServer#sendChunksToClient() MixinWorldServer#sendChunksToClient() + */ + public void sendChunksToClient(); + } + /** * Quacks the gui screen to spit out mouse positions independent of the display size */ public static interface GuiScreenDuck { - + /** * Calculates the true value of the pointer coordinate, by removing the scaling for custom screen sizes applied to it: *

@@ -43,7 +62,7 @@ public static interface GuiScreenDuck {
 		 * @see #rescaleX(int)
 		 */
 		public int unscaleX(int x);
-		
+
 		/**
 		 * Calculates the true value of the pointer coordinate, by removing the scaling for custom screen sizes applied to it:
 		 * 
@@ -55,7 +74,7 @@ public static interface GuiScreenDuck {
 		 * @see #rescaleY(int)
 		 */
 		public int unscaleY(int y);
-		
+
 		/**
 		 * Reapplies the math for custom gui scales to the pointer coordinate:
 		 * 
@@ -65,7 +84,7 @@ public static interface GuiScreenDuck {
 		 * @return The scaled pointer coordinate
 		 */
 		public int rescaleX(int x);
-		
+
 		/**
 		 * Reapplies the math for custom gui scales to the pointer coordinate:
 		 * 
@@ -81,12 +100,33 @@ public static interface GuiScreenDuck {
 	 * Quacks the subtick
 	 */
 	public static interface SubtickDuck {
+
 		/**
 		 * Custom updating method for EntityRenderer, updating the player rotation
 		 * @param partialTicks The partial ticks from the vanilla Minecraft timer
 		 */
-		void runUpdate(float partialTicks);
+		public void runUpdate(float partialTicks);
+	}
+
+	/**
+	 * Quacks the scoreboard
+	 */
+	public static interface ScoreboardDuck {
+
+		/**
+		 * Clears the client scoreboard
+		 */
+		public void clearScoreboard();
 	}
-	
-}
 
+	/**
+	 * Quacks the world client
+	 */
+	public static interface WorldClientDuck {
+
+		/**
+		 * Clear entitylist on the client
+		 */
+		public void clearEntityList();
+	}
+}
diff --git a/src/main/java/com/minecrafttas/tasmod/util/LoggerMarkers.java b/src/main/java/com/minecrafttas/tasmod/util/LoggerMarkers.java
index 61fcfad0..1d2951ed 100644
--- a/src/main/java/com/minecrafttas/tasmod/util/LoggerMarkers.java
+++ b/src/main/java/com/minecrafttas/tasmod/util/LoggerMarkers.java
@@ -3,6 +3,13 @@
 import org.apache.logging.log4j.Marker;
 import org.apache.logging.log4j.MarkerManager;
 
+import com.minecrafttas.tasmod.events.EventClient.EventDrawHotbarAlways;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.gui.ScaledResolution;
+import net.minecraft.client.renderer.GlStateManager;
+
 /**
  * 

A list of Log4J markers which can be added to logging statements. * @@ -24,7 +31,7 @@ * @author Scribble * */ -public class LoggerMarkers { +public class LoggerMarkers implements EventDrawHotbarAlways { public static final Marker Event = MarkerManager.getMarker("Event"); @@ -39,4 +46,132 @@ public class LoggerMarkers { public static final Marker Keyboard = MarkerManager.getMarker("Keyboard"); public static final Marker Mouse = MarkerManager.getMarker("Mouse"); + + @Override + public void onDrawHotbarAlways() { + ScaledResolution scaledresolution = new ScaledResolution(Minecraft.getMinecraft()); + float xpos = scaledresolution.getScaledWidth() / 2 - 2; + float ypos = scaledresolution.getScaledHeight() - 50f; + + float scale = 1.26f; + int rotate = 15; + GlStateManager.translate(xpos, ypos, 0); + GlStateManager.scale(scale, scale, 1); + GlStateManager.rotate(rotate, 0, 0, 1); + int oW = 0xCCAFA5; + int o = 0xC24218; + + int w = 0xFFFFFF; + int c = 0x546980; + + int y = 0; + drawMarker(3, y, o); + drawMarker(4, y, o); + drawMarker(5, y, o); + + y = 1; + drawMarker(2, y, w); + drawMarker(3, y, oW); + drawMarker(4, y, oW); + drawMarker(5, y, oW); + drawMarker(6, y, w); + + y = 2; + drawMarker(2, y, w); + drawMarker(3, y, o); + drawMarker(4, y, o); + drawMarker(5, y, o); + drawMarker(6, y, w); + + y = 3; + drawMarker(3, y, w); + drawMarker(5, y, w); + + y = 4; + drawMarker(3, y, w); + drawMarker(5, y, w); + + y = 5; + drawMarker(2, y, w); + drawMarker(6, y, w); + + y = 6; + drawMarker(1, y, w); + drawMarker(2, y, c); + drawMarker(3, y, w); + drawMarker(4, y, c); + drawMarker(5, y, c); + drawMarker(6, y, c); + drawMarker(7, y, w); + + y = 7; + drawMarker(0, y, w); + drawMarker(1, y, c); + drawMarker(2, y, w); + drawMarker(3, y, c); + drawMarker(4, y, c); + drawMarker(5, y, c); + drawMarker(6, y, c); + drawMarker(7, y, c); + drawMarker(8, y, w); + + y = 8; + drawMarker(0, y, w); + drawMarker(1, y, c); + drawMarker(2, y, w); + drawMarker(3, y, c); + drawMarker(4, y, c); + drawMarker(5, y, c); + drawMarker(6, y, c); + drawMarker(7, y, c); + drawMarker(8, y, w); + + y = 9; + drawMarker(0, y, w); + drawMarker(1, y, c); + drawMarker(2, y, c); + drawMarker(3, y, c); + drawMarker(4, y, c); + drawMarker(5, y, c); + drawMarker(6, y, w); + drawMarker(7, y, c); + drawMarker(8, y, w); + + y = 10; + drawMarker(0, y, w); + drawMarker(1, y, c); + drawMarker(2, y, c); + drawMarker(3, y, c); + drawMarker(4, y, c); + drawMarker(5, y, c); + drawMarker(6, y, w); + drawMarker(7, y, c); + drawMarker(8, y, w); + + y = 11; + drawMarker(0, y, w); + drawMarker(1, y, w); + drawMarker(2, y, c); + drawMarker(3, y, c); + drawMarker(4, y, c); + drawMarker(5, y, w); + drawMarker(6, y, c); + drawMarker(7, y, w); + drawMarker(8, y, w); + + y = 12; + drawMarker(2, y, w); + drawMarker(3, y, w); + drawMarker(4, y, w); + drawMarker(5, y, w); + drawMarker(6, y, w); + GlStateManager.rotate(-rotate, 0, 0, 1); + GlStateManager.scale(1 / scale, 1 / scale, 1); + GlStateManager.translate(-xpos, -ypos, 0); + } + + private void drawMarker(int posX, int posY, int textColor) { + int alpha = 0x80000000; + Gui.drawRect(posX, posY, posX + 1, posY + 1, textColor + alpha); + } } diff --git a/src/main/java/com/minecrafttas/tasmod/util/Monitor.java b/src/main/java/com/minecrafttas/tasmod/util/Monitor.java index d53e5ac1..24f60e06 100644 --- a/src/main/java/com/minecrafttas/tasmod/util/Monitor.java +++ b/src/main/java/com/minecrafttas/tasmod/util/Monitor.java @@ -63,5 +63,4 @@ public static Object accessField(Object objectToAccess, String fieldname) { } return out; } - } diff --git a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java index 7ff5ccad..63272b5e 100644 --- a/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java +++ b/src/main/java/com/minecrafttas/tasmod/virtual/VirtualInput.java @@ -16,6 +16,7 @@ import com.minecrafttas.tasmod.mixin.playbackhooks.MixinEntityRenderer; import com.minecrafttas.tasmod.mixin.playbackhooks.MixinMinecraft; import com.minecrafttas.tasmod.util.Ducks; +import com.minecrafttas.tasmod.util.Ducks.SubtickDuck; import com.minecrafttas.tasmod.util.LoggerMarkers; import com.minecrafttas.tasmod.util.PointerNormalizer; import com.minecrafttas.tasmod.virtual.event.VirtualKeyboardEvent; @@ -144,8 +145,25 @@ public void preloadInput(VirtualKeyboard keyboardToPreload, VirtualMouse mouseTo 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(); + SubtickDuck entityRenderer = (SubtickDuck) Minecraft.getMinecraft().entityRenderer; + entityRenderer.runUpdate(0); + } + + public List getCurrentMousePresses() { + return MOUSE.currentMouse.getCurrentPresses(); + } + + public List getNextMousePresses() { + return MOUSE.nextMouse.getCurrentPresses(); + } + + public List getCurrentKeyboardPresses() { + return KEYBOARD.currentKeyboard.getCurrentPresses(); + } + + public List getNextKeyboardPresses() { + return KEYBOARD.nextKeyboard.getCurrentPresses(); } /** @@ -599,10 +617,10 @@ public void updateNextCameraAngle(float pitchDelta, float yawDelta) { *
* 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 updateSubtick Whether to add the previous camera angle to the {@link Subtickable#subtickList} + * @see MixinEntityRenderer#runUpdate(float) */ public void updateNextCameraAngle(float pitchDelta, float yawDelta, boolean updateSubtick) { // LOGGER.debug("Pitch: {}, Yaw: {}", pitch, yaw); diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index cf44a0fe..dec8d114 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -33,5 +33,11 @@ "fabricloader": ">=0.14.19", "minecraft": "1.12.2", "java": ">=8" + }, + "custom": { + "mc-publish": { + "modrinth": "g0TxtOPk", + "curseforge": "434702" + } } } \ No newline at end of file diff --git a/src/main/resources/tasmod.accesswidener b/src/main/resources/tasmod.accesswidener index 99c6bdf0..0029bc11 100644 --- a/src/main/resources/tasmod.accesswidener +++ b/src/main/resources/tasmod.accesswidener @@ -9,8 +9,6 @@ accessible field net/minecraft/util/Timer tickLength F accessible field net/minecraft/world/World unloadedEntityList Ljava/util/List; accessible field net/minecraft/world/World worldInfo Lnet/minecraft/world/storage/WorldInfo; -accessible field net/minecraft/world/WorldServer pendingTickListEntriesHashSet Ljava/util/Set; - accessible method net/minecraft/world/storage/SaveHandler setSessionLock ()V -accessible field net/minecraft/client/settings/KeyBinding CATEGORY_ORDER Ljava/util/Map; +accessible field net/minecraft/client/settings/KeyBinding CATEGORY_ORDER Ljava/util/Map; \ No newline at end of file diff --git a/src/main/resources/tasmod.mixin.json b/src/main/resources/tasmod.mixin.json index 04a4d468..25231ee5 100644 --- a/src/main/resources/tasmod.mixin.json +++ b/src/main/resources/tasmod.mixin.json @@ -1,47 +1,51 @@ { - "required": true, - "minVersion": "0.8.5", - "package": "com.minecrafttas.tasmod.mixin", - "compatibilityLevel": "JAVA_8", - "mixins": [ - - //General - "MixinMinecraftServer", - "MixinEntityPlayer", - - //Savestates - "savestates.MixinChunkProviderServer", - "savestates.MixinEntityPlayerMP", - "savestates.MixinNetHandlerPlayServer", - "savestates.AccessorChunkLoader", - "savestates.AccessorAnvilChunkLoader", - - //Events - - //Fixing forge and vanilla stuff - "fixes.MixinDragonFightManager" - - ], - "client": [ - //General - "MixinMinecraft", - "MixinTimer", - "MixinInGameHud", - - //Savestates + "required": true, + "minVersion": "0.8.5", + "package": "com.minecrafttas.tasmod.mixin", + "compatibilityLevel": "JAVA_8", + "mixins": [ + // General + "MixinMinecraftServer", + "MixinEntityPlayer", + + // Savestates + "savestates.AccessorAnvilChunkLoader", + "savestates.AccessorChunkLoader", + "savestates.AccessorEntityLivingBase", + "savestates.AccessorPlayerChunkMap", + "savestates.MixinChunkProviderServer", + "savestates.MixinNetHandlerPlayServer", + "savestates.MixinScoreboard", + "savestates.MixinWorldServer", + + // Events + "events.MixinEntityPlayerMP", + + // Fixing forge and vanilla stuff + "fixes.MixinDragonFightManager" + + ], + "client": [ + // General + "MixinMinecraft", + "MixinTimer", + + // Savestates "savestates.MixinChunkProviderClient", + "savestates.MixinWorldClient", - //Interpolation + // Interpolation "MixinFrustum", - //Keybinding + // Keybinding "MixinTextfield", - //Join and leave game event on the client + // Events "events.MixinGuiMainMenu", "events.MixinGuiIngame", + "events.MixinEntityRenderer", - //Playbackhooks + // Playbackhooks "playbackhooks.MixinMinecraft", "playbackhooks.MixinEntityRenderer", "playbackhooks.MixinGuiScreen", @@ -55,12 +59,12 @@ "playbackhooks.MixinGuiStats", "playbackhooks.MixinKeyBinding", - //Shields + // Shields "shields.MixinRenderItem", "shields.MixinTileEntityItemStackRenderer", - //Fixes - "fixes.MixinMinecraftFullscreen", - "fixes.MixinNetworkManager" + // Fixes + "fixes.MixinMinecraftFullscreen", + "fixes.MixinNetworkManager" ] } \ No newline at end of file diff --git a/src/test/java/mctcommon/TestConfiguration.java b/src/test/java/mctcommon/TestConfiguration.java index 70177b22..1aac0e79 100644 --- a/src/test/java/mctcommon/TestConfiguration.java +++ b/src/test/java/mctcommon/TestConfiguration.java @@ -4,29 +4,32 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; -import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; -import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import com.minecrafttas.mctcommon.Configuration; -import com.minecrafttas.mctcommon.Configuration.ConfigOptions; +import com.minecrafttas.mctcommon.ConfigurationRegistry; +import com.minecrafttas.mctcommon.ConfigurationRegistry.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; @@ -36,7 +39,7 @@ public String getDefaultValue() { public String getConfigKey() { return configKey; } - + @Override public String getExtensionName() { return "TestConfig"; @@ -44,19 +47,21 @@ public String getExtensionName() { } private Configuration config; - - private static final File configPath = new File("./config.xml"); - + + ConfigurationRegistry registry = new ConfigurationRegistry(); + + private static final Path configPath = Paths.get("src/test/resources/config.xml"); + @BeforeEach void beforeEach() { - config = new Configuration("Test config", configPath); - config.register(TestConfig.values()); - config.load(); + registry.register(TestConfig.values()); + config = new Configuration("Test config", configPath, registry); + config.loadFromXML(); } - @AfterAll - static void tearDownAfterClass() throws Exception { - configPath.delete(); + @AfterEach + void tearDownAfterClass() throws Exception { + Files.delete(configPath); } /** @@ -66,31 +71,28 @@ static void tearDownAfterClass() throws Exception { void testIfInitialized() { assertNotNull(config); } - + /** * Test if the default option is correctly set */ @Test - void testDefault() { - configPath.delete(); - config = new Configuration("Test config", configPath); - config.register(TestConfig.values()); - config.load(); + void testDefault() throws Exception { + Files.delete(configPath); + config = new Configuration("Test config", configPath, registry); + config.loadFromXML(); assertEquals("", config.get(TestConfig.FileToOpen)); } - + /** * Setting a value and recreating the config should result in the value still being set */ @Test void testSavingAndLoading() { config.set(TestConfig.FileToOpen, "Test"); - config = new Configuration("Test config", configPath); - config.register(TestConfig.values()); - config.load(); + config.loadFromXML(); assertEquals("Test", config.get(TestConfig.FileToOpen)); } - + /** * Test if integers can be set */ @@ -108,7 +110,7 @@ void testBooleans() { config.set(TestConfig.FileToOpen, true); assertEquals(true, config.getBoolean(TestConfig.FileToOpen)); } - + /** * Test if deleting and unsetting a config value works */ @@ -117,7 +119,7 @@ void testDeleteAndContains() { config.delete(TestConfig.FileToOpen); assertFalse(config.has(TestConfig.FileToOpen)); } - + /** * Test if resetting to default works */ diff --git a/src/test/java/tasmod/playback/tasfile/PlaybackSerialiserTest.java b/src/test/java/tasmod/playback/tasfile/PlaybackSerialiserTest.java index 91834091..a640e3fe 100644 --- a/src/test/java/tasmod/playback/tasfile/PlaybackSerialiserTest.java +++ b/src/test/java/tasmod/playback/tasfile/PlaybackSerialiserTest.java @@ -3,6 +3,7 @@ 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.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; @@ -36,26 +37,25 @@ 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"; @@ -63,12 +63,12 @@ public String getExtensionName() { @Override public void onCreate() { - + } @Override public PlaybackMetadata onStore() { - PlaybackMetadata metadata =new PlaybackMetadata(this); + PlaybackMetadata metadata = new PlaybackMetadata(this); metadata.setValue("TestKey", testValue); return metadata; } @@ -80,80 +80,89 @@ public void onLoad(PlaybackMetadata metadata) { @Override public void onClear() { + testValue = ""; + actual = "e"; } - + } - + 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"}; + return new String[] { "testKey", "endlineKey" }; + } + + @Override + public void onClear() { + inline.clear(); + endline.clear(); } } - + 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) { + if (file != null) { file.delete(); } + + testMetadata.onClear(); + testFileCommand.onClear(); } - + @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); - + keyboard1.updateFromEvent(VirtualKey.LCONTROL, true, (char) 0); + // Mouse VirtualMouse mouse1 = new VirtualMouse(); mouse1.updateFromEvent(VirtualKey.MOUSEMOVED, false, 15, 0, 0); @@ -163,17 +172,17 @@ void testSerialiser() { 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); - + keyboard2.updateFromEvent(VirtualKey.W, false, (char) 0); + keyboard2.updateFromEvent(VirtualKey.LCONTROL, false, (char) 0); + // Mouse VirtualMouse mouse2 = new VirtualMouse(); mouse2.copyFrom(mouse1); @@ -184,15 +193,15 @@ void testSerialiser() { 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); @@ -201,7 +210,7 @@ void testSerialiser() { fail(e); } } - + @Test void testDeserialiser() throws PlaybackLoadException, IOException { List lines = new ArrayList<>(); @@ -219,112 +228,176 @@ void testDeserialiser() throws PlaybackLoadException, IOException { 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 testDeserialiserNoExtensions() 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/PlaybackSerialiserTest3.mctas"); + try { + FileUtils.writeLines(file, lines); + } catch (IOException e) { + e.printStackTrace(); + } + + BigArrayList actual = PlaybackSerialiser.loadFromFile(file, false); + + 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("e", testMetadata.actual); + + assertTrue(testFileCommand.inline.isEmpty()); + assertTrue(testFileCommand.endline.isEmpty()); + } + @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, ()->{ + + 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, ()->{ + 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)); } diff --git a/src/test/java/tasmod/playback/tasfile/SerialiserFlavorBaseTest.java b/src/test/java/tasmod/playback/tasfile/SerialiserFlavorBaseTest.java index 8864d879..c5e2c610 100644 --- a/src/test/java/tasmod/playback/tasfile/SerialiserFlavorBaseTest.java +++ b/src/test/java/tasmod/playback/tasfile/SerialiserFlavorBaseTest.java @@ -35,12 +35,12 @@ 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"; @@ -67,7 +67,7 @@ void testSerialiseHeaderStart() { */ @Test void testSerialiseMetadata() { - + class MetadataTest extends PlaybackMetadataExtension { public String testValue; @@ -100,7 +100,7 @@ public void onClear() { } } - + class MetadataTest2 extends PlaybackMetadataExtension { public String testValue; @@ -133,7 +133,7 @@ public void onClear() { } } - + MetadataTest testmetadata1 = new MetadataTest(); testmetadata1.testValue = "This is a test"; @@ -156,14 +156,14 @@ public void onClear() { assertIterableEquals(expected, actual); assertEquals(0, currentTick); - + TASmodAPIRegistry.PLAYBACK_METADATA.unregister(testmetadata1); TASmodAPIRegistry.PLAYBACK_METADATA.unregister(testmetadata2); } - + @Test void testSerialiseFileCommandNames() { - + class TestFileCommand extends PlaybackFileCommandExtension { @Override @@ -176,18 +176,18 @@ 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); } @@ -380,11 +380,11 @@ void testExtractHeaderFail() { */ @Test void testDeserialiseMetadata() { - - class GeneralMetadata extends PlaybackMetadataExtension{ + + class GeneralMetadata extends PlaybackMetadataExtension { PlaybackMetadata metadata = null; - + @Override public String getExtensionName() { return "General"; @@ -407,13 +407,13 @@ public void onLoad(PlaybackMetadata metadata) { @Override public void onClear() { } - + } - + class StartPositionMetadata extends PlaybackMetadataExtension { PlaybackMetadata metadata = null; - + @Override public String getExtensionName() { return "StartPosition"; @@ -436,15 +436,15 @@ public void onLoad(PlaybackMetadata 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"); @@ -481,8 +481,8 @@ public void onClear() { @Test void testDeserialiseFileCommandNames() { - - class Test1 extends PlaybackFileCommandExtension{ + + class Test1 extends PlaybackFileCommandExtension { @Override public String getExtensionName() { @@ -493,9 +493,9 @@ public String getExtensionName() { public String[] getFileCommandNames() { return null; } - + } - + class Test2 extends PlaybackFileCommandExtension { @Override @@ -507,49 +507,49 @@ public String getExtensionName() { 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, ()->{ + + 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 */ @@ -597,7 +597,7 @@ void testExtractTick() { 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();"); @@ -620,7 +620,7 @@ void testExtractTick() { assertIterableEquals(expected, actual); assertIterableEquals(expectedIndex, actualIndex); } - + @Test void testExtractExceptions() { // Create lines to be extracted from @@ -629,14 +629,14 @@ void testExtractExceptions() { 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, ()->{ + 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 @@ -646,14 +646,14 @@ void testExtractExceptions2() { 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, ()->{ + 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 @@ -664,15 +664,15 @@ void testExtractExceptions3() { 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 + + 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 */ @@ -697,10 +697,10 @@ void testDeserialiseContainer() { 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); - + cameraAngle.updateFromState(-202.74799F, 17.85F); + cameraAngle.updateFromState(-2.74799F, 11.85F); + cameraAngle.updateFromState(-22.799F, 45F); + expected.add(new TickContainer(keyboard, mouse, cameraAngle)); assertBigArrayList(expected, actual); @@ -818,7 +818,7 @@ void testDeserialiseKeyboard() { assertEquals(expected, actual); } - + @Test void testDeserialiseKeyboardWithKeyCodes() { List tick = new ArrayList<>(); @@ -858,30 +858,30 @@ void testDeserialiseMouse() { expected.updateFromEvent(VirtualKey.MC, true, 15, 25, 34); assertEquals(expected, actual); - - currentTick=29; + + currentTick = 29; List tick2 = new ArrayList<>(); tick2.add(";0,0,0"); tick2.add("LC;0,12,35"); tick2.add("LC,MC;15,25"); - - Throwable t = assertThrows(PlaybackLoadException.class, ()->{ + + Throwable t = assertThrows(PlaybackLoadException.class, () -> { deserialiseMouse(tick2); }); - - assertEquals("Line 1, Tick 29, Subtick 2: Mouse functions do not have the correct length", t.getMessage()); - - currentTick=30; + + assertEquals("Line 1, Tick 29, Subtick 2: Mouse can't be read. Probably a missing comma: LC,MC;15,25", t.getMessage()); + + currentTick = 30; List tick3 = new ArrayList<>(); tick3.add(";0,0,0"); tick3.add("LC;0,12,35,12"); tick3.add("LC,MC;15,25,15"); - - Throwable t1 = assertThrows(PlaybackLoadException.class, ()->{ + + Throwable t1 = assertThrows(PlaybackLoadException.class, () -> { deserialiseMouse(tick3); }); - - assertEquals("Line 1, Tick 30, Subtick 1: Mouse functions do not have the correct length", t1.getMessage()); + + assertEquals("Line 1, Tick 30, Subtick 1: Mouse can't be read. Probably a missing comma: LC;0,12,35,12", t1.getMessage()); } /** @@ -898,9 +898,9 @@ void testDeserialisingCameraAngle() { 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); + expected.updateFromEvent(-202.74799F, 19F); + expected.updateFromEvent(-2.799F + 202.74799F, 11.1241500F - 19F); + expected.updateFromEvent(-202.79F + 2.799F, 17.3F - 11.1241500F); assertEquals(expected, actual); } @@ -926,66 +926,66 @@ void testIsFloat() { 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, ()->{ + 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("Line 1, Tick 13, Subtick 1: The testParseInt could not be processed. This should be a number: 12.1", 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, ()->{ + 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("Line 1, Tick 15, Subtick 6: The testParseFloat could not be processed. This should be a decimal number: 12.123h", 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, ()->{ + 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, ()->{ + 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()); @@ -1068,6 +1068,5 @@ private ArrayList convertBigArrayListToArrayList(Big public SerialiserFlavorBase clone() { return new SerialiserFlavorBaseTest(); } - - + }