diff --git a/src/main/java/emu/grasscutter/GameConstants.java b/src/main/java/emu/grasscutter/GameConstants.java index b07602429..8923b3380 100644 --- a/src/main/java/emu/grasscutter/GameConstants.java +++ b/src/main/java/emu/grasscutter/GameConstants.java @@ -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"; diff --git a/src/main/java/emu/grasscutter/Grasscutter.java b/src/main/java/emu/grasscutter/Grasscutter.java index 71ab86178..d10bcbda6 100644 --- a/src/main/java/emu/grasscutter/Grasscutter.java +++ b/src/main/java/emu/grasscutter/Grasscutter.java @@ -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. */ diff --git a/src/main/java/emu/grasscutter/command/CommandMap.java b/src/main/java/emu/grasscutter/command/CommandMap.java index 2d154f4c8..4a01737d9 100644 --- a/src/main/java/emu/grasscutter/command/CommandMap.java +++ b/src/main/java/emu/grasscutter/command/CommandMap.java @@ -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; diff --git a/src/main/java/emu/grasscutter/config/ConfigContainer.java b/src/main/java/emu/grasscutter/config/ConfigContainer.java index c913f81ed..3125796ee 100644 --- a/src/main/java/emu/grasscutter/config/ConfigContainer.java +++ b/src/main/java/emu/grasscutter/config/ConfigContainer.java @@ -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; diff --git a/src/main/java/emu/grasscutter/data/DataLoader.java b/src/main/java/emu/grasscutter/data/DataLoader.java index 6f3d68e5d..eefbd98a0 100644 --- a/src/main/java/emu/grasscutter/data/DataLoader.java +++ b/src/main/java/emu/grasscutter/data/DataLoader.java @@ -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 List loadTableToList(String resourcePath, Class 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); diff --git a/src/main/java/emu/grasscutter/data/ResourceLoader.java b/src/main/java/emu/grasscutter/data/ResourceLoader.java index aa989a7ea..2888f8349 100644 --- a/src/main/java/emu/grasscutter/data/ResourceLoader.java +++ b/src/main/java/emu/grasscutter/data/ResourceLoader.java @@ -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> getResourceDefClasses() { - Reflections reflections = new Reflections(ResourceLoader.class.getPackage().getName()); - Set classes = reflections.getSubTypesOf(GameResource.class); + Set classes = Grasscutter.reflector.getSubTypesOf(GameResource.class); List> 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>> getResourceDefClassesPrioritySets() { - val reflections = new Reflections(ResourceLoader.class.getPackage().getName()); - val classes = reflections.getSubTypesOf(GameResource.class); + private static List>> getResourceDefClassesPrioritySets() { + val classes = Grasscutter.reflector.getSubTypesOf(GameResource.class); val priorities = ResourceType.LoadPriority.getInOrder(); Grasscutter.getLogger().debug("Priorities are " + priorities); val map = new LinkedHashMap>>(priorities.size()); diff --git a/src/main/java/emu/grasscutter/database/DatabaseManager.java b/src/main/java/emu/grasscutter/database/DatabaseManager.java index 5c23645c6..f3a2009f9 100644 --- a/src/main/java/emu/grasscutter/database/DatabaseManager.java +++ b/src/main/java/emu/grasscutter/database/DatabaseManager.java @@ -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 -> { diff --git a/src/main/java/emu/grasscutter/game/ability/AbilityManager.java b/src/main/java/emu/grasscutter/game/ability/AbilityManager.java index 7376592c8..311d56dd7 100644 --- a/src/main/java/emu/grasscutter/game/ability/AbilityManager.java +++ b/src/main/java/emu/grasscutter/game/ability/AbilityManager.java @@ -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 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 } } diff --git a/src/main/java/emu/grasscutter/game/activity/ActivityManager.java b/src/main/java/emu/grasscutter/game/activity/ActivityManager.java index 5a19f3036..20003cc7d 100644 --- a/src/main/java/emu/grasscutter/game/activity/ActivityManager.java +++ b/src/main/java/emu/grasscutter/game/activity/ActivityManager.java @@ -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 activityConfigItemMap; @@ -29,16 +30,14 @@ public class ActivityManager extends BasePlayerManager { // scan activity type handler & watcher type var activityHandlerTypeMap = new HashMap>(); var activityWatcherTypeMap = new HashMap>(); - 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 -> { diff --git a/src/main/java/emu/grasscutter/game/dungeons/DungeonSystem.java b/src/main/java/emu/grasscutter/game/dungeons/DungeonSystem.java index b161eee48..f5427da76 100644 --- a/src/main/java/emu/grasscutter/game/dungeons/DungeonSystem.java +++ b/src/main/java/emu/grasscutter/game/dungeons/DungeonSystem.java @@ -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 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 void registerHandlers(Int2ObjectMap map, String packageName, Class clazz) { - Reflections reflections = new Reflections(packageName); - var handlerClasses = reflections.getSubTypesOf(clazz); - + public void registerHandlers(Int2ObjectMap map, Class clazz) { + var handlerClasses = Grasscutter.reflector.getSubTypesOf(clazz); for (var obj : handlerClasses) { this.registerHandler(map, obj); } diff --git a/src/main/java/emu/grasscutter/game/gacha/GachaSystem.java b/src/main/java/emu/grasscutter/game/gacha/GachaSystem.java index a8906a694..e49e862aa 100644 --- a/src/main/java/emu/grasscutter/game/gacha/GachaSystem.java +++ b/src/main/java/emu/grasscutter/game/gacha/GachaSystem.java @@ -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 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() diff --git a/src/main/java/emu/grasscutter/game/quest/QuestSystem.java b/src/main/java/emu/grasscutter/game/quest/QuestSystem.java index 9ed8a7ca4..48093add4 100644 --- a/src/main/java/emu/grasscutter/game/quest/QuestSystem.java +++ b/src/main/java/emu/grasscutter/game/quest/QuestSystem.java @@ -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 condHandlers; private final Int2ObjectMap contHandlers; private final Int2ObjectMap 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 void registerHandlers(Int2ObjectMap map, String packageName, Class clazz) { - Reflections reflections = new Reflections(packageName); - var handlerClasses = reflections.getSubTypesOf(clazz); - + public void registerHandlers(Int2ObjectMap map, Class 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 void registerHandler(Int2ObjectMap map, Class 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); } } diff --git a/src/main/java/emu/grasscutter/game/talk/TalkSystem.java b/src/main/java/emu/grasscutter/game/talk/TalkSystem.java index 46098fadc..0582a9e9f 100644 --- a/src/main/java/emu/grasscutter/game/talk/TalkSystem.java +++ b/src/main/java/emu/grasscutter/game/talk/TalkSystem.java @@ -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 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 void registerHandlers(Int2ObjectMap map, String packageName, Class clazz) { - var reflections = new Reflections(packageName); - var handlerClasses = reflections.getSubTypesOf(clazz); - + public void registerHandlers(Int2ObjectMap map, Class clazz) { + var handlerClasses = Grasscutter.reflector.getSubTypesOf(clazz); for (var obj : handlerClasses) { this.registerTalkHandler(map, obj); } diff --git a/src/main/java/emu/grasscutter/plugin/PluginManager.java b/src/main/java/emu/grasscutter/plugin/PluginManager.java index c9e7fc510..886875d60 100644 --- a/src/main/java/emu/grasscutter/plugin/PluginManager.java +++ b/src/main/java/emu/grasscutter/plugin/PluginManager.java @@ -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 plugins = new LinkedHashMap<>(); diff --git a/src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java b/src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java index f0399dc86..6ffafffb8 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java +++ b/src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java @@ -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 handlers; public GameServerPacketHandler(Class handlerClass) { @@ -23,26 +20,22 @@ public class GameServerPacketHandler { public void registerPacketHandler(Class 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 handlerClass) { - Reflections reflections = new Reflections("emu.grasscutter.server.packet"); - Set handlerClasses = reflections.getSubTypesOf(handlerClass); - - for (Object obj : handlerClasses) { - this.registerPacketHandler((Class) obj); + var handlerClasses = Grasscutter.reflector.getSubTypesOf(handlerClass); + for (var obj : handlerClasses) { + this.registerPacketHandler(obj); } // Debug diff --git a/src/main/java/emu/grasscutter/server/http/documentation/DocumentationServerHandler.java b/src/main/java/emu/grasscutter/server/http/documentation/DocumentationServerHandler.java index 2dea0a3c9..e44f865fd 100644 --- a/src/main/java/emu/grasscutter/server/http/documentation/DocumentationServerHandler.java +++ b/src/main/java/emu/grasscutter/server/http/documentation/DocumentationServerHandler.java @@ -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); } diff --git a/src/main/java/emu/grasscutter/server/http/documentation/HandbookRequestHandler.java b/src/main/java/emu/grasscutter/server/http/documentation/HandbookRequestHandler.java deleted file mode 100644 index 580b741e0..000000000 --- a/src/main/java/emu/grasscutter/server/http/documentation/HandbookRequestHandler.java +++ /dev/null @@ -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 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 generateHandbookHtmls(String template) { - final int NUM_LANGUAGES = Language.TextStrings.NUM_LANGUAGES; - final List output = new ArrayList<>(NUM_LANGUAGES); - final List languages = Language.TextStrings.getLanguages(); - final List 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( - "" - + label - + "" - + languages.get(langIdx).get(descKey) - + "\n"); - }); - sbs.forEach(sb -> sb.setLength(sb.length() - 1)); // Remove trailing \n - final List cmdsTable = sbs.stream().map(StringBuilder::toString).toList(); - - // Avatars table - final Int2ObjectMap 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( - "" - + id - + "" - + name.get(langIdx) - + "\n"); - }); - sbs.forEach(sb -> sb.setLength(sb.length() - 1)); // Remove trailing \n - final List avatarsTable = sbs.stream().map(StringBuilder::toString).toList(); - - // Items table - final Int2ObjectMap 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( - "" - + id - + "" - + name.get(langIdx) - + "\n"); - }); - sbs.forEach(sb -> sb.setLength(sb.length() - 1)); // Remove trailing \n - final List itemsTable = sbs.stream().map(StringBuilder::toString).toList(); - - // Scenes table - final Int2ObjectMap 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( - "" - + id - + "" - + data.getScriptData() - + "\n"); - }); - sbs.forEach(sb -> sb.setLength(sb.length() - 1)); // Remove trailing \n - final List scenesTable = sbs.stream().map(StringBuilder::toString).toList(); - - // Monsters table - final Int2ObjectMap 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( - "" - + id - + "" - + name.get(langIdx) - + "\n"); - }); - sbs.forEach(sb -> sb.setLength(sb.length() - 1)); // Remove trailing \n - final List 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; - } -}