diff --git a/src/main/java/emu/grasscutter/Grasscutter.java b/src/main/java/emu/grasscutter/Grasscutter.java index a8bd09c2e..1f38fa5cf 100644 --- a/src/main/java/emu/grasscutter/Grasscutter.java +++ b/src/main/java/emu/grasscutter/Grasscutter.java @@ -45,7 +45,6 @@ import javax.annotation.Nullable; import java.io.*; import java.util.Calendar; -import static emu.grasscutter.config.Configuration.DATA; import static emu.grasscutter.config.Configuration.SERVER; import static emu.grasscutter.utils.Language.translate; @@ -203,7 +202,7 @@ public final class Grasscutter { // If the file already exists, we attempt to load it. try { - config = JsonUtils.loadToClass(configFile.getPath(), ConfigContainer.class); + config = JsonUtils.loadToClass(configFile.toPath(), ConfigContainer.class); } catch (Exception exception) { getLogger().error("There was an error while trying to load the configuration from config.json. Please make sure that there are no syntax errors. If you want to start with a default configuration, delete your existing config.json."); System.exit(1); diff --git a/src/main/java/emu/grasscutter/config/ConfigContainer.java b/src/main/java/emu/grasscutter/config/ConfigContainer.java index 9417de7c6..f8af1dae8 100644 --- a/src/main/java/emu/grasscutter/config/ConfigContainer.java +++ b/src/main/java/emu/grasscutter/config/ConfigContainer.java @@ -26,7 +26,7 @@ public class ConfigContainer { */ public static void updateConfig() { try { // Check if the server is using a legacy config. - JsonObject configObject = JsonUtils.loadToClass(Grasscutter.configFile.getPath(), JsonObject.class); + JsonObject configObject = JsonUtils.loadToClass(Grasscutter.configFile.toPath(), JsonObject.class); if (!configObject.has("version")) { Grasscutter.getLogger().info("Updating legacy .."); Grasscutter.saveConfig(null); diff --git a/src/main/java/emu/grasscutter/config/Configuration.java b/src/main/java/emu/grasscutter/config/Configuration.java index 7c769b3a3..bf008a41f 100644 --- a/src/main/java/emu/grasscutter/config/Configuration.java +++ b/src/main/java/emu/grasscutter/config/Configuration.java @@ -1,7 +1,15 @@ package emu.grasscutter.config; import java.util.Locale; +import java.util.stream.Stream; +import emu.grasscutter.Grasscutter; + +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import static emu.grasscutter.Grasscutter.config; @@ -30,6 +38,42 @@ public final class Configuration extends ConfigContainer { private static final String PLUGINS_FOLDER = config.folderStructure.plugins; private static final String SCRIPTS_FOLDER = config.folderStructure.scripts; private static final String PACKETS_FOLDER = config.folderStructure.packets; + private static final FileSystem RESOURCES_FILE_SYSTEM; // Not sure about lifetime rules on this one, might be safe to remove + private static final Path RESOURCES_PATH; + static { + FileSystem fs = null; + Path path = Path.of(RESOURCES_FOLDER); + if (RESOURCES_FOLDER.endsWith(".zip")) { // Would be nice to support .tar.gz too at some point, but it doesn't come for free in Java + try { + fs = FileSystems.newFileSystem(path); + } catch (IOException e) { + Grasscutter.getLogger().error("Failed to load resources zip \"" + RESOURCES_FOLDER + "\""); + } + } + + if (fs != null) { + var root = fs.getPath(""); + try (Stream pathStream = java.nio.file.Files.find(root, 3, (p, a) -> { + var filename = p.getFileName(); + if (filename == null) return false; + return filename.toString().equals("ExcelBinOutput"); + })) { + var excelBinOutput = pathStream.findFirst(); + if (excelBinOutput.isPresent()) { + path = excelBinOutput.get().getParent(); + if (path == null) + path = root; + Grasscutter.getLogger().debug("Resources will be loaded from \"" + RESOURCES_FOLDER + "/" + path.toString() + "\""); + } else { + Grasscutter.getLogger().error("Failed to find ExcelBinOutput in resources zip \"" + RESOURCES_FOLDER + "\""); + } + } catch (IOException e) { + Grasscutter.getLogger().error("Failed to scan resources zip \"" + RESOURCES_FOLDER + "\""); + } + } + RESOURCES_FILE_SYSTEM = fs; + RESOURCES_PATH = path; + }; public static final Server SERVER = config.server; public static final Database DATABASE = config.databaseInfo; @@ -54,11 +98,15 @@ public final class Configuration extends ConfigContainer { } public static String DATA(String path) { - return Paths.get(DATA_FOLDER, path).toString(); + return Path.of(DATA_FOLDER, path).toString(); + } + + public static Path getResourcePath(String path) { + return RESOURCES_PATH.resolve(path); } public static String RESOURCE(String path) { - return Paths.get(RESOURCES_FOLDER, path).toString(); + return getResourcePath(path).toString(); } public static String PLUGIN() { @@ -66,15 +114,15 @@ public final class Configuration extends ConfigContainer { } public static String PLUGIN(String path) { - return Paths.get(PLUGINS_FOLDER, path).toString(); + return Path.of(PLUGINS_FOLDER, path).toString(); } public static String SCRIPT(String path) { - return Paths.get(SCRIPTS_FOLDER, path).toString(); + return Path.of(SCRIPTS_FOLDER, path).toString(); } public static String PACKET(String path) { - return Paths.get(PACKETS_FOLDER, path).toString(); + return Path.of(PACKETS_FOLDER, path).toString(); } /** diff --git a/src/main/java/emu/grasscutter/data/ResourceLoader.java b/src/main/java/emu/grasscutter/data/ResourceLoader.java index 0a580d468..d71558160 100644 --- a/src/main/java/emu/grasscutter/data/ResourceLoader.java +++ b/src/main/java/emu/grasscutter/data/ResourceLoader.java @@ -5,7 +5,6 @@ import com.google.gson.annotations.SerializedName; import emu.grasscutter.Grasscutter; import emu.grasscutter.data.binout.*; import emu.grasscutter.data.binout.AbilityModifier.AbilityConfigData; -import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction; import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierActionType; import emu.grasscutter.data.common.PointData; import emu.grasscutter.data.common.ScenePointConfig; @@ -16,21 +15,19 @@ import emu.grasscutter.game.world.SpawnDataEntry.GridBlockId; import emu.grasscutter.game.world.SpawnDataEntry.SpawnGroupEntry; import emu.grasscutter.scripts.SceneIndexManager; import emu.grasscutter.utils.JsonUtils; -import emu.grasscutter.utils.Utils; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import lombok.SneakyThrows; +import lombok.val; + import org.reflections.Reflections; import java.io.*; import java.nio.file.Files; -import java.nio.file.Path; import java.util.*; -import java.util.Map.Entry; -import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Stream; import static emu.grasscutter.config.Configuration.DATA; -import static emu.grasscutter.config.Configuration.RESOURCE; +import static emu.grasscutter.config.Configuration.getResourcePath; import static emu.grasscutter.utils.Language.translate; public class ResourceLoader { @@ -105,7 +102,7 @@ public class ResourceLoader { try { loadFromResource(resourceDefinition, type, map, doReload); } catch (Exception e) { - Grasscutter.getLogger().error("Error loading resource file: " + Arrays.toString(type.name()), e); + Grasscutter.getLogger().error("Error loading resource file: " + Arrays.toString(type.name()), e.getLocalizedMessage()); } } } @@ -123,7 +120,7 @@ public class ResourceLoader { @SuppressWarnings({"rawtypes", "unchecked"}) protected static void loadFromResource(Class c, String fileName, Int2ObjectMap map) throws Exception { - List list = JsonUtils.loadToList(RESOURCE("ExcelBinOutput/" + fileName), c); + List list = JsonUtils.loadToList(getResourcePath("ExcelBinOutput/" + fileName), c); for (T o : list) { GameResource res = (GameResource) o; @@ -133,51 +130,43 @@ public class ResourceLoader { } private static void loadScenePoints() { - Pattern pattern = Pattern.compile("(?<=scene)(.*?)(?=_point.json)"); - File folder = new File(RESOURCE("BinOutput/Scene/Point")); + val pattern = Pattern.compile("scene([0-9]+)_point\\.json"); + try { + Files.newDirectoryStream(getResourcePath("BinOutput/Scene/Point"), "scene*_point.json").forEach(path -> { + val matcher = pattern.matcher(path.getFileName().toString()); + if (!matcher.find()) return; + int sceneId = Integer.parseInt(matcher.group(1)); + ScenePointConfig config; - if (!folder.isDirectory() || !folder.exists() || folder.listFiles() == null) { + try { + config = JsonUtils.loadToClass(path, ScenePointConfig.class); + } catch (Exception e) { + e.printStackTrace(); + return; + } + + if (config.points == null) return; + + List scenePoints = new ArrayList<>(); + for (Map.Entry entry : config.points.entrySet()) { + String key = entry.getKey(); + String name = sceneId + "_" + key; + int id = Integer.parseInt(key); + PointData pointData = JsonUtils.decode(entry.getValue(), PointData.class); + pointData.setId(id); + + GameData.getScenePointIdList().add(id); + GameData.getScenePointEntries().put(name, new ScenePointEntry(name, pointData)); + scenePoints.add(id); + + pointData.updateDailyDungeon(); + } + GameData.getScenePointsPerScene().put(sceneId, scenePoints); + }); + } catch (IOException e) { Grasscutter.getLogger().error("Scene point files cannot be found, you cannot use teleport waypoints!"); return; } - - for (File file : Objects.requireNonNull(folder.listFiles())) { - ScenePointConfig config; - Integer sceneId; - - Matcher matcher = pattern.matcher(file.getName()); - if (matcher.find()) { - sceneId = Integer.parseInt(matcher.group(1)); - } else { - continue; - } - - try { - config = JsonUtils.loadToClass(file.getPath(), ScenePointConfig.class); - } catch (Exception e) { - e.printStackTrace(); - continue; - } - - if (config.points == null) { - continue; - } - - List scenePoints = new ArrayList<>(); - for (Map.Entry entry : config.points.entrySet()) { - int id = Integer.parseInt(entry.getKey()); - String name = sceneId + "_" + entry.getKey(); - PointData pointData = JsonUtils.decode(entry.getValue(), PointData.class); - pointData.setId(id); - - GameData.getScenePointIdList().add(id); - GameData.getScenePointEntries().put(name, new ScenePointEntry(name, pointData)); - scenePoints.add(id); - - pointData.updateDailyDungeon(); - } - GameData.getScenePointsPerScene().put(sceneId, scenePoints); - } } private static void loadAbilityEmbryos() { @@ -190,47 +179,40 @@ public class ResourceLoader { if (embryoList == null) { // Load from BinOutput - Pattern pattern = Pattern.compile("(?<=ConfigAvatar_)(.*?)(?=.json)"); + val pattern = Pattern.compile("ConfigAvatar_(.+?)\\.json"); - embryoList = new ArrayList<>(); - File folder = new File(Utils.toFilePath(RESOURCE("BinOutput/Avatar/"))); - File[] files = folder.listFiles(); - if (files == null) { - Grasscutter.getLogger().error("Error loading ability embryos: no files found in " + folder.getAbsolutePath()); + val l = new ArrayList(); + try { + Files.newDirectoryStream(getResourcePath("BinOutput/Avatar/"), "ConfigAvatar_*.json").forEach(path -> { + val matcher = pattern.matcher(path.getFileName().toString()); + if (!matcher.find()) return; + String avatarName = matcher.group(1); + AvatarConfig config; + + try { + config = JsonUtils.loadToClass(path, AvatarConfig.class); + } catch (Exception e) { + Grasscutter.getLogger().error("Error loading player ability embryos:", e); + return; + } + + if (config.abilities == null) return; + + int s = config.abilities.size(); + AbilityEmbryoEntry al = new AbilityEmbryoEntry(avatarName, config.abilities.stream().map(Object::toString).toArray(size -> new String[s])); + l.add(al); + }); + } catch (IOException e) { + Grasscutter.getLogger().error("Error loading ability embryos: no files found"); return; } - for (File file : files) { - AvatarConfig config; - String avatarName; - - Matcher matcher = pattern.matcher(file.getName()); - if (matcher.find()) { - avatarName = matcher.group(0); - } else { - continue; - } - - try { - config = JsonUtils.loadToClass(file.getPath(), AvatarConfig.class); - } catch (Exception e) { - e.printStackTrace(); - continue; - } - - if (config.abilities == null) { - continue; - } - - int s = config.abilities.size(); - AbilityEmbryoEntry al = new AbilityEmbryoEntry(avatarName, config.abilities.stream().map(Object::toString).toArray(size -> new String[s])); - embryoList.add(al); - } + embryoList = l; try { - GameDepot.setPlayerAbilities(JsonUtils.loadToMap(RESOURCE("BinOutput/AbilityGroup/AbilityGroup_Other_PlayerElementAbility.json"), String.class, AvatarConfig.class)); - } catch (Exception e) { - e.printStackTrace(); + GameDepot.setPlayerAbilities(JsonUtils.loadToMap(getResourcePath("BinOutput/AbilityGroup/AbilityGroup_Other_PlayerElementAbility.json"), String.class, AvatarConfig.class)); + } catch (IOException e) { + Grasscutter.getLogger().error("Error loading player abilities:", e); } } @@ -246,64 +228,54 @@ public class ResourceLoader { private static void loadAbilityModifiers() { // Load from BinOutput - File folder = new File(Utils.toFilePath(RESOURCE("BinOutput/Ability/Temp/AvatarAbilities/"))); - File[] files = folder.listFiles(); - if (files == null) { - Grasscutter.getLogger().error("Error loading ability modifiers: no files found in " + folder.getAbsolutePath()); - return; - } + try { + Files.newDirectoryStream(getResourcePath("BinOutput/Ability/Temp/AvatarAbilities/")).forEach(path -> { + List abilityConfigList; - for (File file : files) { - List abilityConfigList; - - try { - abilityConfigList = JsonUtils.loadToList(file.getPath(), AbilityConfigData.class); - } catch (Exception e) { - e.printStackTrace(); - continue; - } - - for (AbilityConfigData data : abilityConfigList) { - if (data.Default.modifiers == null || data.Default.modifiers.size() == 0) { - continue; + try { + abilityConfigList = JsonUtils.loadToList(path, AbilityConfigData.class); + } catch (IOException e) { + Grasscutter.getLogger().error("Error loading ability modifiers from path " + path.toString() + ": ", e); + return; } - AbilityModifierEntry modifierEntry = new AbilityModifierEntry(data.Default.abilityName); + abilityConfigList.forEach(data -> { + if (data.Default.modifiers == null || data.Default.modifiers.size() == 0) { + return; + } - for (Entry entry : data.Default.modifiers.entrySet()) { - AbilityModifier modifier = entry.getValue(); - - // Stare. - if (modifier.onAdded != null) { - for (AbilityModifierAction action : modifier.onAdded) { - if (action.$type.contains("HealHP")) { + String name = data.Default.abilityName; + AbilityModifierEntry modifierEntry = new AbilityModifierEntry(name); + data.Default.modifiers.forEach((key, modifier) -> { + Stream.ofNullable(modifier.onAdded) + .flatMap(Stream::of) + .filter(action -> action.$type.contains("HealHP")) + .forEach(action -> { action.type = AbilityModifierActionType.HealHP; modifierEntry.getOnAdded().add(action); - } - } - } - - if (modifier.onThinkInterval != null) { - for (AbilityModifierAction action : modifier.onThinkInterval) { - if (action.$type.contains("HealHP")) { + }); + Stream.ofNullable(modifier.onThinkInterval) + .flatMap(Stream::of) + .filter(action -> action.$type.contains("HealHP")) + .forEach(action -> { action.type = AbilityModifierActionType.HealHP; modifierEntry.getOnThinkInterval().add(action); - } - } - } - - if (modifier.onRemoved != null) { - for (AbilityModifierAction action : modifier.onRemoved) { - if (action.$type.contains("HealHP")) { + }); + Stream.ofNullable(modifier.onRemoved) + .flatMap(Stream::of) + .filter(action -> action.$type.contains("HealHP")) + .forEach(action -> { action.type = AbilityModifierActionType.HealHP; modifierEntry.getOnRemoved().add(action); - } - } - } - } + }); + }); - GameData.getAbilityModifiers().put(modifierEntry.getName(), modifierEntry); - } + GameData.getAbilityModifiers().put(name, modifierEntry); + }); + }); + } catch (IOException e) { + Grasscutter.getLogger().error("Error loading ability modifiers: ", e); + return; } } @@ -353,30 +325,20 @@ public class ResourceLoader { Map map = new TreeMap<>(); String[] folderNames = {"BinOutput/Talent/EquipTalents/", "BinOutput/Talent/AvatarTalents/"}; - for (String name : folderNames) { - File folder = new File(Utils.toFilePath(RESOURCE(name))); - File[] files = folder.listFiles(); - if (files == null) { - Grasscutter.getLogger().error("Error loading open config: no files found in " + folder.getAbsolutePath()); return; - } - - for (File file : files) { - if (!file.getName().endsWith(".json")) { - continue; - } - Map config; - - try { - config = JsonUtils.loadToMap(file.getPath(), String.class, OpenConfigData[].class); - } catch (Exception e) { - e.printStackTrace(); - continue; - } - - for (Entry e : config.entrySet()) { - OpenConfigEntry entry = new OpenConfigEntry(e.getKey(), e.getValue()); - map.put(entry.getName(), entry); - } + for (String folderName : folderNames) { + try { + Files.newDirectoryStream(getResourcePath(folderName), "*.json").forEach(path -> { + try { + JsonUtils.loadToMap(path, String.class, OpenConfigData[].class) + .forEach((name, data) -> map.put(name, new OpenConfigEntry(name, data))); + } catch (Exception e) { + e.printStackTrace(); + return; + } + }); + } catch (IOException e) { + Grasscutter.getLogger().error("Error loading open config: no files found in " + folderName); + return; } } @@ -394,37 +356,29 @@ public class ResourceLoader { } private static void loadQuests() { - File folder = new File(RESOURCE("BinOutput/Quest/")); + 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) { - if (!folder.exists()) { + } + }); + } catch (IOException e) { + Grasscutter.getLogger().error("Quest data missing"); return; } - for (File file : folder.listFiles()) { - MainQuestData mainQuest = null; - - try { - mainQuest = JsonUtils.loadToClass(file.getPath(), MainQuestData.class); - } catch (Exception e) { - e.printStackTrace(); - continue; - } - - GameData.getMainQuestDataMap().put(mainQuest.getId(), mainQuest); - } - try { - List keys; - Int2ObjectMap questEncryptionMap = GameData.getMainQuestEncryptionMap(); + val questEncryptionMap = GameData.getMainQuestEncryptionMap(); String path = "QuestEncryptionKeys.json"; - if (Utils.fileExists(RESOURCE(path))) { - keys = JsonUtils.loadToList(RESOURCE(path), QuestEncryptionKey.class); - keys.forEach(key -> questEncryptionMap.put(key.getMainQuestId(), key)); - } - if (Utils.fileExists(DATA(path))) { - keys = DataLoader.loadList(path, QuestEncryptionKey.class); - keys.forEach(key -> questEncryptionMap.put(key.getMainQuestId(), key)); - } + 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); @@ -434,95 +388,84 @@ public class ResourceLoader { } public static void loadScriptSceneData() { - File folder = new File(RESOURCE("ScriptSceneData/")); - - if (!folder.exists()) { - return; - } - - for (File file : folder.listFiles()) { - ScriptSceneData sceneData; - try { - sceneData = JsonUtils.loadToClass(file.getPath(), ScriptSceneData.class); - } catch (Exception e) { - e.printStackTrace(); - continue; - } - - GameData.getScriptSceneDataMap().put(file.getName(), sceneData); - } - - Grasscutter.getLogger().debug("Loaded " + GameData.getScriptSceneDataMap().size() + " ScriptSceneDatas."); - } - - @SneakyThrows - private static void loadHomeworldDefaultSaveData() { - var pattern = Pattern.compile("scene(.*)_home_config.json"); - Files.list(Path.of(RESOURCE("BinOutput/HomeworldDefaultSave"))).forEach(file -> { - String filename = file.getFileName().toString(); - var matcher = pattern.matcher(filename); - if (!matcher.find()) { - return; - } - try { - var sceneId = Integer.parseInt(matcher.group(1)); - var data = JsonUtils.loadToClass(file.toString(), HomeworldDefaultSaveData.class); - GameData.getHomeworldDefaultSaveData().put(sceneId, data); - } catch (Exception ignored) {} - }); - - Grasscutter.getLogger().debug("Loaded " + GameData.getHomeworldDefaultSaveData().size() + " HomeworldDefaultSaveDatas."); - } - - @SneakyThrows - private static void loadNpcBornData() { - Files.list(Path.of(RESOURCE("BinOutput/Scene/SceneNpcBorn"))).forEach(file -> { - if (file.toFile().isDirectory()) { - return; - } - try { - var data = JsonUtils.loadToClass(file.toString(), SceneNpcBornData.class); - if (data.getBornPosList() == null || data.getBornPosList().size() == 0) { + try { + Files.list(getResourcePath("ScriptSceneData/")).forEach(path -> { + try { + GameData.getScriptSceneDataMap().put(path.getFileName().toString(), JsonUtils.loadToClass(path, ScriptSceneData.class)); + } catch (IOException e) { + e.printStackTrace(); return; } - - data.setIndex(SceneIndexManager.buildIndex(3, data.getBornPosList(), item -> item.getPos().toPoint())); - GameData.getSceneNpcBornData().put(data.getSceneId(), data); - } catch (Exception ignored) {} - }); - - Grasscutter.getLogger().debug("Loaded " + GameData.getSceneNpcBornData().size() + " SceneNpcBornDatas."); + }); + Grasscutter.getLogger().debug("Loaded " + GameData.getScriptSceneDataMap().size() + " ScriptSceneDatas."); + } catch (IOException e) { + Grasscutter.getLogger().debug("ScriptSceneData folder missing or empty."); + return; + } + } + + private static void loadHomeworldDefaultSaveData() { + val pattern = Pattern.compile("scene([0-9]+)_home_config\\.json"); + try { + Files.newDirectoryStream(getResourcePath("BinOutput/HomeworldDefaultSave"), "scene*_home_config.json").forEach(path -> { + val matcher = pattern.matcher(path.getFileName().toString()); + if (!matcher.find()) return; + + try { + val sceneId = Integer.parseInt(matcher.group(1)); + val data = JsonUtils.loadToClass(path, HomeworldDefaultSaveData.class); + GameData.getHomeworldDefaultSaveData().put(sceneId, data); + } catch (Exception ignored) {} + }); + Grasscutter.getLogger().debug("Loaded " + GameData.getHomeworldDefaultSaveData().size() + " HomeworldDefaultSaveDatas."); + } catch (IOException e) { + Grasscutter.getLogger().error("Failed to load HomeworldDefaultSave folder."); + } + } + + private static void loadNpcBornData() { + try { + Files.newDirectoryStream(getResourcePath("BinOutput/Scene/SceneNpcBorn/"), "*.json").forEach(path -> { + try { + val data = JsonUtils.loadToClass(path, SceneNpcBornData.class); + if (data.getBornPosList() == null || data.getBornPosList().size() == 0) { + return; + } + + data.setIndex(SceneIndexManager.buildIndex(3, data.getBornPosList(), item -> item.getPos().toPoint())); + GameData.getSceneNpcBornData().put(data.getSceneId(), data); + } catch (IOException ignored) {} + }); + Grasscutter.getLogger().debug("Loaded " + GameData.getSceneNpcBornData().size() + " SceneNpcBornDatas."); + } catch (IOException e) { + Grasscutter.getLogger().error("Failed to load SceneNpcBorn folder."); + } } - @SneakyThrows private static void loadGadgetConfigData() { - Files.list(Path.of(RESOURCE("BinOutput/Gadget/"))).forEach(filePath -> { - var file = filePath.toFile(); - if (file.isDirectory() || !file.getName().endsWith("json")) { - return; - } + try { + Files.newDirectoryStream(getResourcePath("BinOutput/Gadget/"), "*.json").forEach(path -> { + try { + GameData.getGadgetConfigData().putAll(JsonUtils.loadToMap(path, String.class, ConfigGadget.class)); + } catch (Exception e) { + Grasscutter.getLogger().error("failed to load ConfigGadget entries for " + path.toString(), e); + return; + } + }); - Map config; - - try { - config = JsonUtils.loadToMap(filePath.toString(), String.class, ConfigGadget.class); - } catch (Exception e) { - Grasscutter.getLogger().error("failed to load ConfigGadget entries for "+filePath, e); - return; - } - - for (Entry e : config.entrySet()) { - GameData.getGadgetConfigData().put(e.getKey(), e.getValue()); - } - }); - - Grasscutter.getLogger().debug("Loaded {} ConfigGadget entries.", GameData.getGadgetConfigData().size()); + Grasscutter.getLogger().debug("Loaded {} ConfigGadget entries.", GameData.getGadgetConfigData().size()); + } catch (IOException e) { + Grasscutter.getLogger().error("Failed to load ConfigGadget folder."); + } } - @SneakyThrows private static void loadBlossomResources() { - GameDepot.setBlossomConfig(DataLoader.loadClass("BlossomConfig.json", BlossomConfig.class)); - Grasscutter.getLogger().debug("Loaded BlossomConfig."); + try { + GameDepot.setBlossomConfig(DataLoader.loadClass("BlossomConfig.json", BlossomConfig.class)); + Grasscutter.getLogger().debug("Loaded BlossomConfig."); + } catch (IOException e) { + Grasscutter.getLogger().warn("Failed to load BlossomConfig."); + } } // BinOutput configs diff --git a/src/main/java/emu/grasscutter/tools/Tools.java b/src/main/java/emu/grasscutter/tools/Tools.java index 792b8e77e..0823afaf9 100644 --- a/src/main/java/emu/grasscutter/tools/Tools.java +++ b/src/main/java/emu/grasscutter/tools/Tools.java @@ -2,9 +2,11 @@ package emu.grasscutter.tools; import java.io.File; import java.io.FileOutputStream; +import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; @@ -215,11 +217,15 @@ public final class Tools { } public static List getAvailableLanguage() { - File textMapFolder = new File(RESOURCE("TextMap")); List availableLangList = new ArrayList<>(); - for (String textMapFileName : Objects.requireNonNull(textMapFolder.list((dir, name) -> name.startsWith("TextMap") && name.endsWith(".json")))) { - availableLangList.add(textMapFileName.replace("TextMap", "").replace(".json", "").toLowerCase()); - } return availableLangList; + try { + Files.newDirectoryStream(getResourcePath("TextMap"), "TextMap*.json").forEach(path -> { + availableLangList.add(path.getFileName().toString().replace("TextMap", "").replace(".json", "").toLowerCase()); + }); + } catch (IOException e) { + Grasscutter.getLogger().error("Failed to get available languages:", e); + } + return availableLangList; } @Deprecated(forRemoval = true, since = "1.2.3") diff --git a/src/main/java/emu/grasscutter/utils/FileUtils.java b/src/main/java/emu/grasscutter/utils/FileUtils.java index ff3c23e86..6ef4f7763 100644 --- a/src/main/java/emu/grasscutter/utils/FileUtils.java +++ b/src/main/java/emu/grasscutter/utils/FileUtils.java @@ -9,7 +9,6 @@ import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.nio.file.*; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -17,7 +16,7 @@ import java.util.stream.Collectors; public final class FileUtils { public static void write(String dest, byte[] bytes) { - Path path = Paths.get(dest); + Path path = Path.of(dest); try { Files.write(path, bytes); @@ -27,7 +26,7 @@ public final class FileUtils { } public static byte[] read(String dest) { - return read(Paths.get(dest)); + return read(Path.of(dest)); } public static byte[] read(Path path) { diff --git a/src/main/java/emu/grasscutter/utils/JsonUtils.java b/src/main/java/emu/grasscutter/utils/JsonUtils.java index ea2b34834..5742b87f1 100644 --- a/src/main/java/emu/grasscutter/utils/JsonUtils.java +++ b/src/main/java/emu/grasscutter/utils/JsonUtils.java @@ -3,7 +3,10 @@ package emu.grasscutter.utils; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; +import java.io.Reader; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; import java.util.Map; @@ -32,36 +35,57 @@ public final class JsonUtils { return gson.fromJson(jsonElement, classType); } - public static T loadToClass(InputStreamReader fileReader, Class classType) throws IOException { + public static T loadToClass(Reader fileReader, Class classType) throws IOException { return gson.fromJson(fileReader, classType); } + @Deprecated public static T loadToClass(String filename, Class classType) throws IOException { try (InputStreamReader fileReader = new InputStreamReader(new FileInputStream(Utils.toFilePath(filename)), StandardCharsets.UTF_8)) { return loadToClass(fileReader, classType); } } - public static List loadToList(InputStreamReader fileReader, Class classType) throws IOException { + public static T loadToClass(Path filename, Class classType) throws IOException { + try (var fileReader = Files.newBufferedReader(filename, StandardCharsets.UTF_8)) { + return loadToClass(fileReader, classType); + } + } + + public static List loadToList(Reader fileReader, Class classType) throws IOException { return gson.fromJson(fileReader, TypeToken.getParameterized(List.class, classType).getType()); } + @Deprecated public static List loadToList(String filename, Class classType) throws IOException { try (InputStreamReader fileReader = new InputStreamReader(new FileInputStream(Utils.toFilePath(filename)), StandardCharsets.UTF_8)) { return loadToList(fileReader, classType); } } - public static Map loadToMap(InputStreamReader fileReader, Class keyType, Class valueType) throws IOException { + public static List loadToList(Path filename, Class classType) throws IOException { + try (var fileReader = Files.newBufferedReader(filename, StandardCharsets.UTF_8)) { + return loadToList(fileReader, classType); + } + } + + public static Map loadToMap(Reader fileReader, Class keyType, Class valueType) throws IOException { return gson.fromJson(fileReader, TypeToken.getParameterized(Map.class, keyType, valueType).getType()); } + @Deprecated public static Map loadToMap(String filename, Class keyType, Class valueType) throws IOException { try (InputStreamReader fileReader = new InputStreamReader(new FileInputStream(Utils.toFilePath(filename)), StandardCharsets.UTF_8)) { return loadToMap(fileReader, keyType, valueType); } } + public static Map loadToMap(Path filename, Class keyType, Class valueType) throws IOException { + try (var fileReader = Files.newBufferedReader(filename, StandardCharsets.UTF_8)) { + return loadToMap(fileReader, keyType, valueType); + } + } + /** * Safely JSON decodes a given string. * @param jsonData The JSON-encoded data. diff --git a/src/main/java/emu/grasscutter/utils/Language.java b/src/main/java/emu/grasscutter/utils/Language.java index c06a7c967..f82fdb166 100644 --- a/src/main/java/emu/grasscutter/utils/Language.java +++ b/src/main/java/emu/grasscutter/utils/Language.java @@ -325,7 +325,7 @@ public final class Language { private static Int2ObjectMap loadTextMapFile(String language, IntSet nameHashes) { Int2ObjectMap output = new Int2ObjectOpenHashMap<>(); - try (BufferedReader file = new BufferedReader(new FileReader(Utils.toFilePath(RESOURCE("TextMap/TextMap"+language+".json")), StandardCharsets.UTF_8))) { + try (BufferedReader file = Files.newBufferedReader(getResourcePath("TextMap/TextMap"+language+".json"), StandardCharsets.UTF_8)) { Matcher matcher = textMapKeyValueRegex.matcher(""); return new Int2ObjectOpenHashMap<>( file.lines() @@ -406,7 +406,7 @@ public final class Language { try { long cacheModified = Files.getLastModifiedTime(TEXTMAP_CACHE_PATH).toMillis(); - long textmapsModified = Files.list(Path.of(RESOURCE("TextMap"))) + long textmapsModified = Files.list(getResourcePath("TextMap")) .filter(path -> path.toString().endsWith(".json")) .map(path -> { try { diff --git a/src/main/java/emu/grasscutter/utils/Utils.java b/src/main/java/emu/grasscutter/utils/Utils.java index bc09a6e60..9ae7f95bf 100644 --- a/src/main/java/emu/grasscutter/utils/Utils.java +++ b/src/main/java/emu/grasscutter/utils/Utils.java @@ -22,6 +22,7 @@ import org.slf4j.Logger; import javax.annotation.Nullable; +import static emu.grasscutter.config.Configuration.getResourcePath; import static emu.grasscutter.utils.Language.translate; @SuppressWarnings({"UnusedReturnValue", "BooleanMethodIsAlwaysInverted"}) @@ -169,19 +170,18 @@ public final class Utils { Logger logger = Grasscutter.getLogger(); boolean exit = false; - String resourcesFolder = config.folderStructure.resources; String dataFolder = config.folderStructure.data; // Check for resources folder. - if (!fileExists(resourcesFolder)) { + if (!Files.exists(getResourcePath(""))) { logger.info(translate("messages.status.create_resources")); logger.info(translate("messages.status.resources_error")); - createFolder(resourcesFolder); exit = true; + createFolder(config.folderStructure.resources); exit = true; } // Check for BinOutput + ExcelBinOutput. - if (!fileExists(resourcesFolder + "BinOutput") || - !fileExists(resourcesFolder + "ExcelBinOutput")) { + if (!Files.exists(getResourcePath("BinOutput")) || + !Files.exists(getResourcePath("ExcelBinOutput"))) { logger.info(translate("messages.status.resources_error")); exit = true; }