Reflections change & Start HTTP before game server

This commit is contained in:
KingRainbow44 2023-08-31 20:59:38 -04:00
parent 58b3570f27
commit 9295d79bc2
No known key found for this signature in database
GPG Key ID: FC2CB64B00D257BE
17 changed files with 148 additions and 379 deletions

View File

@ -1,10 +1,10 @@
package emu.grasscutter;
import emu.grasscutter.game.world.Position;
import emu.grasscutter.utils.objects.SparseSet;
import emu.grasscutter.utils.Utils;
import emu.grasscutter.utils.objects.SparseSet;
import java.util.*;
import java.util.Arrays;
public final class GameConstants {
public static String VERSION = "4.0.0";

View File

@ -1,8 +1,5 @@
package emu.grasscutter;
import static emu.grasscutter.config.Configuration.SERVER;
import static emu.grasscutter.utils.lang.Language.translate;
import ch.qos.logback.classic.*;
import emu.grasscutter.auth.*;
import emu.grasscutter.command.*;
@ -21,16 +18,20 @@ import emu.grasscutter.tools.Tools;
import emu.grasscutter.utils.*;
import emu.grasscutter.utils.lang.Language;
import io.netty.util.concurrent.FastThreadLocalThread;
import java.io.*;
import java.util.Calendar;
import java.util.concurrent.*;
import javax.annotation.Nullable;
import lombok.*;
import org.jline.reader.*;
import org.jline.terminal.*;
import org.reflections.Reflections;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.io.*;
import java.util.Calendar;
import java.util.concurrent.*;
import static emu.grasscutter.config.Configuration.SERVER;
import static emu.grasscutter.utils.lang.Language.translate;
public final class Grasscutter {
public static final File configFile = new File("./config.json");
public static final Reflections reflector = new Reflections("emu.grasscutter");
@ -108,15 +109,6 @@ public final class Grasscutter {
logger.info(translate("messages.status.game_version", GameConstants.VERSION));
logger.info(translate("messages.status.version", BuildConfig.VERSION, BuildConfig.GIT_HASH));
if (runMode != ServerRunMode.DISPATCH_ONLY) {
// Load all resources.
Grasscutter.updateDayOfWeek();
ResourceLoader.loadAll();
// Generate handbooks.
Tools.createGmHandbooks(false);
}
// Initialize database.
DatabaseManager.initialize();
@ -150,16 +142,31 @@ public final class Grasscutter {
httpServer.addRouter(HandbookHandler.class);
}
// Check if the HTTP server should start.
var started = config.server.http.startImmediately;
if (started) {
Grasscutter.getLogger().info("HTTP server is starting...");
Grasscutter.startDispatch();
Grasscutter.getLogger().info("Game server is starting...");
}
// Load resources.
if (runMode != ServerRunMode.DISPATCH_ONLY) {
// Load all resources.
Grasscutter.updateDayOfWeek();
ResourceLoader.loadAll();
// Generate handbooks.
Tools.createGmHandbooks(false);
}
// Start servers.
if (runMode == ServerRunMode.HYBRID) {
httpServer.start();
if (!started) Grasscutter.startDispatch();
gameServer.start();
} else if (runMode == ServerRunMode.DISPATCH_ONLY) {
httpServer.start();
// Start dispatch server.
dispatchServer = new DispatchServer("0.0.0.0", 1111);
dispatchServer.start();
if (!started) Grasscutter.startDispatch();
} else if (runMode == ServerRunMode.GAME_ONLY) {
gameServer.start();
} else {
@ -204,6 +211,20 @@ public final class Grasscutter {
}
}
/**
* Utility method for starting the:
* - SDK server
* - Dispatch server
*/
public static void startDispatch() throws Exception {
httpServer.start(); // Start the SDK/HTTP server.
if (Grasscutter.getRunMode() == ServerRunMode.DISPATCH_ONLY) {
dispatchServer = new DispatchServer("0.0.0.0", 1111); // Create the dispatch server.
dispatchServer.start(); // Start the dispatch server.
}
}
/*
* Methods for the language system component.
*/

View File

@ -1,15 +1,16 @@
package emu.grasscutter.command;
import static emu.grasscutter.config.Configuration.SERVER;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.event.game.ExecuteCommandEvent;
import it.unimi.dsi.fastutil.objects.*;
import java.util.*;
import org.reflections.Reflections;
import java.util.*;
import static emu.grasscutter.config.Configuration.SERVER;
@SuppressWarnings({"UnusedReturnValue", "unused"})
public final class CommandMap {
private static final int INVALID_UID = Integer.MIN_VALUE;
@ -53,7 +54,7 @@ public final class CommandMap {
* @return Instance chaining.
*/
public CommandMap registerCommand(String label, CommandHandler command) {
Grasscutter.getLogger().debug("Registered command: " + label);
Grasscutter.getLogger().trace("Registered command: " + label);
label = label.toLowerCase();
// Get command data.
@ -76,7 +77,7 @@ public final class CommandMap {
* @return Instance chaining.
*/
public CommandMap unregisterCommand(String label) {
Grasscutter.getLogger().debug("Unregistered command: " + label);
Grasscutter.getLogger().trace("Un-registered command: " + label);
CommandHandler handler = this.commands.get(label);
if (handler == null) return this;

View File

@ -31,9 +31,11 @@ public class ConfigContainer {
* on trial avatars.
* Version 11 - 'server.fastRequire' was added for disabling the new
* Lua script require system if performance is a concern.
* Version 12 - 'http.startImmediately' was added to control whether the
* HTTP server should start immediately.
*/
private static int version() {
return 11;
return 12;
}
/**
@ -43,7 +45,7 @@ public class ConfigContainer {
try { // Check if the server is using a legacy config.
var configObject = JsonUtils.loadToClass(Grasscutter.configFile.toPath(), JsonObject.class);
if (!configObject.has("version")) {
Grasscutter.getLogger().info("Updating legacy ..");
Grasscutter.getLogger().info("Updating legacy config...");
Grasscutter.saveConfig(null);
}
} catch (Exception ignored) { }
@ -142,6 +144,9 @@ public class ConfigContainer {
/* Server options. */
public static class HTTP {
/* This starts the HTTP server before the game server. */
public boolean startImmediately = false;
public String bindAddress = "0.0.0.0";
public int bindPort = 443;

View File

@ -3,19 +3,13 @@ package emu.grasscutter.data;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.server.http.handlers.GachaHandler;
import emu.grasscutter.tools.Tools;
import emu.grasscutter.utils.FileUtils;
import emu.grasscutter.utils.JsonUtils;
import emu.grasscutter.utils.TsvUtils;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import emu.grasscutter.utils.*;
import lombok.val;
import java.io.*;
import java.nio.file.*;
import java.util.*;
public class DataLoader {
/**
@ -98,7 +92,7 @@ public class DataLoader {
public static <T> List<T> loadTableToList(String resourcePath, Class<T> classType)
throws IOException {
val path = FileUtils.getDataPathTsjJsonTsv(resourcePath);
Grasscutter.getLogger().debug("Loading data table from: " + path);
Grasscutter.getLogger().trace("Loading data table from: " + path);
return switch (FileUtils.getFileExtension(path)) {
case "json" -> JsonUtils.loadToList(path, classType);
case "tsj" -> TsvUtils.loadTsjToListSetField(path, classType);

View File

@ -1,8 +1,5 @@
package emu.grasscutter.data;
import static emu.grasscutter.utils.FileUtils.*;
import static emu.grasscutter.utils.lang.Language.translate;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;
import emu.grasscutter.Grasscutter;
@ -23,6 +20,9 @@ import emu.grasscutter.scripts.*;
import emu.grasscutter.utils.*;
import it.unimi.dsi.fastutil.Pair;
import it.unimi.dsi.fastutil.ints.*;
import lombok.*;
import javax.script.*;
import java.io.*;
import java.nio.file.*;
import java.util.*;
@ -30,9 +30,9 @@ import java.util.Map.Entry;
import java.util.concurrent.*;
import java.util.regex.Pattern;
import java.util.stream.*;
import javax.script.*;
import lombok.*;
import org.reflections.Reflections;
import static emu.grasscutter.utils.FileUtils.*;
import static emu.grasscutter.utils.lang.Language.translate;
public final class ResourceLoader {
@ -41,8 +41,7 @@ public final class ResourceLoader {
// Get a list of all resource classes, sorted by loadPriority
public static List<Class<?>> getResourceDefClasses() {
Reflections reflections = new Reflections(ResourceLoader.class.getPackage().getName());
Set<?> classes = reflections.getSubTypesOf(GameResource.class);
Set<?> classes = Grasscutter.reflector.getSubTypesOf(GameResource.class);
List<Class<?>> classList = new ArrayList<>(classes.size());
classes.forEach(
@ -62,9 +61,8 @@ public final class ResourceLoader {
}
// Get a list containing sets of all resource classes, sorted by loadPriority
protected static List<Set<Class<?>>> getResourceDefClassesPrioritySets() {
val reflections = new Reflections(ResourceLoader.class.getPackage().getName());
val classes = reflections.getSubTypesOf(GameResource.class);
private static List<Set<Class<?>>> getResourceDefClassesPrioritySets() {
val classes = Grasscutter.reflector.getSubTypesOf(GameResource.class);
val priorities = ResourceType.LoadPriority.getInOrder();
Grasscutter.getLogger().debug("Priorities are " + priorities);
val map = new LinkedHashMap<ResourceType.LoadPriority, Set<Class<?>>>(priorities.size());

View File

@ -1,22 +1,16 @@
package emu.grasscutter.database;
import static emu.grasscutter.config.Configuration.DATABASE;
import com.mongodb.MongoCommandException;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.MongoIterable;
import dev.morphia.Datastore;
import dev.morphia.Morphia;
import com.mongodb.client.*;
import dev.morphia.*;
import dev.morphia.annotations.Entity;
import dev.morphia.mapping.Mapper;
import dev.morphia.mapping.MapperOptions;
import dev.morphia.mapping.*;
import dev.morphia.query.experimental.filters.Filters;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.Grasscutter.ServerRunMode;
import emu.grasscutter.game.Account;
import org.reflections.Reflections;
import static emu.grasscutter.config.Configuration.DATABASE;
public final class DatabaseManager {
private static Datastore gameDatastore;
@ -48,8 +42,8 @@ public final class DatabaseManager {
Morphia.createDatastore(gameMongoClient, DATABASE.game.collection, mapperOptions);
// Map classes.
Class<?>[] entities =
new Reflections(Grasscutter.class.getPackageName())
var entities =
Grasscutter.reflector
.getTypesAnnotatedWith(Entity.class).stream()
.filter(
cls -> {

View File

@ -20,10 +20,10 @@ import emu.grasscutter.net.proto.AbilityScalarValueEntryOuterClass.AbilityScalar
import emu.grasscutter.net.proto.ModifierActionOuterClass.ModifierAction;
import emu.grasscutter.server.event.player.PlayerUseSkillEvent;
import io.netty.util.concurrent.FastThreadLocalThread;
import lombok.Getter;
import java.util.HashMap;
import java.util.concurrent.*;
import lombok.Getter;
import org.reflections.Reflections;
public final class AbilityManager extends BasePlayerManager {
private static final HashMap<AbilityModifierAction.Type, AbilityActionHandler> actionHandlers =
@ -54,8 +54,8 @@ public final class AbilityManager extends BasePlayerManager {
}
public static void registerHandlers() {
Reflections reflections = new Reflections("emu.grasscutter.game.ability.actions");
var handlerClassesAction = reflections.getSubTypesOf(AbilityActionHandler.class);
var handlerClassesAction = Grasscutter.reflector
.getSubTypesOf(AbilityActionHandler.class);
for (var obj : handlerClassesAction) {
try {
@ -70,9 +70,8 @@ public final class AbilityManager extends BasePlayerManager {
}
}
reflections = new Reflections("emu.grasscutter.game.ability.mixins");
var handlerClassesMixin = reflections.getSubTypesOf(AbilityMixinHandler.class);
var handlerClassesMixin = Grasscutter.reflector
.getSubTypesOf(AbilityMixinHandler.class);
for (var obj : handlerClassesMixin) {
try {
if (obj.isAnnotationPresent(AbilityAction.class)) {
@ -413,11 +412,9 @@ public final class AbilityManager extends BasePlayerManager {
if (instancedAbilityData == null) {
// search on entity base id
if (entity != null) {
if ((head.getInstancedAbilityId() - 1) < entity.getInstancedAbilities().size()) {
instancedAbility = entity.getInstancedAbilities().get(head.getInstancedAbilityId() - 1);
if (instancedAbility != null) instancedAbilityData = instancedAbility.getData();
}
if ((head.getInstancedAbilityId() - 1) < entity.getInstancedAbilities().size()) {
instancedAbility = entity.getInstancedAbilities().get(head.getInstancedAbilityId() - 1);
if (instancedAbility != null) instancedAbilityData = instancedAbility.getData();
}
}
@ -581,6 +578,6 @@ public final class AbilityManager extends BasePlayerManager {
public void addAbilityToEntity(GameEntity entity, AbilityData abilityData) {
var ability = new Ability(abilityData, entity, this.player);
entity.getInstancedAbilities().add(ability); // This are in order
entity.getInstancedAbilities().add(ability); // This is in order
}
}

View File

@ -8,11 +8,12 @@ import emu.grasscutter.game.player.*;
import emu.grasscutter.game.props.*;
import emu.grasscutter.net.proto.ActivityInfoOuterClass;
import emu.grasscutter.server.packet.send.PacketActivityScheduleInfoNotify;
import lombok.Getter;
import java.util.*;
import java.util.concurrent.*;
import lombok.Getter;
import org.reflections.Reflections;
@SuppressWarnings("unchecked")
@Getter
public class ActivityManager extends BasePlayerManager {
private static final Map<Integer, ActivityConfigItem> activityConfigItemMap;
@ -29,16 +30,14 @@ public class ActivityManager extends BasePlayerManager {
// scan activity type handler & watcher type
var activityHandlerTypeMap = new HashMap<ActivityType, ConstructorAccess<?>>();
var activityWatcherTypeMap = new HashMap<WatcherTriggerType, ConstructorAccess<?>>();
var reflections = new Reflections(ActivityManager.class.getPackage().getName());
reflections
Grasscutter.reflector
.getSubTypesOf(ActivityHandler.class)
.forEach(
item -> {
var typeName = item.getAnnotation(GameActivity.class);
activityHandlerTypeMap.put(typeName.value(), ConstructorAccess.get(item));
});
reflections
Grasscutter.reflector
.getSubTypesOf(ActivityWatcher.class)
.forEach(
item -> {

View File

@ -1,49 +1,41 @@
package emu.grasscutter.game.dungeons;
import emu.grasscutter.GameConstants;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.*;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.binout.ScenePointEntry;
import emu.grasscutter.data.excels.dungeon.DungeonData;
import emu.grasscutter.data.excels.dungeon.DungeonPassConfigData;
import emu.grasscutter.data.excels.dungeon.*;
import emu.grasscutter.game.dungeons.handlers.DungeonBaseHandler;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.SceneType;
import emu.grasscutter.game.world.Position;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.server.game.BaseGameSystem;
import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.game.world.*;
import emu.grasscutter.net.packet.*;
import emu.grasscutter.server.game.*;
import emu.grasscutter.server.packet.send.PacketDungeonEntryInfoRsp;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.util.List;
import it.unimi.dsi.fastutil.ints.*;
import lombok.val;
import org.reflections.Reflections;
public class DungeonSystem extends BaseGameSystem {
import java.util.List;
public final class DungeonSystem extends BaseGameSystem {
private static final BasicDungeonSettleListener basicDungeonSettleObserver =
new BasicDungeonSettleListener();
private final Int2ObjectMap<DungeonBaseHandler> passCondHandlers;
public DungeonSystem(GameServer server) {
super(server);
this.passCondHandlers = new Int2ObjectOpenHashMap<>();
registerHandlers();
this.registerHandlers();
}
public void registerHandlers() {
this.registerHandlers(
this.passCondHandlers,
"emu.grasscutter.game.dungeons.pass_condition",
DungeonBaseHandler.class);
}
public <T> void registerHandlers(Int2ObjectMap<T> map, String packageName, Class<T> clazz) {
Reflections reflections = new Reflections(packageName);
var handlerClasses = reflections.getSubTypesOf(clazz);
public <T> void registerHandlers(Int2ObjectMap<T> map, Class<T> clazz) {
var handlerClasses = Grasscutter.reflector.getSubTypesOf(clazz);
for (var obj : handlerClasses) {
this.registerHandler(map, obj);
}

View File

@ -1,7 +1,5 @@
package emu.grasscutter.game.gacha;
import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
import com.sun.nio.file.SensitivityWatchEventModifier;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.*;
@ -23,10 +21,13 @@ import emu.grasscutter.server.game.*;
import emu.grasscutter.server.packet.send.PacketDoGachaRsp;
import emu.grasscutter.utils.*;
import it.unimi.dsi.fastutil.ints.*;
import org.greenrobot.eventbus.Subscribe;
import java.nio.file.*;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import org.greenrobot.eventbus.Subscribe;
import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
public class GachaSystem extends BaseGameSystem {
private static final int starglitterId = 221;
@ -58,9 +59,9 @@ public class GachaSystem extends BaseGameSystem {
int autoScheduleId = 1000;
int autoSortId = 9000;
try {
List<GachaBanner> banners = DataLoader.loadTableToList("Banners", GachaBanner.class);
if (banners.size() > 0) {
for (GachaBanner banner : banners) {
var banners = DataLoader.loadTableToList("Banners", GachaBanner.class);
if (!banners.isEmpty()) {
for (var banner : banners) {
banner.onLoad();
if (banner.isDeprecated()) {
Grasscutter.getLogger()

View File

@ -2,21 +2,15 @@ package emu.grasscutter.game.quest;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.excels.quest.QuestData;
import emu.grasscutter.data.excels.quest.QuestData.QuestAcceptCondition;
import emu.grasscutter.data.excels.quest.QuestData.QuestContentCondition;
import emu.grasscutter.data.excels.quest.QuestData.QuestExecParam;
import emu.grasscutter.data.excels.quest.QuestData.*;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.conditions.BaseCondition;
import emu.grasscutter.game.quest.content.BaseContent;
import emu.grasscutter.game.quest.handlers.QuestExecHandler;
import emu.grasscutter.server.game.BaseGameSystem;
import emu.grasscutter.server.game.GameServer;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.reflections.Reflections;
import emu.grasscutter.server.game.*;
import it.unimi.dsi.fastutil.ints.*;
@SuppressWarnings("unchecked")
public class QuestSystem extends BaseGameSystem {
public final class QuestSystem extends BaseGameSystem {
private final Int2ObjectMap<BaseCondition> condHandlers;
private final Int2ObjectMap<BaseContent> contHandlers;
private final Int2ObjectMap<QuestExecHandler> execHandlers;
@ -33,17 +27,15 @@ public class QuestSystem extends BaseGameSystem {
public void registerHandlers() {
this.registerHandlers(
this.condHandlers, "emu.grasscutter.game.quest.conditions", BaseCondition.class);
this.condHandlers, BaseCondition.class);
this.registerHandlers(
this.contHandlers, "emu.grasscutter.game.quest.content", BaseContent.class);
this.contHandlers, BaseContent.class);
this.registerHandlers(
this.execHandlers, "emu.grasscutter.game.quest.exec", QuestExecHandler.class);
this.execHandlers, QuestExecHandler.class);
}
public <T> void registerHandlers(Int2ObjectMap<T> map, String packageName, Class<T> clazz) {
Reflections reflections = new Reflections(packageName);
var handlerClasses = reflections.getSubTypesOf(clazz);
public <T> void registerHandlers(Int2ObjectMap<T> map, Class<T> clazz) {
var handlerClasses = Grasscutter.reflector.getSubTypesOf(clazz);
for (var obj : handlerClasses) {
this.registerHandler(map, obj);
}
@ -51,8 +43,7 @@ public class QuestSystem extends BaseGameSystem {
public <T> void registerHandler(Int2ObjectMap<T> map, Class<? extends T> handlerClass) {
try {
int value = 0;
if (handlerClass.isAnnotationPresent(QuestValueExec.class)) {
int value; if (handlerClass.isAnnotationPresent(QuestValueExec.class)) {
QuestValueExec opcode = handlerClass.getAnnotation(QuestValueExec.class);
value = opcode.value().getValue();
} else if (handlerClass.isAnnotationPresent(QuestValueContent.class)) {
@ -71,7 +62,7 @@ public class QuestSystem extends BaseGameSystem {
map.put(value, handlerClass.getDeclaredConstructor().newInstance());
} catch (Exception e) {
e.printStackTrace();
Grasscutter.getLogger().warn("Unable to register handler {}.", handlerClass.getSimpleName(), e);
}
}

View File

@ -4,11 +4,8 @@ import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.excels.TalkConfigData;
import emu.grasscutter.data.excels.TalkConfigData.TalkExecParam;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.game.BaseGameSystem;
import emu.grasscutter.server.game.GameServer;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.reflections.Reflections;
import emu.grasscutter.server.game.*;
import it.unimi.dsi.fastutil.ints.*;
public final class TalkSystem extends BaseGameSystem {
private final Int2ObjectMap<TalkExecHandler> execHandlers = new Int2ObjectOpenHashMap<>();
@ -16,21 +13,17 @@ public final class TalkSystem extends BaseGameSystem {
public TalkSystem(GameServer server) {
super(server);
this.registerHandlers(
this.execHandlers, "emu.grasscutter.game.talk.exec", TalkExecHandler.class);
this.registerHandlers(this.execHandlers, TalkExecHandler.class);
}
/**
* Registers all handlers with the required conditions.
*
* @param map The map to save handlers to.
* @param packageName The full package domain.
* @param clazz The class which handlers should derive from.
*/
public <T> void registerHandlers(Int2ObjectMap<T> map, String packageName, Class<T> clazz) {
var reflections = new Reflections(packageName);
var handlerClasses = reflections.getSubTypesOf(clazz);
public <T> void registerHandlers(Int2ObjectMap<T> map, Class<T> clazz) {
var handlerClasses = Grasscutter.reflector.getSubTypesOf(clazz);
for (var obj : handlerClasses) {
this.registerTalkHandler(map, obj);
}

View File

@ -1,17 +1,18 @@
package emu.grasscutter.plugin;
import static emu.grasscutter.utils.lang.Language.translate;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.*;
import emu.grasscutter.server.event.*;
import emu.grasscutter.utils.*;
import lombok.*;
import javax.annotation.Nullable;
import java.io.*;
import java.lang.reflect.Method;
import java.net.*;
import java.util.*;
import java.util.jar.*;
import javax.annotation.Nullable;
import lombok.*;
import static emu.grasscutter.utils.lang.Language.translate;
/** Manages the server's plugins and the event system. */
public final class PluginManager {
@ -20,7 +21,7 @@ public final class PluginManager {
* A 'breaking change' is something which changes the existing logic of the API.
*/
@SuppressWarnings("FieldCanBeLocal")
private static int API_VERSION = 2;
public static int API_VERSION = 3;
/* All loaded plugins. */
private final Map<String, Plugin> plugins = new LinkedHashMap<>();

View File

@ -1,18 +1,15 @@
package emu.grasscutter.server.game;
import static emu.grasscutter.config.Configuration.GAME_INFO;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.Grasscutter.ServerDebugMode;
import emu.grasscutter.net.packet.*;
import emu.grasscutter.server.event.game.ReceivePacketEvent;
import emu.grasscutter.server.game.GameSession.SessionState;
import it.unimi.dsi.fastutil.ints.*;
import java.util.Set;
import org.reflections.Reflections;
@SuppressWarnings("unchecked")
public class GameServerPacketHandler {
import static emu.grasscutter.config.Configuration.GAME_INFO;
public final class GameServerPacketHandler {
private final Int2ObjectMap<PacketHandler> handlers;
public GameServerPacketHandler(Class<? extends PacketHandler> handlerClass) {
@ -23,26 +20,22 @@ public class GameServerPacketHandler {
public void registerPacketHandler(Class<? extends PacketHandler> handlerClass) {
try {
Opcodes opcode = handlerClass.getAnnotation(Opcodes.class);
var opcode = handlerClass.getAnnotation(Opcodes.class);
if (opcode == null || opcode.disabled() || opcode.value() <= 0) {
return;
}
PacketHandler packetHandler = handlerClass.getDeclaredConstructor().newInstance();
var packetHandler = handlerClass.getDeclaredConstructor().newInstance();
this.handlers.put(opcode.value(), packetHandler);
} catch (Exception e) {
e.printStackTrace();
Grasscutter.getLogger().warn("Unable to register handler {}.", handlerClass.getSimpleName(), e);
}
}
public void registerHandlers(Class<? extends PacketHandler> handlerClass) {
Reflections reflections = new Reflections("emu.grasscutter.server.packet");
Set<?> handlerClasses = reflections.getSubTypesOf(handlerClass);
for (Object obj : handlerClasses) {
this.registerPacketHandler((Class<? extends PacketHandler>) obj);
var handlerClasses = Grasscutter.reflector.getSubTypesOf(handlerClass);
for (var obj : handlerClasses) {
this.registerPacketHandler(obj);
}
// Debug

View File

@ -1,6 +1,5 @@
package emu.grasscutter.server.http.documentation;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.server.http.Router;
import io.javalin.Javalin;
@ -11,13 +10,7 @@ public final class DocumentationServerHandler implements Router {
final var root = new RootRequestHandler();
final var gachaMapping = new GachaMappingRequestHandler();
// TODO: Removal
// TODO: Forward /documentation requests to https://grasscutter.io/wiki
if (Grasscutter.getRunMode() != Grasscutter.ServerRunMode.DISPATCH_ONLY) {
final var handbook = new HandbookRequestHandler();
javalin.get("/documentation/handbook", handbook::handle);
}
javalin.get("/documentation/handbook", ctx -> ctx.redirect("https://grasscutter.io/handbook"));
javalin.get("/documentation/gachamapping", gachaMapping::handle);
javalin.get("/documentation", root::handle);
}

View File

@ -1,204 +0,0 @@
package emu.grasscutter.server.http.documentation;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.CommandMap;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.data.excels.avatar.AvatarData;
import emu.grasscutter.data.excels.monster.MonsterData;
import emu.grasscutter.data.excels.scene.SceneData;
import emu.grasscutter.utils.FileUtils;
import emu.grasscutter.utils.lang.Language;
import io.javalin.http.ContentType;
import io.javalin.http.Context;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
final class HandbookRequestHandler implements DocumentationHandler {
private List<String> handbookHtmls;
public HandbookRequestHandler() {
var templatePath = FileUtils.getDataPath("documentation/handbook.html");
try {
this.handbookHtmls = generateHandbookHtmls(Files.readString(templatePath));
} catch (IOException ignored) {
Grasscutter.getLogger().warn("File does not exist: " + templatePath);
}
}
@Override
public void handle(Context ctx) {
int langIdx = 0;
String acceptLanguage = ctx.header("Accept-Language");
if (acceptLanguage != null) {
Pattern localePattern = Pattern.compile("[a-z]+-[A-Z]+");
Matcher matcher = localePattern.matcher(acceptLanguage);
if (matcher.find()) {
String lang = matcher.group(0);
langIdx = Language.TextStrings.MAP_GC_LANGUAGES.getOrDefault(lang, 0);
}
}
if (this.handbookHtmls == null) {
ctx.status(500);
} else {
if (langIdx <= this.handbookHtmls.size() - 1) {
ctx.contentType(ContentType.TEXT_HTML);
ctx.result(this.handbookHtmls.get(langIdx));
}
}
}
private List<String> generateHandbookHtmls(String template) {
final int NUM_LANGUAGES = Language.TextStrings.NUM_LANGUAGES;
final List<String> output = new ArrayList<>(NUM_LANGUAGES);
final List<Language> languages = Language.TextStrings.getLanguages();
final List<StringBuilder> sbs = new ArrayList<>(NUM_LANGUAGES);
for (int langIdx = 0; langIdx < NUM_LANGUAGES; langIdx++) sbs.add(new StringBuilder());
// Commands table
CommandMap.getInstance()
.getHandlersAsList()
.forEach(
cmd -> {
String label = cmd.getLabel();
String descKey = cmd.getDescriptionKey();
for (int langIdx = 0; langIdx < NUM_LANGUAGES; langIdx++)
sbs.get(langIdx)
.append(
"<tr><td><code>"
+ label
+ "</code></td><td>"
+ languages.get(langIdx).get(descKey)
+ "</td></tr>\n");
});
sbs.forEach(sb -> sb.setLength(sb.length() - 1)); // Remove trailing \n
final List<String> cmdsTable = sbs.stream().map(StringBuilder::toString).toList();
// Avatars table
final Int2ObjectMap<AvatarData> avatarMap = GameData.getAvatarDataMap();
sbs.forEach(sb -> sb.setLength(0));
avatarMap
.keySet()
.intStream()
.sorted()
.mapToObj(avatarMap::get)
.forEach(
data -> {
int id = data.getId();
Language.TextStrings name = Language.getTextMapKey(data.getNameTextMapHash());
for (int langIdx = 0; langIdx < NUM_LANGUAGES; langIdx++)
sbs.get(langIdx)
.append(
"<tr><td><code>"
+ id
+ "</code></td><td>"
+ name.get(langIdx)
+ "</td></tr>\n");
});
sbs.forEach(sb -> sb.setLength(sb.length() - 1)); // Remove trailing \n
final List<String> avatarsTable = sbs.stream().map(StringBuilder::toString).toList();
// Items table
final Int2ObjectMap<ItemData> itemMap = GameData.getItemDataMap();
sbs.forEach(sb -> sb.setLength(0));
itemMap
.keySet()
.intStream()
.sorted()
.mapToObj(itemMap::get)
.forEach(
data -> {
int id = data.getId();
Language.TextStrings name = Language.getTextMapKey(data.getNameTextMapHash());
for (int langIdx = 0; langIdx < NUM_LANGUAGES; langIdx++)
sbs.get(langIdx)
.append(
"<tr><td><code>"
+ id
+ "</code></td><td>"
+ name.get(langIdx)
+ "</td></tr>\n");
});
sbs.forEach(sb -> sb.setLength(sb.length() - 1)); // Remove trailing \n
final List<String> itemsTable = sbs.stream().map(StringBuilder::toString).toList();
// Scenes table
final Int2ObjectMap<SceneData> sceneMap = GameData.getSceneDataMap();
sceneMap
.keySet()
.intStream()
.sorted()
.mapToObj(sceneMap::get)
.forEach(
data -> {
int id = data.getId();
for (int langIdx = 0; langIdx < NUM_LANGUAGES; langIdx++)
sbs.get(langIdx)
.append(
"<tr><td><code>"
+ id
+ "</code></td><td>"
+ data.getScriptData()
+ "</td></tr>\n");
});
sbs.forEach(sb -> sb.setLength(sb.length() - 1)); // Remove trailing \n
final List<String> scenesTable = sbs.stream().map(StringBuilder::toString).toList();
// Monsters table
final Int2ObjectMap<MonsterData> monsterMap = GameData.getMonsterDataMap();
monsterMap
.keySet()
.intStream()
.sorted()
.mapToObj(monsterMap::get)
.forEach(
data -> {
int id = data.getId();
Language.TextStrings name = Language.getTextMapKey(data.getNameTextMapHash());
for (int langIdx = 0; langIdx < NUM_LANGUAGES; langIdx++)
sbs.get(langIdx)
.append(
"<tr><td><code>"
+ id
+ "</code></td><td>"
+ name.get(langIdx)
+ "</td></tr>\n");
});
sbs.forEach(sb -> sb.setLength(sb.length() - 1)); // Remove trailing \n
final List<String> monstersTable = sbs.stream().map(StringBuilder::toString).toList();
// Add translated title etc. to the page.
for (int langIdx = 0; langIdx < NUM_LANGUAGES; langIdx++) {
Language lang = languages.get(langIdx);
output.add(
template
.replace("{{TITLE}}", lang.get("documentation.handbook.title"))
.replace("{{TITLE_COMMANDS}}", lang.get("documentation.handbook.title_commands"))
.replace("{{TITLE_AVATARS}}", lang.get("documentation.handbook.title_avatars"))
.replace("{{TITLE_ITEMS}}", lang.get("documentation.handbook.title_items"))
.replace("{{TITLE_SCENES}}", lang.get("documentation.handbook.title_scenes"))
.replace("{{TITLE_MONSTERS}}", lang.get("documentation.handbook.title_monsters"))
.replace("{{HEADER_ID}}", lang.get("documentation.handbook.header_id"))
.replace("{{HEADER_COMMAND}}", lang.get("documentation.handbook.header_command"))
.replace(
"{{HEADER_DESCRIPTION}}", lang.get("documentation.handbook.header_description"))
.replace("{{HEADER_AVATAR}}", lang.get("documentation.handbook.header_avatar"))
.replace("{{HEADER_ITEM}}", lang.get("documentation.handbook.header_item"))
.replace("{{HEADER_SCENE}}", lang.get("documentation.handbook.header_scene"))
.replace("{{HEADER_MONSTER}}", lang.get("documentation.handbook.header_monster"))
// Commands table
.replace("{{COMMANDS_TABLE}}", cmdsTable.get(langIdx))
.replace("{{AVATARS_TABLE}}", avatarsTable.get(langIdx))
.replace("{{ITEMS_TABLE}}", itemsTable.get(langIdx))
.replace("{{SCENES_TABLE}}", scenesTable.get(langIdx))
.replace("{{MONSTERS_TABLE}}", monstersTable.get(langIdx)));
}
return output;
}
}