package emu.grasscutter.server.http.documentation; import static emu.grasscutter.Configuration.*; import static emu.grasscutter.utils.Language.translate; import com.google.gson.reflect.TypeToken; import emu.grasscutter.Grasscutter; import emu.grasscutter.command.CommandMap; import emu.grasscutter.data.GameData; import emu.grasscutter.data.def.AvatarData; import emu.grasscutter.data.def.ItemData; import emu.grasscutter.data.def.MonsterData; import emu.grasscutter.data.def.SceneData; import emu.grasscutter.utils.FileUtils; import emu.grasscutter.utils.Utils; import express.http.Request; import express.http.Response; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; final class HandbookRequestHandler implements DocumentationHandler { private final String template; private Map map; public HandbookRequestHandler() { final File templateFile = new File(Utils.toFilePath(DATA("documentation/handbook.html"))); if (templateFile.exists()) { template = new String(FileUtils.read(templateFile), StandardCharsets.UTF_8); } else { Grasscutter.getLogger().warn("File does not exist: " + templateFile); template = null; } final String textMapFile = "TextMap/TextMap" + DOCUMENT_LANGUAGE + ".json"; try (InputStreamReader fileReader = new InputStreamReader(new FileInputStream( Utils.toFilePath(RESOURCE(textMapFile))), StandardCharsets.UTF_8)) { map = Grasscutter.getGsonFactory() .fromJson(fileReader, new TypeToken>() { }.getType()); } catch (IOException e) { Grasscutter.getLogger().warn("Resource does not exist: " + textMapFile); map = new HashMap<>(); } } @Override public void handle(Request request, Response response) { if (template == null) { response.status(500); return; } final CommandMap cmdMap = new CommandMap(true); final Int2ObjectMap avatarMap = GameData.getAvatarDataMap(); final Int2ObjectMap itemMap = GameData.getItemDataMap(); final Int2ObjectMap sceneMap = GameData.getSceneDataMap(); final Int2ObjectMap monsterMap = GameData.getMonsterDataMap(); // Add translated title etc. to the page. String content = template.replace("{{TITLE}}", translate("documentation.handbook.title")) .replace("{{TITLE_COMMANDS}}", translate("documentation.handbook.title_commands")) .replace("{{TITLE_AVATARS}}", translate("documentation.handbook.title_avatars")) .replace("{{TITLE_ITEMS}}", translate("documentation.handbook.title_items")) .replace("{{TITLE_SCENES}}", translate("documentation.handbook.title_scenes")) .replace("{{TITLE_MONSTERS}}", translate("documentation.handbook.title_monsters")) .replace("{{HEADER_ID}}", translate("documentation.handbook.header_id")) .replace("{{HEADER_COMMAND}}", translate("documentation.handbook.header_command")) .replace("{{HEADER_DESCRIPTION}}", translate("documentation.handbook.header_description")) .replace("{{HEADER_AVATAR}}", translate("documentation.handbook.header_avatar")) .replace("{{HEADER_ITEM}}", translate("documentation.handbook.header_item")) .replace("{{HEADER_SCENE}}", translate("documentation.handbook.header_scene")) .replace("{{HEADER_MONSTER}}", translate("documentation.handbook.header_monster")) // Commands table .replace("{{COMMANDS_TABLE}}", cmdMap.getAnnotationsAsList() .stream() .map(cmd -> "" + cmd.label() + "" + cmd.description() + "") .collect(Collectors.joining("\n"))) // Avatars table .replace("{{AVATARS_TABLE}}", GameData.getAvatarDataMap().keySet() .intStream() .sorted() .mapToObj(avatarMap::get) .map(data -> "" + data.getId() + "" + map.get(data.getNameTextMapHash()) + "") .collect(Collectors.joining("\n"))) // Items table .replace("{{ITEMS_TABLE}}", GameData.getItemDataMap().keySet() .intStream() .sorted() .mapToObj(itemMap::get) .map(data -> "" + data.getId() + "" + map.get(data.getNameTextMapHash()) + "") .collect(Collectors.joining("\n"))) // Scenes table .replace("{{SCENES_TABLE}}", GameData.getSceneDataMap().keySet() .intStream() .sorted() .mapToObj(sceneMap::get) .map(data -> "" + data.getId() + "" + data.getScriptData() + "") .collect(Collectors.joining("\n"))) .replace("{{MONSTERS_TABLE}}", GameData.getMonsterDataMap().keySet() .intStream() .sorted() .mapToObj(monsterMap::get) .map(data -> "" + data.getId() + "" + map.get(data.getNameTextMapHash()) + "") .collect(Collectors.joining("\n"))); response.send(content); } }