Refactor handbook generation to not use naked textmap getter

This commit is contained in:
AnimeGitB 2022-10-15 23:57:47 +10:30
parent 4a5a7bd6bd
commit f3a5bc16a8
2 changed files with 48 additions and 39 deletions

View File

@ -11,11 +11,10 @@ import java.nio.file.Path;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.*; import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import java.util.stream.LongStream;
import emu.grasscutter.GameConstants; import emu.grasscutter.GameConstants;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
@ -29,16 +28,15 @@ import emu.grasscutter.utils.Language;
import emu.grasscutter.utils.Language.TextStrings; import emu.grasscutter.utils.Language.TextStrings;
import it.unimi.dsi.fastutil.ints.Int2IntRBTreeMap; import it.unimi.dsi.fastutil.ints.Int2IntRBTreeMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap; import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap;
import it.unimi.dsi.fastutil.ints.IntList;
import lombok.val; import lombok.val;
import static emu.grasscutter.config.Configuration.*; import static emu.grasscutter.config.Configuration.*;
import static emu.grasscutter.utils.FileUtils.getResourcePath; import static emu.grasscutter.utils.FileUtils.getResourcePath;
import static emu.grasscutter.utils.Language.getTextMapKey;
public final class Tools { public final class Tools {
public static void createGmHandbooks() throws Exception { public static void createGmHandbooks() throws Exception {
val languages = Language.TextStrings.getLanguages(); val languages = Language.TextStrings.getLanguages();
val textMaps = Language.getTextMapStrings();
ResourceLoader.loadAll(); ResourceLoader.loadAll();
val mainQuestTitles = new Int2IntRBTreeMap(GameData.getMainQuestDataMap().int2ObjectEntrySet().stream().collect(Collectors.toMap(e -> (int) e.getIntKey(), e -> (int) e.getValue().getTitleTextMapHash()))); val mainQuestTitles = new Int2IntRBTreeMap(GameData.getMainQuestDataMap().int2ObjectEntrySet().stream().collect(Collectors.toMap(e -> (int) e.getIntKey(), e -> (int) e.getValue().getTitleTextMapHash())));
@ -54,24 +52,32 @@ public final class Tools {
// Create builders and helper functions // Create builders and helper functions
val handbookBuilders = IntStream.range(0, TextStrings.NUM_LANGUAGES).mapToObj(i -> new StringBuilder()).toList(); val handbookBuilders = IntStream.range(0, TextStrings.NUM_LANGUAGES).mapToObj(i -> new StringBuilder()).toList();
Consumer<String> newSection = title -> handbookBuilders.forEach(b -> b.append("\n\n// " + title + "\n")); var h = new Object() {
Consumer<String> newLine = line -> handbookBuilders.forEach(b -> b.append(line + "\n")); void newLine(String line) {
BiConsumer<String, IntList> newTranslatedLine = (template, textmapHashes) -> { handbookBuilders.forEach(b -> b.append(line + "\n"));
val textstrings = textmapHashes.intStream().mapToObj(textMaps::get).toList(); }
for (int i = 0; i < TextStrings.NUM_LANGUAGES; i++) { void newSection(String title) {
String s = template; newLine("\n\n// " + title);
for (int j = 0; j < textstrings.size(); j++) }
s = s.replace("{"+j+"}", textstrings.get(j).strings[i]); void newTranslatedLine(String template, TextStrings... textstrings) {
handbookBuilders.get(i).append(s + "\n"); for (int i = 0; i < TextStrings.NUM_LANGUAGES; i++) {
String s = template;
for (int j = 0; j < textstrings.length; j++)
s = s.replace("{"+j+"}", textstrings[j].strings[i]);
handbookBuilders.get(i).append(s + "\n");
}
}
void newTranslatedLine(String template, long... hashes) {
newTranslatedLine(template, LongStream.of(hashes).mapToObj(hash -> getTextMapKey(hash)).toArray(TextStrings[]::new));
} }
}; };
// Preamble // Preamble
newLine.accept("// Grasscutter " + GameConstants.VERSION + " GM Handbook"); h.newLine("// Grasscutter " + GameConstants.VERSION + " GM Handbook");
newLine.accept("// Created " + DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss").format(LocalDateTime.now())); h.newLine("// Created " + DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss").format(LocalDateTime.now()));
// Commands // Commands
newSection.accept("Commands"); h.newSection("Commands");
final List<CommandHandler> cmdList = CommandMap.getInstance().getHandlersAsList(); final List<CommandHandler> cmdList = CommandMap.getInstance().getHandlersAsList();
final String padCmdLabel = "%" + cmdList.stream().map(CommandHandler::getLabel).map(String::length).max(Integer::compare).get().toString() + "s : "; final String padCmdLabel = "%" + cmdList.stream().map(CommandHandler::getLabel).map(String::length).max(Integer::compare).get().toString() + "s : ";
for (CommandHandler cmd : cmdList) { for (CommandHandler cmd : cmdList) {
@ -83,51 +89,50 @@ public final class Tools {
} }
} }
// Avatars // Avatars
newSection.accept("Avatars"); h.newSection("Avatars");
val avatarPre = getPad.apply(avatarDataMap); val avatarPre = getPad.apply(avatarDataMap);
avatarDataMap.forEach((id, data) -> newTranslatedLine.accept(avatarPre.formatted(id) + "{0}", IntList.of((int) data.getNameTextMapHash()))); avatarDataMap.forEach((id, data) -> h.newTranslatedLine(avatarPre.formatted(id) + "{0}", data.getNameTextMapHash()));
// Items // Items
newSection.accept("Items"); h.newSection("Items");
val itemPre = getPad.apply(itemDataMap); val itemPre = getPad.apply(itemDataMap);
itemDataMap.forEach((id, data) -> { itemDataMap.forEach((id, data) -> {
val nameHash = (int) data.getNameTextMapHash(); val name = getTextMapKey(data.getNameTextMapHash());
switch (data.getMaterialType()) { switch (data.getMaterialType()) {
case MATERIAL_BGM: case MATERIAL_BGM:
val bgmNameHash = Optional.ofNullable(data.getItemUse()) val bgmName = Optional.ofNullable(data.getItemUse())
.map(u -> u.get(0)) .map(u -> u.get(0))
.map(u -> u.getUseParam()) .map(u -> u.getUseParam())
.filter(u -> u.length > 0) .filter(u -> u.length > 0)
.map(u -> Integer.parseInt(u[0])) .map(u -> Integer.parseInt(u[0]))
.map(bgmId -> GameData.getHomeWorldBgmDataMap().get(bgmId)) .map(bgmId -> GameData.getHomeWorldBgmDataMap().get(bgmId))
.map(bgm -> bgm.getBgmNameTextMapHash()); .map(bgm -> bgm.getBgmNameTextMapHash())
if (bgmNameHash.isPresent()) { .map(hash -> getTextMapKey(hash));
int trackNameHash = (int) ((long) bgmNameHash.get()); // Textmap hashes are u32, we index as i32 but store most of them as Long :') if (bgmName.isPresent()) {
if (textMaps.containsKey(trackNameHash)) { h.newTranslatedLine(itemPre.formatted(id) + "{0} - {1}", name, bgmName.get());
newTranslatedLine.accept(itemPre.formatted(id) + "{0} - {1}", IntList.of(nameHash, trackNameHash)); return;
return;
}
} // Fall-through } // Fall-through
default: default:
newTranslatedLine.accept(itemPre.formatted(id) + "{0}", IntList.of(nameHash)); h.newTranslatedLine(itemPre.formatted(id) + "{0}", name);
return; return;
} }
}); });
// Monsters // Monsters
newSection.accept("Monsters"); h.newSection("Monsters");
val monsterPre = getPad.apply(monsterDataMap); val monsterPre = getPad.apply(monsterDataMap);
monsterDataMap.forEach((id, data) -> newTranslatedLine.accept( monsterDataMap.forEach((id, data) -> h.newTranslatedLine(
monsterPre.formatted(id) + data.getMonsterName() + " - {0}", monsterPre.formatted(id) + data.getMonsterName() + " - {0}",
IntList.of((int) data.getNameTextMapHash()))); data.getNameTextMapHash()));
// Scenes - no translations // Scenes - no translations
newSection.accept("Scenes"); h.newSection("Scenes");
val padSceneId = getPad.apply(sceneDataMap); val padSceneId = getPad.apply(sceneDataMap);
sceneDataMap.forEach((id, data) -> newLine.accept(padSceneId.formatted(id) + data.getScriptData())); sceneDataMap.forEach((id, data) -> h.newLine(padSceneId.formatted(id) + data.getScriptData()));
// Quests // Quests
newSection.accept("Quests"); h.newSection("Quests");
val padQuestId = getPad.apply(questDataMap); val padQuestId = getPad.apply(questDataMap);
questDataMap.forEach((id, data) -> newTranslatedLine.accept( questDataMap.forEach((id, data) -> h.newTranslatedLine(
padQuestId.formatted(id) + "{0} - {1}", padQuestId.formatted(id) + "{0} - {1}",
IntList.of((int) mainQuestTitles.get(data.getMainId()), (int) data.getDescTextMapHash()))); mainQuestTitles.get(data.getMainId()),
data.getDescTextMapHash()));
// Write txt files // Write txt files
for (int i = 0; i < TextStrings.NUM_LANGUAGES; i++) { for (int i = 0; i < TextStrings.NUM_LANGUAGES; i++) {

View File

@ -378,19 +378,23 @@ public final class Language {
private static Int2ObjectMap<TextStrings> textMapStrings; private static Int2ObjectMap<TextStrings> textMapStrings;
private static final Path TEXTMAP_CACHE_PATH = Path.of(Utils.toFilePath("cache/TextMapCache.bin")); private static final Path TEXTMAP_CACHE_PATH = Path.of(Utils.toFilePath("cache/TextMapCache.bin"));
@Deprecated(forRemoval = true)
public static Int2ObjectMap<TextStrings> getTextMapStrings() { public static Int2ObjectMap<TextStrings> getTextMapStrings() {
if (textMapStrings == null) if (textMapStrings == null)
loadTextMaps(); loadTextMaps();
return textMapStrings; return textMapStrings;
} }
public static TextStrings getTextMapKey(long hash) { public static TextStrings getTextMapKey(int key) {
int key = (int) hash;
if ((textMapStrings == null) || (!scannedTextmaps && !textMapStrings.containsKey(key))) if ((textMapStrings == null) || (!scannedTextmaps && !textMapStrings.containsKey(key)))
loadTextMaps(); loadTextMaps();
return textMapStrings.get(key); return textMapStrings.get(key);
} }
public static TextStrings getTextMapKey(long hash) {
return getTextMapKey((int) hash);
}
public static void loadTextMaps() { public static void loadTextMaps() {
// Check system timestamps on cache and resources // Check system timestamps on cache and resources
try { try {