Create initial handbook data dumpers

these can be accessed by using `-dump=commands,en-us` or `-dump=avatars/items,EN` (all languages supported)
This commit is contained in:
KingRainbow44 2023-04-05 22:43:19 -04:00
parent ac7b4d1238
commit 7c4186f5df
No known key found for this signature in database
GPG Key ID: FC2CB64B00D257BE
2 changed files with 379 additions and 145 deletions

View File

@ -1,16 +1,215 @@
package emu.grasscutter.tools; package emu.grasscutter.tools;
import emu.grasscutter.net.proto.GetGachaInfoRspOuterClass.GetGachaInfoRsp; import emu.grasscutter.command.Command;
import emu.grasscutter.net.proto.GetShopRspOuterClass.GetShopRsp; import emu.grasscutter.command.Command.TargetRequirement;
import emu.grasscutter.command.CommandMap;
public final class Dumpers { import emu.grasscutter.data.GameData;
public static void extractBanner(byte[] data) throws Exception { import emu.grasscutter.data.ResourceLoader;
GetGachaInfoRsp proto = GetGachaInfoRsp.parseFrom(data); import emu.grasscutter.game.inventory.ItemType;
System.out.println(proto); import emu.grasscutter.utils.JsonUtils;
} import emu.grasscutter.utils.Language;
import lombok.AllArgsConstructor;
public static void extractShop(byte[] data) throws Exception {
GetShopRsp proto = GetShopRsp.parseFrom(data); import java.io.File;
System.out.println(proto); import java.io.IOException;
} import java.nio.file.Files;
} import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public interface Dumpers {
/**
* Fetches the description of a command.
*
* @param locale The locale to use.
* @param command The command to get the description of.
* @return The description of the command.
*/
private static String commandDescription(String locale, Command command) {
try {
// Get the language by the locale.
var language = Language.getLanguage(locale);
if (language == null) throw new IllegalArgumentException("Invalid language.");
return language.get("commands." + command.label() + ".description");
} catch (IllegalArgumentException ignored) {
return command.label();
}
}
/**
* Encodes the dump into comma separated values.
*
* @param dump The dump to encode.
* @return The encoded dump.
*/
private static String miniEncode(Map<Integer, ?> dump) {
return dump.entrySet().stream()
.map(entry -> entry.getKey() + "," + entry.getValue().toString())
.collect(Collectors.joining("\n"));
}
/**
* Dumps all commands to a JSON file.
*
* @param locale The language to dump the commands in.
*/
static void dumpCommands(String locale) {
// Check that commands are registered.
var commandMap = CommandMap.getInstance();
if (commandMap == null) commandMap = new CommandMap(true);
// Convert all registered commands to an info map.
var dump = new HashMap<String, CommandInfo>();
commandMap.getAnnotationsAsList().forEach(command -> {
var description = Dumpers.commandDescription(locale, command);
var labels = new ArrayList<String>(){{
this.add(command.label());
this.addAll(List.of(command.aliases()));
}};
// Add the command info to the list.
dump.put(command.label(), new CommandInfo(
labels, description, List.of(command.usage()), List.of(
command.permission(), command.permissionTargeted()),
command.targetRequirement()));
});
try {
// Create a file for the dump.
var file = new File("commands.json");
if (file.exists() && !file.delete())
throw new RuntimeException("Failed to delete file.");
if (!file.exists() && !file.createNewFile())
throw new RuntimeException("Failed to create file.");
// Write the dump to the file.
Files.writeString(file.toPath(), JsonUtils.encode(dump));
} catch (IOException ignored) {
throw new RuntimeException("Failed to write to file.");
}
}
/**
* Dumps all avatars to a JSON file.
*
* @param locale The language to dump the avatars in.
*/
static void dumpAvatars(String locale) {
// Reload resources.
ResourceLoader.loadAll();
Language.loadTextMaps();
// Convert all known avatars to an avatar map.
var dump = new HashMap<Integer, AvatarInfo>();
GameData.getAvatarDataMap().forEach((id, avatar) -> {
var langHash = avatar.getNameTextMapHash();
dump.put(id, new AvatarInfo(
langHash == 0 ? avatar.getName() : Language.getTextMapKey(langHash).get(locale),
avatar.getQualityType().equals("QUALITY_PURPLE") ? Quality.EPIC : Quality.LEGENDARY,
avatar.getId()
));
});
try {
// Create a file for the dump.
var file = new File("avatars.json");
if (file.exists() && !file.delete())
throw new RuntimeException("Failed to delete file.");
if (!file.exists() && !file.createNewFile())
throw new RuntimeException("Failed to create file.");
// Write the dump to the file.
Files.writeString(file.toPath(), JsonUtils.encode(dump));
} catch (IOException ignored) {
throw new RuntimeException("Failed to write to file.");
}
}
/**
* Dumps all items to a JSON file.
*
* @param locale The language to dump the items in.
*/
static void dumpItems(String locale) {
// Reload resources.
ResourceLoader.loadAll();
Language.loadTextMaps();
// Convert all known items to an item map.
var dump = new HashMap<Integer, ItemData>();
GameData.getItemDataMap().forEach((id, item) -> dump.put(id, new ItemData(
item.getId(), Language.getTextMapKey(item.getNameTextMapHash()).get(locale),
Quality.from(item.getRankLevel()), item.getItemType()
)));
try {
// Create a file for the dump.
var file = new File("items.csv");
if (file.exists() && !file.delete())
throw new RuntimeException("Failed to delete file.");
if (!file.exists() && !file.createNewFile())
throw new RuntimeException("Failed to create file.");
// Write the dump to the file.
Files.writeString(file.toPath(), Dumpers.miniEncode(dump));
} catch (IOException ignored) {
throw new RuntimeException("Failed to write to file.");
}
}
@AllArgsConstructor
class CommandInfo {
public List<String> name;
public String description;
public List<String> usage;
public List<String> permission;
public TargetRequirement target;
}
@AllArgsConstructor
class AvatarInfo {
public String name;
public Quality quality;
public int id;
}
@AllArgsConstructor
class ItemData {
public int id;
public String name;
public Quality quality;
public ItemType type;
@Override
public String toString() {
return this.id + ","
+ this.name + ","
+ this.quality + ","
+ this.type;
}
}
enum Quality {
LEGENDARY, EPIC, RARE, UNCOMMON, COMMON, UNKNOWN;
/**
* Convert a rank level to a quality.
*
* @param rankLevel The rank level to convert.
* @return The quality.
*/
static Quality from(int rankLevel) {
return switch (rankLevel) {
case 0 -> UNKNOWN;
case 1 -> COMMON;
case 2 -> UNCOMMON;
case 3 -> RARE;
case 4 -> EPIC;
default -> LEGENDARY;
};
}
}
}

