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