Grasscutter/src/main/java/emu/grasscutter/data/ResourceLoader.java

470 lines
14 KiB
Java
Raw Normal View History

2022-04-17 20:43:07 +08:00
package emu.grasscutter.data;
import java.io.*;
2022-05-25 10:44:46 +08:00
import java.nio.file.Files;
import java.nio.file.Path;
2022-04-19 11:26:34 +08:00
import java.util.*;
2022-04-17 20:43:07 +08:00
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
2022-05-25 10:44:46 +08:00
import ch.ethz.globis.phtree.PhTree;
import ch.ethz.globis.phtree.v16.PhTree16;
import com.google.gson.Gson;
2022-05-25 10:44:46 +08:00
import emu.grasscutter.data.custom.*;
2022-04-19 11:26:34 +08:00
import emu.grasscutter.utils.Utils;
2022-05-25 10:44:46 +08:00
import lombok.SneakyThrows;
2022-04-17 20:43:07 +08:00
import org.reflections.Reflections;
2022-04-21 04:45:38 +08:00
import com.google.gson.JsonElement;
2022-04-17 20:43:07 +08:00
import com.google.gson.reflect.TypeToken;
import emu.grasscutter.Grasscutter;
2022-04-21 04:45:38 +08:00
import emu.grasscutter.data.common.PointData;
import emu.grasscutter.data.common.ScenePointConfig;
2022-05-08 19:40:01 +08:00
import emu.grasscutter.data.custom.AbilityModifier.AbilityConfigData;
import emu.grasscutter.data.custom.AbilityModifier.AbilityModifierAction;
import emu.grasscutter.data.custom.AbilityModifier.AbilityModifierActionType;
import emu.grasscutter.game.world.SpawnDataEntry.*;
2022-04-17 20:43:07 +08:00
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import static emu.grasscutter.Configuration.*;
2022-04-17 20:43:07 +08:00
public class ResourceLoader {
private static List<String> loadedResources = new ArrayList<String>();
2022-04-17 20:43:07 +08:00
public static List<Class<?>> getResourceDefClasses() {
Reflections reflections = new Reflections(ResourceLoader.class.getPackage().getName());
Set<?> classes = reflections.getSubTypesOf(GameResource.class);
2022-04-17 20:43:07 +08:00
List<Class<?>> classList = new ArrayList<>(classes.size());
classes.forEach(o -> {
Class<?> c = (Class<?>) o;
if (c.getAnnotation(ResourceType.class) != null) {
classList.add(c);
}
});
2022-04-19 11:26:34 +08:00
classList.sort((a, b) -> b.getAnnotation(ResourceType.class).loadPriority().value() - a.getAnnotation(ResourceType.class).loadPriority().value());
2022-04-17 20:43:07 +08:00
return classList;
}
public static void loadAll() {
// Load ability lists
loadAbilityEmbryos();
loadOpenConfig();
2022-05-08 19:40:01 +08:00
loadAbilityModifiers();
2022-04-17 20:43:07 +08:00
// Load resources
loadResources();
// Process into depots
GameDepot.load();
2022-05-11 18:56:40 +08:00
// Load spawn data and quests
2022-04-25 16:50:58 +08:00
loadSpawnData();
2022-05-11 18:56:40 +08:00
loadQuests();
// Load scene points - must be done AFTER resources are loaded
loadScenePoints();
2022-05-25 10:44:46 +08:00
loadNpcBornData();
2022-04-17 20:43:07 +08:00
// Custom - TODO move this somewhere else
try {
GameData.getAvatarSkillDepotDataMap().get(504).setAbilities(
2022-04-17 20:43:07 +08:00
new AbilityEmbryoEntry(
"",
new String[] {
"Avatar_PlayerBoy_ExtraAttack_Wind",
"Avatar_Player_UziExplode_Mix",
"Avatar_Player_UziExplode",
"Avatar_Player_UziExplode_Strike_01",
"Avatar_Player_UziExplode_Strike_02",
"Avatar_Player_WindBreathe",
"Avatar_Player_WindBreathe_CameraController"
}
));
GameData.getAvatarSkillDepotDataMap().get(704).setAbilities(
2022-04-17 20:43:07 +08:00
new AbilityEmbryoEntry(
"",
new String[] {
"Avatar_PlayerGirl_ExtraAttack_Wind",
"Avatar_Player_UziExplode_Mix",
"Avatar_Player_UziExplode",
"Avatar_Player_UziExplode_Strike_01",
"Avatar_Player_UziExplode_Strike_02",
"Avatar_Player_WindBreathe",
"Avatar_Player_WindBreathe_CameraController"
}
));
} catch (Exception e) {
Grasscutter.getLogger().error("Error loading abilities", e);
}
}
public static void loadResources() {
loadResources(false);
}
public static void loadResources(boolean doReload) {
2022-04-17 20:43:07 +08:00
for (Class<?> resourceDefinition : getResourceDefClasses()) {
ResourceType type = resourceDefinition.getAnnotation(ResourceType.class);
if (type == null) {
continue;
}
@SuppressWarnings("rawtypes")
Int2ObjectMap map = GameData.getMapByResourceDef(resourceDefinition);
2022-04-17 20:43:07 +08:00
if (map == null) {
continue;
}
try {
loadFromResource(resourceDefinition, type, map, doReload);
2022-04-17 20:43:07 +08:00
} catch (Exception e) {
2022-04-19 11:26:34 +08:00
Grasscutter.getLogger().error("Error loading resource file: " + Arrays.toString(type.name()), e);
2022-04-17 20:43:07 +08:00
}
}
}
@SuppressWarnings("rawtypes")
protected static void loadFromResource(Class<?> c, ResourceType type, Int2ObjectMap map, boolean doReload) throws Exception {
if(!loadedResources.contains(c.getSimpleName()) || doReload) {
for (String name : type.name()) {
loadFromResource(c, name, map);
}
Grasscutter.getLogger().info("Loaded " + map.size() + " " + c.getSimpleName() + "s.");
loadedResources.add(c.getSimpleName());
2022-04-17 20:43:07 +08:00
}
}
2022-04-17 20:43:07 +08:00
@SuppressWarnings({"rawtypes", "unchecked"})
protected static void loadFromResource(Class<?> c, String fileName, Int2ObjectMap map) throws Exception {
FileReader fileReader = new FileReader(RESOURCE("ExcelBinOutput/" + fileName));
Gson gson = Grasscutter.getGsonFactory();
List list = gson.fromJson(fileReader, List.class);
for (Object o : list) {
Map<String, Object> tempMap = Utils.switchPropertiesUpperLowerCase((Map<String, Object>) o, c);
GameResource res = gson.fromJson(gson.toJson(tempMap), TypeToken.get(c).getType());
res.onLoad();
if(map.containsKey(res.getId())) {
map.remove(res.getId());
}
map.put(res.getId(), res);
2022-04-17 20:43:07 +08:00
}
}
2022-04-21 04:45:38 +08:00
private static void loadScenePoints() {
Pattern pattern = Pattern.compile("(?<=scene)(.*?)(?=_point.json)");
File folder = new File(RESOURCE("BinOutput/Scene/Point"));
2022-04-21 10:37:24 +08:00
if (!folder.isDirectory() || !folder.exists() || folder.listFiles() == null) {
Grasscutter.getLogger().error("Scene point files cannot be found, you cannot use teleport waypoints!");
return;
}
2022-04-21 04:45:38 +08:00
List<ScenePointEntry> scenePointList = new ArrayList<>();
for (File file : Objects.requireNonNull(folder.listFiles())) {
ScenePointConfig config; Integer sceneId;
2022-04-21 04:45:38 +08:00
Matcher matcher = pattern.matcher(file.getName());
if (matcher.find()) {
sceneId = Integer.parseInt(matcher.group(1));
} else {
continue;
}
try (FileReader fileReader = new FileReader(file)) {
config = Grasscutter.getGsonFactory().fromJson(fileReader, ScenePointConfig.class);
} catch (Exception e) {
e.printStackTrace();
continue;
}
if (config.points == null) {
continue;
}
for (Map.Entry<String, JsonElement> entry : config.points.entrySet()) {
PointData pointData = Grasscutter.getGsonFactory().fromJson(entry.getValue(), PointData.class);
2022-04-28 23:20:37 +08:00
pointData.setId(Integer.parseInt(entry.getKey()));
2022-04-21 04:45:38 +08:00
ScenePointEntry sl = new ScenePointEntry(sceneId + "_" + entry.getKey(), pointData);
scenePointList.add(sl);
GameData.getScenePointIdList().add(pointData.getId());
pointData.updateDailyDungeon();
2022-04-21 04:45:38 +08:00
}
for (ScenePointEntry entry : scenePointList) {
GameData.getScenePointEntries().put(entry.getName(), entry);
2022-04-21 04:45:38 +08:00
}
}
}
2022-04-17 20:43:07 +08:00
private static void loadAbilityEmbryos() {
List<AbilityEmbryoEntry> embryoList = null;
// Read from cached file if exists
try(InputStream embryoCache = DataLoader.load("AbilityEmbryos.json", false)) {
embryoList = Grasscutter.getGsonFactory().fromJson(new InputStreamReader(embryoCache), TypeToken.getParameterized(Collection.class, AbilityEmbryoEntry.class).getType());
} catch(Exception ignored) {}
if(embryoList == null) {
2022-04-17 20:43:07 +08:00
// Load from BinOutput
Pattern pattern = Pattern.compile("(?<=ConfigAvatar_)(.*?)(?=.json)");
2022-04-17 20:43:07 +08:00
embryoList = new LinkedList<>();
File folder = new File(Utils.toFilePath(RESOURCE("BinOutput/Avatar/")));
2022-04-19 11:26:34 +08:00
File[] files = folder.listFiles();
if(files == null) {
Grasscutter.getLogger().error("Error loading ability embryos: no files found in " + folder.getAbsolutePath());
return;
}
2022-04-19 11:26:34 +08:00
for (File file : files) {
AvatarConfig config;
String avatarName;
2022-04-17 20:43:07 +08:00
Matcher matcher = pattern.matcher(file.getName());
if (matcher.find()) {
avatarName = matcher.group(0);
} else {
continue;
}
2022-04-17 20:43:07 +08:00
try (FileReader fileReader = new FileReader(file)) {
config = Grasscutter.getGsonFactory().fromJson(fileReader, AvatarConfig.class);
} catch (Exception e) {
e.printStackTrace();
continue;
}
2022-04-17 20:43:07 +08:00
if (config.abilities == null) {
continue;
}
2022-04-17 20:43:07 +08:00
int s = config.abilities.size();
AbilityEmbryoEntry al = new AbilityEmbryoEntry(avatarName, config.abilities.stream().map(Object::toString).toArray(size -> new String[s]));
embryoList.add(al);
}
}
if (embryoList == null || embryoList.isEmpty()) {
Grasscutter.getLogger().error("No embryos loaded!");
return;
}
for (AbilityEmbryoEntry entry : embryoList) {
GameData.getAbilityEmbryoInfo().put(entry.getName(), entry);
2022-04-17 20:43:07 +08:00
}
}
2022-05-08 19:40:01 +08:00
private static void loadAbilityModifiers() {
// Load from BinOutput
File folder = new File(Utils.toFilePath(RESOURCE("BinOutput/Ability/Temp/AvatarAbilities/")));
2022-05-08 19:40:01 +08:00
File[] files = folder.listFiles();
if (files == null) {
Grasscutter.getLogger().error("Error loading ability modifiers: no files found in " + folder.getAbsolutePath());
return;
}
for (File file : files) {
List<AbilityConfigData> abilityConfigList;
2022-05-08 19:40:01 +08:00
try (FileReader fileReader = new FileReader(file)) {
abilityConfigList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, AbilityConfigData.class).getType());
} catch (Exception e) {
e.printStackTrace();
continue;
}
for (AbilityConfigData data : abilityConfigList) {
if (data.Default.modifiers == null || data.Default.modifiers.size() == 0) {
continue;
}
AbilityModifierEntry modifierEntry = new AbilityModifierEntry(data.Default.abilityName);
for (Entry<String, AbilityModifier> entry : data.Default.modifiers.entrySet()) {
AbilityModifier modifier = entry.getValue();
// Stare.
if (modifier.onAdded != null) {
for (AbilityModifierAction action : modifier.onAdded) {
if (action.$type.contains("HealHP")) {
action.type = AbilityModifierActionType.HealHP;
modifierEntry.getOnAdded().add(action);
}
}
}
if (modifier.onThinkInterval != null) {
for (AbilityModifierAction action : modifier.onThinkInterval) {
if (action.$type.contains("HealHP")) {
action.type = AbilityModifierActionType.HealHP;
modifierEntry.getOnThinkInterval().add(action);
}
}
}
if (modifier.onRemoved != null) {
for (AbilityModifierAction action : modifier.onRemoved) {
if (action.$type.contains("HealHP")) {
action.type = AbilityModifierActionType.HealHP;
modifierEntry.getOnRemoved().add(action);
}
}
}
}
GameData.getAbilityModifiers().put(modifierEntry.getName(), modifierEntry);
}
}
}
2022-04-25 16:50:58 +08:00
private static void loadSpawnData() {
List<SpawnGroupEntry> spawnEntryList = null;
// Read from cached file if exists
try(InputStream spawnDataEntries = DataLoader.load("Spawns.json")) {
spawnEntryList = Grasscutter.getGsonFactory().fromJson(new InputStreamReader(spawnDataEntries), TypeToken.getParameterized(Collection.class, SpawnGroupEntry.class).getType());
} catch (Exception ignored) {}
2022-04-25 16:50:58 +08:00
if (spawnEntryList == null || spawnEntryList.isEmpty()) {
Grasscutter.getLogger().error("No spawn data loaded!");
return;
}
for (SpawnGroupEntry entry : spawnEntryList) {
entry.getSpawns().forEach(s -> s.setGroup(entry));
GameDepot.getSpawnListById(entry.getSceneId()).insert(entry, entry.getPos().getX(), entry.getPos().getZ());
2022-04-25 16:50:58 +08:00
}
}
2022-04-17 20:43:07 +08:00
private static void loadOpenConfig() {
// Read from cached file if exists
List<OpenConfigEntry> list = null;
try(InputStream openConfigCache = DataLoader.load("OpenConfig.json", false)) {
list = Grasscutter.getGsonFactory().fromJson(new InputStreamReader(openConfigCache), TypeToken.getParameterized(Collection.class, SpawnGroupEntry.class).getType());
} catch (Exception ignored) {}
if (list == null) {
2022-04-17 20:43:07 +08:00
Map<String, OpenConfigEntry> map = new TreeMap<>();
java.lang.reflect.Type type = new TypeToken<Map<String, OpenConfigData[]>>() {}.getType();
2022-04-20 00:21:14 +08:00
String[] folderNames = {"BinOutput/Talent/EquipTalents/", "BinOutput/Talent/AvatarTalents/"};
2022-04-17 20:43:07 +08:00
for (String name : folderNames) {
File folder = new File(Utils.toFilePath(RESOURCE(name)));
2022-04-19 11:26:34 +08:00
File[] files = folder.listFiles();
if(files == null) {
Grasscutter.getLogger().error("Error loading open config: no files found in " + folder.getAbsolutePath()); return;
}
2022-04-17 20:43:07 +08:00
2022-04-19 11:26:34 +08:00
for (File file : files) {
2022-04-17 20:43:07 +08:00
if (!file.getName().endsWith(".json")) {
continue;
}
2022-04-19 11:26:34 +08:00
Map<String, OpenConfigData[]> config;
2022-04-17 20:43:07 +08:00
try (FileReader fileReader = new FileReader(file)) {
config = Grasscutter.getGsonFactory().fromJson(fileReader, type);
} catch (Exception e) {
e.printStackTrace();
continue;
}
for (Entry<String, OpenConfigData[]> e : config.entrySet()) {
OpenConfigEntry entry = new OpenConfigEntry(e.getKey(), e.getValue());
2022-04-17 20:43:07 +08:00
map.put(entry.getName(), entry);
}
}
}
list = new ArrayList<>(map.values());
}
if (list == null || list.isEmpty()) {
Grasscutter.getLogger().error("No openconfig entries loaded!");
return;
}
for (OpenConfigEntry entry : list) {
GameData.getOpenConfigEntries().put(entry.getName(), entry);
2022-04-17 20:43:07 +08:00
}
}
2022-05-11 18:56:40 +08:00
private static void loadQuests() {
2022-05-11 19:01:38 +08:00
File folder = new File(RESOURCE("BinOutput/Quest/"));
2022-05-11 18:56:40 +08:00
if (!folder.exists()) {
return;
}
for (File file : folder.listFiles()) {
2022-05-13 18:12:25 +08:00
MainQuestData mainQuest = null;
2022-05-11 18:56:40 +08:00
try (FileReader fileReader = new FileReader(file)) {
2022-05-13 18:12:25 +08:00
mainQuest = Grasscutter.getGsonFactory().fromJson(fileReader, MainQuestData.class);
2022-05-11 18:56:40 +08:00
} catch (Exception e) {
e.printStackTrace();
continue;
}
2022-05-13 18:12:25 +08:00
GameData.getMainQuestDataMap().put(mainQuest.getId(), mainQuest);
2022-05-11 18:56:40 +08:00
}
2022-05-13 18:12:25 +08:00
Grasscutter.getLogger().info("Loaded " + GameData.getMainQuestDataMap().size() + " MainQuestDatas.");
2022-05-11 18:56:40 +08:00
}
2022-05-25 10:44:46 +08:00
@SneakyThrows
private static void loadNpcBornData(){
var folder = Files.list(Path.of(RESOURCE("BinOutput/Scene/SceneNpcBorn"))).toList();
for(var file : folder){
if(file.toFile().isDirectory()){
continue;
}
PhTree<SceneNpcBornEntry> index = new PhTree16<>(3);
var data = Grasscutter.getGsonFactory().fromJson(Files.readString(file), SceneNpcBornData.class);
if(data.getBornPosList() == null || data.getBornPosList().size() == 0){
continue;
}
data.getBornPosList().forEach(item -> index.put(item.getPos().toLongArray(), item));
data.setIndex(index);
GameData.getSceneNpcBornData().put(data.getSceneId(), data);
}
Grasscutter.getLogger().info("Loaded " + GameData.getSceneNpcBornData().size() + " SceneNpcBornDatas.");
}
2022-04-17 20:43:07 +08:00
// BinOutput configs
private static class AvatarConfig {
public ArrayList<AvatarConfigAbility> abilities;
private static class AvatarConfigAbility {
public String abilityName;
public String toString() {
return abilityName;
}
}
}
private static class OpenConfig {
public OpenConfigData[] data;
}
public static class OpenConfigData {
2022-04-17 20:43:07 +08:00
public String $type;
public String abilityName;
public int talentIndex;
public int skillID;
public int pointDelta;
2022-04-17 20:43:07 +08:00
}
}