View File

@ -1,129 +1,164 @@
package emu.grasscutter.utils; package emu.grasscutter.utils;
import static emu.grasscutter.config.Configuration.*; import static emu.grasscutter.config.Configuration.*;
import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.Logger;
import emu.grasscutter.BuildConfig; import emu.grasscutter.BuildConfig;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.Grasscutter.ServerRunMode; import emu.grasscutter.Grasscutter.ServerRunMode;
import emu.grasscutter.net.packet.PacketOpcodesUtils; import emu.grasscutter.net.packet.PacketOpcodesUtils;
import java.util.Map; import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
import org.slf4j.LoggerFactory;
import emu.grasscutter.tools.Dumpers;
/** A parser for start-up arguments. */ import org.slf4j.LoggerFactory;
public final class StartupArguments {
/* A map of parameter -> argument handler. */ /** A parser for start-up arguments. */
private static final Map<String, Function<String, Boolean>> argumentHandlers = public final class StartupArguments {
Map.of( /* A map of parameter -> argument handler. */
"-dumppacketids", private static final Map<String, Function<String, Boolean>> argumentHandlers =
parameter -> { Map.of(
PacketOpcodesUtils.dumpPacketIds(); "-dumppacketids",
return true; parameter -> {
}, PacketOpcodesUtils.dumpPacketIds();
"-version", StartupArguments::printVersion, return true;
"-debug", StartupArguments::enableDebug, },
"-lang", "-version", StartupArguments::printVersion,
parameter -> { "-debug", StartupArguments::enableDebug,
Grasscutter.setPreferredLanguage(parameter); "-lang",
return false; parameter -> {
}, Grasscutter.setPreferredLanguage(parameter);
"-game", return false;
parameter -> { },
Grasscutter.setRunModeOverride(ServerRunMode.GAME_ONLY); "-game",
return false; parameter -> {
}, Grasscutter.setRunModeOverride(ServerRunMode.GAME_ONLY);
"-dispatch", return false;
parameter -> { },
Grasscutter.setRunModeOverride(ServerRunMode.DISPATCH_ONLY); "-dispatch",
return false; parameter -> {
}, Grasscutter.setRunModeOverride(ServerRunMode.DISPATCH_ONLY);
"-test", return false;
parameter -> { },
// Disable the console. "-test",
SERVER.game.enableConsole = false; parameter -> {
// Disable HTTP encryption. // Disable the console.
SERVER.http.encryption.useEncryption = false; SERVER.game.enableConsole = false;
return false; // Disable HTTP encryption.
}, SERVER.http.encryption.useEncryption = false;
return false;
// Aliases. },
"-v", StartupArguments::printVersion, "-dump", StartupArguments::dump,
"-debugall",
parameter -> { // Aliases.
StartupArguments.enableDebug("all"); "-v", StartupArguments::printVersion,
return false; "-debugall",
}); parameter -> {
StartupArguments.enableDebug("all");
private StartupArguments() { return false;
// This class is not meant to be instantiated. });
}
private StartupArguments() {
/** // This class is not meant to be instantiated.
* Parses the provided start-up arguments. }
*
* @param args The application start-up arguments. /**
* @return If the application should exit. * Parses the provided start-up arguments.
*/ *
public static boolean parse(String[] args) { * @param args The application start-up arguments.
boolean exitEarly = false; * @return If the application should exit.
*/
// Parse the arguments. public static boolean parse(String[] args) {
for (var input : args) { boolean exitEarly = false;
var containsParameter = input.contains("=");
// Parse the arguments.
var argument = containsParameter ? input.split("=")[0] : input; for (var input : args) {
var handler = argumentHandlers.get(argument.toLowerCase()); var containsParameter = input.contains("=");
if (handler != null) { var argument = containsParameter ? input.split("=")[0] : input;
exitEarly |= handler.apply(containsParameter ? input.split("=")[1] : null); var handler = argumentHandlers.get(argument.toLowerCase());
}
} if (handler != null) {
exitEarly |= handler.apply(containsParameter ? input.split("=")[1] : null);
return exitEarly; }
} }
/** return exitEarly;
* Prints the server version. }
*
* @param parameter Additional parameters. /**
* @return True to exit early. * Prints the server version.
*/ *
private static boolean printVersion(String parameter) { * @param parameter Additional parameters.
System.out.println("Grasscutter version: " + BuildConfig.VERSION + "-" + BuildConfig.GIT_HASH); * @return True to exit early.
return true; */
} private static boolean printVersion(String parameter) {
System.out.println("Grasscutter version: " + BuildConfig.VERSION + "-" + BuildConfig.GIT_HASH);
/** return true;
* Enables debug logging. }
*
* @param parameter Additional parameters. /**
* @return False to continue execution. * Enables debug logging.
*/ *
private static boolean enableDebug(String parameter) { * @param parameter Additional parameters.
if (parameter != null && parameter.equals("all")) { * @return False to continue execution.
// Override default debug configs */
GAME_INFO.isShowLoopPackets = DEBUG_MODE_INFO.isShowLoopPackets; private static boolean enableDebug(String parameter) {
GAME_INFO.isShowPacketPayload = DEBUG_MODE_INFO.isShowPacketPayload; if (parameter != null && parameter.equals("all")) {
GAME_INFO.logPackets = DEBUG_MODE_INFO.logPackets; // Override default debug configs
DISPATCH_INFO.logRequests = DEBUG_MODE_INFO.logRequests; GAME_INFO.isShowLoopPackets = DEBUG_MODE_INFO.isShowLoopPackets;
} GAME_INFO.isShowPacketPayload = DEBUG_MODE_INFO.isShowPacketPayload;
GAME_INFO.logPackets = DEBUG_MODE_INFO.logPackets;
// Set the main logger to debug. DISPATCH_INFO.logRequests = DEBUG_MODE_INFO.logRequests;
Grasscutter.getLogger().setLevel(DEBUG_MODE_INFO.serverLoggerLevel); }
Grasscutter.getLogger().debug("The logger is now running in debug mode.");
// Set the main logger to debug.
// Log level to other third-party services Grasscutter.getLogger().setLevel(DEBUG_MODE_INFO.serverLoggerLevel);
Level loggerLevel = DEBUG_MODE_INFO.servicesLoggersLevel; Grasscutter.getLogger().debug("The logger is now running in debug mode.");
// Change loggers to debug. // Log level to other third-party services
((Logger) LoggerFactory.getLogger("io.javalin")).setLevel(loggerLevel); Level loggerLevel = DEBUG_MODE_INFO.servicesLoggersLevel;
((Logger) LoggerFactory.getLogger("org.quartz")).setLevel(loggerLevel);
((Logger) LoggerFactory.getLogger("org.reflections")).setLevel(loggerLevel); // Change loggers to debug.
((Logger) LoggerFactory.getLogger("org.eclipse.jetty")).setLevel(loggerLevel); ((Logger) LoggerFactory.getLogger("io.javalin")).setLevel(loggerLevel);
((Logger) LoggerFactory.getLogger("org.mongodb.driver")).setLevel(loggerLevel); ((Logger) LoggerFactory.getLogger("org.quartz")).setLevel(loggerLevel);
((Logger) LoggerFactory.getLogger("org.reflections")).setLevel(loggerLevel);
return false; ((Logger) LoggerFactory.getLogger("org.eclipse.jetty")).setLevel(loggerLevel);
} ((Logger) LoggerFactory.getLogger("org.mongodb.driver")).setLevel(loggerLevel);
}
return false;
}
/**
* Dumps the specified information.
*
* @param parameter The parameter to dump.
* @return True to exit early.
*/
private static boolean dump(String parameter) {
// Parse the parameter.
if (!parameter.contains(",")) {
Grasscutter.getLogger().error("Dumper usage: -dump=<content>,<language>");
return true;
}
var split = parameter.split(",");
var content = split[0];
var language = split[1];
try {
switch (content.toLowerCase()) {
case "commands" -> Dumpers.dumpCommands(language);
case "avatars" -> Dumpers.dumpAvatars(language);
case "items" -> Dumpers.dumpItems(language);
}
Grasscutter.getLogger().info("Finished dumping.");
} catch (Exception exception) {
Grasscutter.getLogger().error("Unable to complete dump.", exception);
}
return true;
}
}