From 1a8d7e901a8b49113e727e3308a0576b6b90299b Mon Sep 17 00:00:00 2001 From: KingRainbow44 Date: Mon, 24 Apr 2023 01:44:58 -0400 Subject: [PATCH] Apply additional data from specific main quest lines --- .../java/emu/grasscutter/data/DataLoader.java | 306 +++++++++--------- .../emu/grasscutter/data/ResourceLoader.java | 22 +- .../data/binout/MainQuestData.java | 165 +++++----- .../grasscutter/data/excels/QuestData.java | 8 +- 4 files changed, 258 insertions(+), 243 deletions(-) diff --git a/src/main/java/emu/grasscutter/data/DataLoader.java b/src/main/java/emu/grasscutter/data/DataLoader.java index 98a7d3567..6f3d68e5d 100644 --- a/src/main/java/emu/grasscutter/data/DataLoader.java +++ b/src/main/java/emu/grasscutter/data/DataLoader.java @@ -1,153 +1,153 @@ -package emu.grasscutter.data; - -import emu.grasscutter.Grasscutter; -import emu.grasscutter.server.http.handlers.GachaHandler; -import emu.grasscutter.tools.Tools; -import emu.grasscutter.utils.FileUtils; -import emu.grasscutter.utils.JsonUtils; -import emu.grasscutter.utils.TsvUtils; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; -import java.util.Map; -import lombok.val; - -public class DataLoader { - - /** - * Load a data file by its name. If the file isn't found within the /data directory then it will - * fallback to the default within the jar resources - * - * @param resourcePath The path to the data file to be loaded. - * @return InputStream of the data file. - * @throws FileNotFoundException - * @see #load(String, boolean) - */ - public static InputStream load(String resourcePath) throws FileNotFoundException { - return load(resourcePath, true); - } - - /** - * Creates an input stream reader for a data file. If the file isn't found within the /data - * directory then it will fallback to the default within the jar resources - * - * @param resourcePath The path to the data file to be loaded. - * @return InputStreamReader of the data file. - * @throws IOException - * @throws FileNotFoundException - * @see #load(String, boolean) - */ - public static InputStreamReader loadReader(String resourcePath) - throws IOException, FileNotFoundException { - try { - InputStream is = load(resourcePath, true); - return new InputStreamReader(is); - } catch (FileNotFoundException exception) { - throw exception; - } - } - - /** - * Load a data file by its name. - * - * @param resourcePath The path to the data file to be loaded. - * @param useFallback If the file does not exist in the /data directory, should it use the default - * file in the jar? - * @return InputStream of the data file. - * @throws FileNotFoundException - */ - public static InputStream load(String resourcePath, boolean useFallback) - throws FileNotFoundException { - Path path = - useFallback ? FileUtils.getDataPath(resourcePath) : FileUtils.getDataUserPath(resourcePath); - if (Files.exists(path)) { - // Data is in the resource directory - try { - return Files.newInputStream(path); - } catch (IOException e) { - throw new FileNotFoundException( - e.getMessage()); // This is evil but so is changing the function signature at this point - } - } - return null; - } - - public static T loadClass(String resourcePath, Class classType) throws IOException { - try (InputStreamReader reader = loadReader(resourcePath)) { - return JsonUtils.loadToClass(reader, classType); - } - } - - public static List loadList(String resourcePath, Class classType) throws IOException { - try (InputStreamReader reader = loadReader(resourcePath)) { - return JsonUtils.loadToList(reader, classType); - } - } - - public static Map loadMap( - String resourcePath, Class keyType, Class valueType) throws IOException { - try (InputStreamReader reader = loadReader(resourcePath)) { - return JsonUtils.loadToMap(reader, keyType, valueType); - } - } - - public static List loadTableToList(String resourcePath, Class classType) - throws IOException { - val path = FileUtils.getDataPathTsjJsonTsv(resourcePath); - Grasscutter.getLogger().debug("Loading data table from: " + path); - return switch (FileUtils.getFileExtension(path)) { - case "json" -> JsonUtils.loadToList(path, classType); - case "tsj" -> TsvUtils.loadTsjToListSetField(path, classType); - case "tsv" -> TsvUtils.loadTsvToListSetField(path, classType); - default -> null; - }; - } - - public static void checkAllFiles() { - try { - List filenames = FileUtils.getPathsFromResource("/defaults/data/"); - - if (filenames == null) { - Grasscutter.getLogger().error("We were unable to locate your default data files."); - } // else for (Path file : filenames) { - // String relativePath = String.valueOf(file).split("defaults[\\\\\\/]data[\\\\\\/]")[1]; - - // checkAndCopyData(relativePath); - // } - } catch (Exception e) { - Grasscutter.getLogger().error("An error occurred while trying to check the data folder.", e); - } - - generateGachaMappings(); - } - - private static void checkAndCopyData(String name) { - // TODO: Revisit this if default dumping is ever reintroduced - Path filePath = FileUtils.getDataPath(name); - - if (!Files.exists(filePath)) { - var root = filePath.getParent(); - if (root.toFile().mkdirs()) - Grasscutter.getLogger().info("Created data folder '" + root + "'"); - - Grasscutter.getLogger().debug("Creating default '" + name + "' data"); - FileUtils.copyResource("/defaults/data/" + name, filePath.toString()); - } - } - - private static void generateGachaMappings() { - var path = GachaHandler.getGachaMappingsPath(); - if (!Files.exists(path)) { - try { - Grasscutter.getLogger().debug("Creating default '" + path + "' data"); - Tools.createGachaMappings(path); - } catch (Exception exception) { - Grasscutter.getLogger().warn("Failed to create gacha mappings. \n" + exception); - } - } - } -} +package emu.grasscutter.data; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.server.http.handlers.GachaHandler; +import emu.grasscutter.tools.Tools; +import emu.grasscutter.utils.FileUtils; +import emu.grasscutter.utils.JsonUtils; +import emu.grasscutter.utils.TsvUtils; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import lombok.val; + +public class DataLoader { + + /** + * Load a data file by its name. If the file isn't found within the /data directory then it will + * fallback to the default within the jar resources + * + * @param resourcePath The path to the data file to be loaded. + * @return InputStream of the data file. + * @throws FileNotFoundException + * @see #load(String, boolean) + */ + public static InputStream load(String resourcePath) throws FileNotFoundException { + return load(resourcePath, true); + } + + /** + * Creates an input stream reader for a data file. If the file isn't found within the /data + * directory then it will fallback to the default within the jar resources + * + * @param resourcePath The path to the data file to be loaded. + * @return InputStreamReader of the data file. + * @throws IOException + * @throws FileNotFoundException + * @see #load(String, boolean) + */ + public static InputStreamReader loadReader(String resourcePath) + throws IOException, FileNotFoundException { + try { + InputStream is = load(resourcePath, true); + return new InputStreamReader(is); + } catch (FileNotFoundException exception) { + throw exception; + } + } + + /** + * Load a data file by its name. + * + * @param resourcePath The path to the data file to be loaded. + * @param useFallback If the file does not exist in the /data directory, should it use the default + * file in the jar? + * @return InputStream of the data file. + * @throws FileNotFoundException + */ + public static InputStream load(String resourcePath, boolean useFallback) + throws FileNotFoundException { + Path path = + useFallback ? FileUtils.getDataPath(resourcePath) : FileUtils.getDataUserPath(resourcePath); + if (Files.exists(path)) { + // Data is in the resource directory + try { + return Files.newInputStream(path); + } catch (IOException e) { + throw new FileNotFoundException( + e.getMessage()); // This is evil but so is changing the function signature at this point + } + } + return null; + } + + public static T loadClass(String resourcePath, Class classType) throws IOException { + try (InputStreamReader reader = loadReader(resourcePath)) { + return JsonUtils.loadToClass(reader, classType); + } + } + + public static List loadList(String resourcePath, Class classType) throws IOException { + try (var reader = loadReader(resourcePath)) { + return JsonUtils.loadToList(reader, classType); + } + } + + public static Map loadMap( + String resourcePath, Class keyType, Class valueType) throws IOException { + try (InputStreamReader reader = loadReader(resourcePath)) { + return JsonUtils.loadToMap(reader, keyType, valueType); + } + } + + public static List loadTableToList(String resourcePath, Class classType) + throws IOException { + val path = FileUtils.getDataPathTsjJsonTsv(resourcePath); + Grasscutter.getLogger().debug("Loading data table from: " + path); + return switch (FileUtils.getFileExtension(path)) { + case "json" -> JsonUtils.loadToList(path, classType); + case "tsj" -> TsvUtils.loadTsjToListSetField(path, classType); + case "tsv" -> TsvUtils.loadTsvToListSetField(path, classType); + default -> null; + }; + } + + public static void checkAllFiles() { + try { + List filenames = FileUtils.getPathsFromResource("/defaults/data/"); + + if (filenames == null) { + Grasscutter.getLogger().error("We were unable to locate your default data files."); + } // else for (Path file : filenames) { + // String relativePath = String.valueOf(file).split("defaults[\\\\\\/]data[\\\\\\/]")[1]; + + // checkAndCopyData(relativePath); + // } + } catch (Exception e) { + Grasscutter.getLogger().error("An error occurred while trying to check the data folder.", e); + } + + generateGachaMappings(); + } + + private static void checkAndCopyData(String name) { + // TODO: Revisit this if default dumping is ever reintroduced + Path filePath = FileUtils.getDataPath(name); + + if (!Files.exists(filePath)) { + var root = filePath.getParent(); + if (root.toFile().mkdirs()) + Grasscutter.getLogger().info("Created data folder '" + root + "'"); + + Grasscutter.getLogger().debug("Creating default '" + name + "' data"); + FileUtils.copyResource("/defaults/data/" + name, filePath.toString()); + } + } + + private static void generateGachaMappings() { + var path = GachaHandler.getGachaMappingsPath(); + if (!Files.exists(path)) { + try { + Grasscutter.getLogger().debug("Creating default '" + path + "' data"); + Tools.createGachaMappings(path); + } catch (Exception exception) { + Grasscutter.getLogger().warn("Failed to create gacha mappings. \n" + exception); + } + } + } +} diff --git a/src/main/java/emu/grasscutter/data/ResourceLoader.java b/src/main/java/emu/grasscutter/data/ResourceLoader.java index 65fa4b714..480d6a046 100644 --- a/src/main/java/emu/grasscutter/data/ResourceLoader.java +++ b/src/main/java/emu/grasscutter/data/ResourceLoader.java @@ -489,17 +489,15 @@ public final class ResourceLoader { } private static void loadQuests() { - try { - Files.list(getResourcePath("BinOutput/Quest/")) - .forEach( - path -> { - try { - val mainQuest = JsonUtils.loadToClass(path, MainQuestData.class); - GameData.getMainQuestDataMap().put(mainQuest.getId(), mainQuest); - } catch (IOException e) { + try (var files = Files.list(getResourcePath("BinOutput/Quest/"))) { + files.forEach(path -> { + try { + val mainQuest = JsonUtils.loadToClass(path, MainQuestData.class); + GameData.getMainQuestDataMap().put(mainQuest.getId(), mainQuest); - } - }); + mainQuest.onLoad(); // Load the quest data. + } catch (IOException ignored) { } + }); } catch (IOException e) { Grasscutter.getLogger().error("Quest data missing"); return; @@ -507,17 +505,19 @@ public final class ResourceLoader { try { val questEncryptionMap = GameData.getMainQuestEncryptionMap(); - String path = "QuestEncryptionKeys.json"; + var path = "QuestEncryptionKeys.json"; try { JsonUtils.loadToList(getResourcePath(path), QuestEncryptionKey.class) .forEach(key -> questEncryptionMap.put(key.getMainQuestId(), key)); } catch (IOException | NullPointerException ignored) { } + try { DataLoader.loadList(path, QuestEncryptionKey.class) .forEach(key -> questEncryptionMap.put(key.getMainQuestId(), key)); } catch (IOException | NullPointerException ignored) { } + Grasscutter.getLogger().debug("Loaded {} quest keys.", questEncryptionMap.size()); } catch (Exception e) { Grasscutter.getLogger().error("Unable to load quest keys.", e); diff --git a/src/main/java/emu/grasscutter/data/binout/MainQuestData.java b/src/main/java/emu/grasscutter/data/binout/MainQuestData.java index ba9f17c31..7a5ca5213 100644 --- a/src/main/java/emu/grasscutter/data/binout/MainQuestData.java +++ b/src/main/java/emu/grasscutter/data/binout/MainQuestData.java @@ -1,78 +1,87 @@ -package emu.grasscutter.data.binout; - -import dev.morphia.annotations.Entity; -import emu.grasscutter.game.quest.enums.QuestType; -import java.util.List; -import java.util.Objects; -import lombok.Data; - -public class MainQuestData { - private int id; - private int ICLLDPJFIMA; - private int series; - private QuestType type; - - private long titleTextMapHash; - private int[] suggestTrackMainQuestList; - private int[] rewardIdList; - - private SubQuestData[] subQuests; - private List talks; - private long[] preloadLuaList; - - public int getId() { - return id; - } - - public int getSeries() { - return series; - } - - public QuestType getType() { - return type; - } - - public long getTitleTextMapHash() { - return titleTextMapHash; - } - - public int[] getSuggestTrackMainQuestList() { - return suggestTrackMainQuestList; - } - - public int[] getRewardIdList() { - return rewardIdList; - } - - public SubQuestData[] getSubQuests() { - return subQuests; - } - - public List getTalks() { - return talks; - } - - public void onLoad() { - this.talks = talks.stream().filter(Objects::nonNull).toList(); - } - - @Data - public static class SubQuestData { - private int subId; - private int order; - } - - @Data - @Entity - public static class TalkData { - private int id; - private String heroTalk; - - public TalkData() {} - - public TalkData(int id, String heroTalk) { - this.id = id; - this.heroTalk = heroTalk; - } - } -} +package emu.grasscutter.data.binout; + +import dev.morphia.annotations.Entity; +import emu.grasscutter.data.GameData; +import emu.grasscutter.game.quest.enums.QuestType; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import lombok.Data; + +public class MainQuestData { + private int id; + private int ICLLDPJFIMA; + private int series; + private QuestType type; + + private long titleTextMapHash; + private int[] suggestTrackMainQuestList; + private int[] rewardIdList; + + private SubQuestData[] subQuests; + private List talks; + private long[] preloadLuaList; + + public int getId() { + return id; + } + + public int getSeries() { + return series; + } + + public QuestType getType() { + return type; + } + + public long getTitleTextMapHash() { + return titleTextMapHash; + } + + public int[] getSuggestTrackMainQuestList() { + return suggestTrackMainQuestList; + } + + public int[] getRewardIdList() { + return rewardIdList; + } + + public SubQuestData[] getSubQuests() { + return subQuests; + } + + public List getTalks() { + return talks; + } + + public void onLoad() { + this.talks = talks.stream().filter(Objects::nonNull).toList(); + Arrays.stream(this.subQuests).forEach(quest -> { + var questData = GameData.getQuestDataMap().get(quest.getSubId()); + if (questData != null) questData.applyFrom(quest); + }); + } + + @Data + public static class SubQuestData { + private int subId; + private int order; + private boolean isRewind; + private boolean finishParent; + } + + @Data + @Entity + public static class TalkData { + private int id; + private String heroTalk; + + public TalkData() {} + + public TalkData(int id, String heroTalk) { + this.id = id; + this.heroTalk = heroTalk; + } + } +} diff --git a/src/main/java/emu/grasscutter/data/excels/QuestData.java b/src/main/java/emu/grasscutter/data/excels/QuestData.java index 3290b1e79..2789b295e 100644 --- a/src/main/java/emu/grasscutter/data/excels/QuestData.java +++ b/src/main/java/emu/grasscutter/data/excels/QuestData.java @@ -5,6 +5,7 @@ import emu.grasscutter.Grasscutter; import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameResource; import emu.grasscutter.data.ResourceType; +import emu.grasscutter.data.binout.MainQuestData; import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.game.quest.enums.*; import java.util.ArrayList; @@ -69,7 +70,12 @@ public class QuestData extends GameResource { if (this.gainItems == null) this.gainItems = Collections.emptyList(); - addToCache(); + this.addToCache(); + } + + public void applyFrom(MainQuestData.SubQuestData additionalData) { + this.isRewind = additionalData.isRewind(); + this.finishParent = additionalData.isFinishParent(); } private void addToCache() {