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;
import emu.grasscutter.net.proto.GetGachaInfoRspOuterClass.GetGachaInfoRsp;
import emu.grasscutter.net.proto.GetShopRspOuterClass.GetShopRsp;
public final class Dumpers {
public static void extractBanner(byte[] data) throws Exception {
GetGachaInfoRsp proto = GetGachaInfoRsp.parseFrom(data);
System.out.println(proto);
}
public static void extractShop(byte[] data) throws Exception {
GetShopRsp proto = GetShopRsp.parseFrom(data);
System.out.println(proto);
}
}
package emu.grasscutter.tools;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.Command.TargetRequirement;
import emu.grasscutter.command.CommandMap;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.ResourceLoader;
import emu.grasscutter.game.inventory.ItemType;
import emu.grasscutter.utils.JsonUtils;
import emu.grasscutter.utils.Language;
import lombok.AllArgsConstructor;
import java.io.File;
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;
import static emu.grasscutter.config.Configuration.*;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import emu.grasscutter.BuildConfig;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.Grasscutter.ServerRunMode;
import emu.grasscutter.net.packet.PacketOpcodesUtils;
import java.util.Map;
import java.util.function.Function;
import org.slf4j.LoggerFactory;
/** A parser for start-up arguments. */
public final class StartupArguments {
/* A map of parameter -> argument handler. */
private static final Map<String, Function<String, Boolean>> argumentHandlers =
Map.of(
"-dumppacketids",
parameter -> {
PacketOpcodesUtils.dumpPacketIds();
return true;
},
"-version", StartupArguments::printVersion,
"-debug", StartupArguments::enableDebug,
"-lang",
parameter -> {
Grasscutter.setPreferredLanguage(parameter);
return false;
},
"-game",
parameter -> {
Grasscutter.setRunModeOverride(ServerRunMode.GAME_ONLY);
return false;
},
"-dispatch",
parameter -> {
Grasscutter.setRunModeOverride(ServerRunMode.DISPATCH_ONLY);
return false;
},
"-test",
parameter -> {
// Disable the console.
SERVER.game.enableConsole = false;
// Disable HTTP encryption.
SERVER.http.encryption.useEncryption = false;
return false;
},
// Aliases.
"-v", StartupArguments::printVersion,
"-debugall",
parameter -> {
StartupArguments.enableDebug("all");
return false;
});
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.
*/
public static boolean parse(String[] args) {
boolean exitEarly = false;
// Parse the arguments.
for (var input : args) {
var containsParameter = input.contains("=");
var argument = containsParameter ? input.split("=")[0] : input;
var handler = argumentHandlers.get(argument.toLowerCase());
if (handler != null) {
exitEarly |= handler.apply(containsParameter ? input.split("=")[1] : null);
}
}
return exitEarly;
}
/**
* Prints the server version.
*
* @param parameter Additional parameters.
* @return True to exit early.
*/
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.
*/
private static boolean enableDebug(String parameter) {
if (parameter != null && parameter.equals("all")) {
// Override default debug configs
GAME_INFO.isShowLoopPackets = DEBUG_MODE_INFO.isShowLoopPackets;
GAME_INFO.isShowPacketPayload = DEBUG_MODE_INFO.isShowPacketPayload;
GAME_INFO.logPackets = DEBUG_MODE_INFO.logPackets;
DISPATCH_INFO.logRequests = DEBUG_MODE_INFO.logRequests;
}
// Set the main logger to debug.
Grasscutter.getLogger().setLevel(DEBUG_MODE_INFO.serverLoggerLevel);
Grasscutter.getLogger().debug("The logger is now running in debug mode.");
// Log level to other third-party services
Level loggerLevel = DEBUG_MODE_INFO.servicesLoggersLevel;
// Change loggers to debug.
((Logger) LoggerFactory.getLogger("io.javalin")).setLevel(loggerLevel);
((Logger) LoggerFactory.getLogger("org.quartz")).setLevel(loggerLevel);
((Logger) LoggerFactory.getLogger("org.reflections")).setLevel(loggerLevel);
((Logger) LoggerFactory.getLogger("org.eclipse.jetty")).setLevel(loggerLevel);
((Logger) LoggerFactory.getLogger("org.mongodb.driver")).setLevel(loggerLevel);
return false;
}
}
package emu.grasscutter.utils;
import static emu.grasscutter.config.Configuration.*;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import emu.grasscutter.BuildConfig;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.Grasscutter.ServerRunMode;
import emu.grasscutter.net.packet.PacketOpcodesUtils;
import java.util.Map;
import java.util.function.Function;
import emu.grasscutter.tools.Dumpers;
import org.slf4j.LoggerFactory;
/** A parser for start-up arguments. */
public final class StartupArguments {
/* A map of parameter -> argument handler. */
private static final Map<String, Function<String, Boolean>> argumentHandlers =
Map.of(
"-dumppacketids",
parameter -> {
PacketOpcodesUtils.dumpPacketIds();
return true;
},
"-version", StartupArguments::printVersion,
"-debug", StartupArguments::enableDebug,
"-lang",
parameter -> {
Grasscutter.setPreferredLanguage(parameter);
return false;
},
"-game",
parameter -> {
Grasscutter.setRunModeOverride(ServerRunMode.GAME_ONLY);
return false;
},
"-dispatch",
parameter -> {
Grasscutter.setRunModeOverride(ServerRunMode.DISPATCH_ONLY);
return false;
},
"-test",
parameter -> {
// Disable the console.
SERVER.game.enableConsole = false;
// Disable HTTP encryption.
SERVER.http.encryption.useEncryption = false;
return false;
},
"-dump", StartupArguments::dump,
// Aliases.
"-v", StartupArguments::printVersion,
"-debugall",
parameter -> {
StartupArguments.enableDebug("all");
return false;
});
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.
*/
public static boolean parse(String[] args) {
boolean exitEarly = false;
// Parse the arguments.
for (var input : args) {
var containsParameter = input.contains("=");
var argument = containsParameter ? input.split("=")[0] : input;
var handler = argumentHandlers.get(argument.toLowerCase());
if (handler != null) {
exitEarly |= handler.apply(containsParameter ? input.split("=")[1] : null);
}
}
return exitEarly;
}
/**
* Prints the server version.
*
* @param parameter Additional parameters.
* @return True to exit early.
*/
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.
*/
private static boolean enableDebug(String parameter) {
if (parameter != null && parameter.equals("all")) {
// Override default debug configs
GAME_INFO.isShowLoopPackets = DEBUG_MODE_INFO.isShowLoopPackets;
GAME_INFO.isShowPacketPayload = DEBUG_MODE_INFO.isShowPacketPayload;
GAME_INFO.logPackets = DEBUG_MODE_INFO.logPackets;
DISPATCH_INFO.logRequests = DEBUG_MODE_INFO.logRequests;
}
// Set the main logger to debug.
Grasscutter.getLogger().setLevel(DEBUG_MODE_INFO.serverLoggerLevel);
Grasscutter.getLogger().debug("The logger is now running in debug mode.");
// Log level to other third-party services
Level loggerLevel = DEBUG_MODE_INFO.servicesLoggersLevel;
// Change loggers to debug.
((Logger) LoggerFactory.getLogger("io.javalin")).setLevel(loggerLevel);
((Logger) LoggerFactory.getLogger("org.quartz")).setLevel(loggerLevel);
((Logger) LoggerFactory.getLogger("org.reflections")).setLevel(loggerLevel);
((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;
}
}