From d59799ce55b777806acc3f73f8ea4dcc84dc7201 Mon Sep 17 00:00:00 2001 From: Benjamin Elsdon Date: Wed, 20 Apr 2022 16:05:53 +0800 Subject: [PATCH 1/7] Update gradle + Work gacha reload (gs broken atm) --- build.gradle | 24 ++++++---- gradle/wrapper/gradle-wrapper.properties | 2 +- src/main/java/emu/grasscutter/Config.java | 3 +- .../grasscutter/game/gacha/GachaManager.java | 46 ++++++++++++++++++- .../grasscutter/server/game/GameServer.java | 10 ++++ .../server/game/GameServerTickEvent.java | 5 ++ 6 files changed, 76 insertions(+), 14 deletions(-) create mode 100644 src/main/java/emu/grasscutter/server/game/GameServerTickEvent.java diff --git a/build.gradle b/build.gradle index c7c9eae79..97477456a 100644 --- a/build.gradle +++ b/build.gradle @@ -23,19 +23,21 @@ repositories { } dependencies { - compile fileTree(dir: 'lib', include: '*.jar') + implementation fileTree(dir: 'lib', include: ['*.jar']) - compile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.32' - compile group: 'ch.qos.logback', name: 'logback-core', version: '1.2.6' - compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.6' - compile group: 'io.netty', name: 'netty-all', version: '4.1.69.Final' + implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.32' + implementation group: 'ch.qos.logback', name: 'logback-core', version: '1.2.6' + implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.6' + implementation group: 'io.netty', name: 'netty-all', version: '4.1.69.Final' - compile group: 'com.google.code.gson', name: 'gson', version: '2.8.8' - compile group: 'com.google.protobuf', name: 'protobuf-java', version: '3.18.1' + implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.8' + implementation group: 'com.google.protobuf', name: 'protobuf-java', version: '3.18.1' - compile group: 'org.reflections', name: 'reflections', version: '0.9.12' + implementation group: 'org.reflections', name: 'reflections', version: '0.9.12' - compile group: 'dev.morphia.morphia', name: 'core', version: '1.6.1' + implementation group: 'dev.morphia.morphia', name: 'core', version: '1.6.1' + + implementation group: 'org.greenrobot', name: 'eventbus-java', version: '3.3.1' } application { @@ -51,9 +53,11 @@ jar { jar.baseName = 'grasscutter' from { - configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } + duplicatesStrategy = DuplicatesStrategy.INCLUDE + from('src/main/java') { include '*.xml' } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f04d6a20a..aa991fcea 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/emu/grasscutter/Config.java b/src/main/java/emu/grasscutter/Config.java index 983524223..510d7c822 100644 --- a/src/main/java/emu/grasscutter/Config.java +++ b/src/main/java/emu/grasscutter/Config.java @@ -24,7 +24,7 @@ public final class Config { public String DUMPS_FOLDER = "./dumps/"; public String KEY_FOLDER = "./keys/"; public boolean LOG_PACKETS = false; - + public GameRates Game = new GameRates(); public ServerOptions ServerOptions = new ServerOptions(); @@ -51,6 +51,7 @@ public final class Config { public int MaxAvatarsInTeam = 4; public int MaxAvatarsInTeamMultiplayer = 4; public int MaxEntityLimit = 1000; // Max entity limit per world. // TODO: Enforce later. + public boolean WatchGacha = false; public int[] WelcomeEmotes = {2007, 1002, 4010}; public String WelcomeMotd = "Welcome to Grasscutter emu"; } diff --git a/src/main/java/emu/grasscutter/game/gacha/GachaManager.java b/src/main/java/emu/grasscutter/game/gacha/GachaManager.java index 692e09264..390fb5b95 100644 --- a/src/main/java/emu/grasscutter/game/gacha/GachaManager.java +++ b/src/main/java/emu/grasscutter/game/gacha/GachaManager.java @@ -1,6 +1,8 @@ package emu.grasscutter.game.gacha; +import java.io.File; import java.io.FileReader; +import java.nio.file.*; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -21,17 +23,23 @@ import emu.grasscutter.net.proto.GachaTransferItemOuterClass.GachaTransferItem; import emu.grasscutter.net.proto.GetGachaInfoRspOuterClass.GetGachaInfoRsp; import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam; import emu.grasscutter.server.game.GameServer; +import emu.grasscutter.server.game.GameServerTickEvent; import emu.grasscutter.server.packet.send.PacketDoGachaRsp; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; +import org.greenrobot.eventbus.Subscribe; + +import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; public class GachaManager { private final GameServer server; private final Int2ObjectMap gachaBanners; private GetGachaInfoRsp cachedProto; - + WatchService watchService; + WatchKey watchKey; + private int[] yellowAvatars = new int[] {1003, 1016, 1042, 1035, 1041}; private int[] yellowWeapons = new int[] {11501, 11502, 12501, 12502, 13502, 13505, 14501, 14502, 15501, 15502}; private int[] purpleAvatars = new int[] {1006, 1014, 1015, 1020, 1021, 1023, 1024, 1025, 1027, 1031, 1032, 1034, 1036, 1039, 1043, 1044, 1045, 1048, 1053, 1055, 1056, 1064}; @@ -40,11 +48,12 @@ public class GachaManager { private static int starglitterId = 221; private static int stardustId = 222; - + public GachaManager(GameServer server) { this.server = server; this.gachaBanners = new Int2ObjectOpenHashMap<>(); this.load(); + this.startWatcher(server); } public GameServer getServer() { @@ -266,6 +275,39 @@ public class GachaManager { // Packets player.sendPacket(new PacketDoGachaRsp(banner, list)); } + + private synchronized void startWatcher(GameServer server) { + if(this.watchService == null) { + try { + this.watchService = FileSystems.getDefault().newWatchService(); + Path path = new File(Grasscutter.getConfig().DATA_FOLDER).toPath(); + path.register(watchService, ENTRY_MODIFY); + watchKey = watchService.take(); + + server.OnGameServerTick.register(this); + } catch (Exception e) { + Grasscutter.getLogger().error("Unable to load the Gacha Manager Watch Service. If ServerOptions.watchGacha is true it will not auto-reload"); + e.printStackTrace(); + } + } else { + Grasscutter.getLogger().error("Cannot reinitialise watcher "); + } + } + + @Subscribe + public synchronized void watchBannerJson(GameServerTickEvent tickEvent) { + try { + for (WatchEvent event : watchKey.pollEvents()) { + final Path changed = (Path) event.context(); + if (changed.endsWith("Banners.json")) { + Grasscutter.getLogger().info("Change detected with banners.json. Reloading gacha config"); + this.load(); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } private synchronized GetGachaInfoRsp createProto() { GetGachaInfoRsp.Builder proto = GetGachaInfoRsp.newBuilder().setGachaRandom(12345); diff --git a/src/main/java/emu/grasscutter/server/game/GameServer.java b/src/main/java/emu/grasscutter/server/game/GameServer.java index b42ced55c..7304abc9d 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServer.java +++ b/src/main/java/emu/grasscutter/server/game/GameServer.java @@ -3,6 +3,7 @@ package emu.grasscutter.server.game; import java.net.InetSocketAddress; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; import emu.grasscutter.GenshinConstants; import emu.grasscutter.Grasscutter; @@ -19,6 +20,8 @@ import emu.grasscutter.game.shop.ShopManager; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail; import emu.grasscutter.netty.MihoyoKcpServer; +import jdk.internal.event.Event; +import org.greenrobot.eventbus.EventBus; public final class GameServer extends MihoyoKcpServer { private final InetSocketAddress address; @@ -33,9 +36,14 @@ public final class GameServer extends MihoyoKcpServer { private final MultiplayerManager multiplayerManager; private final DungeonManager dungeonManager; private final CommandMap commandMap; + + public EventBus OnGameServerTick; + public EventBus OnGameServerStop; // TODO public GameServer(InetSocketAddress address) { super(address); + + OnGameServerTick = EventBus.builder().throwSubscriberException(true).build(); this.setServerInitializer(new GameServerInitializer(this)); this.address = address; @@ -155,6 +163,8 @@ public final class GameServer extends MihoyoKcpServer { for (GenshinPlayer player : this.getPlayers().values()) { player.onTick(); } + + OnGameServerTick.post(new GameServerTickEvent()); } @Override diff --git a/src/main/java/emu/grasscutter/server/game/GameServerTickEvent.java b/src/main/java/emu/grasscutter/server/game/GameServerTickEvent.java new file mode 100644 index 000000000..f9069d2d9 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/game/GameServerTickEvent.java @@ -0,0 +1,5 @@ +package emu.grasscutter.server.game; + +public class GameServerTickEvent { + // Placeholder class for now, probably will get used later +} From 28a070f19abd1bc71127e0588e9446297fc4a72d Mon Sep 17 00:00:00 2001 From: Benjamin Elsdon Date: Wed, 20 Apr 2022 16:07:18 +0800 Subject: [PATCH 2/7] Forgot config check --- .../grasscutter/game/gacha/GachaManager.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/java/emu/grasscutter/game/gacha/GachaManager.java b/src/main/java/emu/grasscutter/game/gacha/GachaManager.java index 390fb5b95..d52dff8bf 100644 --- a/src/main/java/emu/grasscutter/game/gacha/GachaManager.java +++ b/src/main/java/emu/grasscutter/game/gacha/GachaManager.java @@ -296,16 +296,18 @@ public class GachaManager { @Subscribe public synchronized void watchBannerJson(GameServerTickEvent tickEvent) { - try { - for (WatchEvent event : watchKey.pollEvents()) { - final Path changed = (Path) event.context(); - if (changed.endsWith("Banners.json")) { - Grasscutter.getLogger().info("Change detected with banners.json. Reloading gacha config"); - this.load(); + if(Grasscutter.getConfig().getServerOptions().WatchGacha) { + try { + for (WatchEvent event : watchKey.pollEvents()) { + final Path changed = (Path) event.context(); + if (changed.endsWith("Banners.json")) { + Grasscutter.getLogger().info("Change detected with banners.json. Reloading gacha config"); + this.load(); + } } + } catch (Exception e) { + e.printStackTrace(); } - } catch (Exception e) { - e.printStackTrace(); } } From 26e1341ce9ba53f23ef837fda6f019ab8a16942d Mon Sep 17 00:00:00 2001 From: Benjamin Elsdon Date: Wed, 20 Apr 2022 17:14:07 +0800 Subject: [PATCH 3/7] Out of my madness and suffering I have achieved the impossible. I almost lost all hope and was about to end it all. However, it worked out in the end and now we have gacha reloading. --- .../grasscutter/game/gacha/GachaManager.java | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/main/java/emu/grasscutter/game/gacha/GachaManager.java b/src/main/java/emu/grasscutter/game/gacha/GachaManager.java index d52dff8bf..eb1cba379 100644 --- a/src/main/java/emu/grasscutter/game/gacha/GachaManager.java +++ b/src/main/java/emu/grasscutter/game/gacha/GachaManager.java @@ -10,6 +10,7 @@ import java.util.concurrent.ThreadLocalRandom; import com.google.gson.reflect.TypeToken; +import com.sun.nio.file.SensitivityWatchEventModifier; import emu.grasscutter.Grasscutter; import emu.grasscutter.data.GenshinData; import emu.grasscutter.data.def.ItemData; @@ -31,14 +32,11 @@ import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import org.greenrobot.eventbus.Subscribe; -import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; - public class GachaManager { private final GameServer server; private final Int2ObjectMap gachaBanners; private GetGachaInfoRsp cachedProto; WatchService watchService; - WatchKey watchKey; private int[] yellowAvatars = new int[] {1003, 1016, 1042, 1035, 1041}; private int[] yellowWeapons = new int[] {11501, 11502, 12501, 12502, 13502, 13505, 14501, 14502, 15501, 15502}; @@ -74,9 +72,16 @@ public class GachaManager { public synchronized void load() { try (FileReader fileReader = new FileReader(Grasscutter.getConfig().DATA_FOLDER + "Banners.json")) { + getGachaBanners().clear(); List banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, GachaBanner.class).getType()); - for (GachaBanner banner : banners) { - getGachaBanners().put(banner.getGachaType(), banner); + if(banners.size() > 0) { + for (GachaBanner banner : banners) { + getGachaBanners().put(banner.getGachaType(), banner); + } + Grasscutter.getLogger().info("Banners successfully loaded."); + this.cachedProto = createProto(); + } else { + Grasscutter.getLogger().error("Unable to load banners. Banners size is 0."); } } catch (Exception e) { // TODO Auto-generated catch block @@ -281,8 +286,7 @@ public class GachaManager { try { this.watchService = FileSystems.getDefault().newWatchService(); Path path = new File(Grasscutter.getConfig().DATA_FOLDER).toPath(); - path.register(watchService, ENTRY_MODIFY); - watchKey = watchService.take(); + path.register(watchService, new WatchEvent.Kind[]{StandardWatchEventKinds.ENTRY_MODIFY}, SensitivityWatchEventModifier.HIGH); server.OnGameServerTick.register(this); } catch (Exception e) { @@ -298,6 +302,8 @@ public class GachaManager { public synchronized void watchBannerJson(GameServerTickEvent tickEvent) { if(Grasscutter.getConfig().getServerOptions().WatchGacha) { try { + WatchKey watchKey = watchService.take(); + for (WatchEvent event : watchKey.pollEvents()) { final Path changed = (Path) event.context(); if (changed.endsWith("Banners.json")) { @@ -305,6 +311,12 @@ public class GachaManager { this.load(); } } + + boolean valid = watchKey.reset(); + if (!valid) { + Grasscutter.getLogger().error("Unable to reset Gacha Manager Watch Key. Auto-reload of banners.json will no longer work."); + return; + } } catch (Exception e) { e.printStackTrace(); } From 1c2560736f533e8cffb025d7767236f8ba6d325f Mon Sep 17 00:00:00 2001 From: Benjamin Elsdon Date: Wed, 20 Apr 2022 17:17:59 +0800 Subject: [PATCH 4/7] /reload now reloads the gacha as well --- src/main/java/emu/grasscutter/commands/ServerCommands.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/emu/grasscutter/commands/ServerCommands.java b/src/main/java/emu/grasscutter/commands/ServerCommands.java index edeac6ca3..d721edd07 100644 --- a/src/main/java/emu/grasscutter/commands/ServerCommands.java +++ b/src/main/java/emu/grasscutter/commands/ServerCommands.java @@ -18,6 +18,7 @@ public final class ServerCommands { public void execute(List args) { Grasscutter.getLogger().info("Reloading config."); Grasscutter.loadConfig(); + Grasscutter.getGameServer().getGachaManager().load(); Grasscutter.getDispatchServer().loadQueries(); Grasscutter.getLogger().info("Reload complete."); } From b7f5cc9748a205409fd57faf3ab700ce15b94764 Mon Sep 17 00:00:00 2001 From: Benjamin Elsdon Date: Wed, 20 Apr 2022 17:31:41 +0800 Subject: [PATCH 5/7] Added OnGameServerStop EventBus There is a temporary warning message telling users to ignore the 'No subscribers registered' error. This can be removed once things actually subscribe to it --- .../java/emu/grasscutter/server/game/GameServer.java | 10 ++++++++-- .../grasscutter/server/game/GameServerStopEvent.java | 5 +++++ 2 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 src/main/java/emu/grasscutter/server/game/GameServerStopEvent.java diff --git a/src/main/java/emu/grasscutter/server/game/GameServer.java b/src/main/java/emu/grasscutter/server/game/GameServer.java index 7304abc9d..155e84979 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServer.java +++ b/src/main/java/emu/grasscutter/server/game/GameServer.java @@ -38,13 +38,14 @@ public final class GameServer extends MihoyoKcpServer { private final CommandMap commandMap; public EventBus OnGameServerTick; - public EventBus OnGameServerStop; // TODO + public EventBus OnGameServerStop; public GameServer(InetSocketAddress address) { super(address); OnGameServerTick = EventBus.builder().throwSubscriberException(true).build(); - + OnGameServerStop = EventBus.builder().throwSubscriberException(true).build(); + this.setServerInitializer(new GameServerInitializer(this)); this.address = address; this.packetHandler = new GameServerPacketHandler(PacketHandler.class); @@ -173,6 +174,11 @@ public final class GameServer extends MihoyoKcpServer { } public void onServerShutdown() { + OnGameServerStop.post(new GameServerStopEvent()); + Grasscutter.getLogger().info("Ignore the 'No subscribers registered' error"); + // TODO: Remove the log once things actually listen to OnGameServerStop. + // I just added it there to prevent people from flooding #support with this error + // Kick and save all players List list = new ArrayList<>(this.getPlayers().size()); list.addAll(this.getPlayers().values()); diff --git a/src/main/java/emu/grasscutter/server/game/GameServerStopEvent.java b/src/main/java/emu/grasscutter/server/game/GameServerStopEvent.java new file mode 100644 index 000000000..7a7ef40d7 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/game/GameServerStopEvent.java @@ -0,0 +1,5 @@ +package emu.grasscutter.server.game; + +public class GameServerStopEvent { + // Placeholder class for now, probably will get used later +} From e8601de5d565fbaa4cc622c94918797a89448521 Mon Sep 17 00:00:00 2001 From: Benjamin Elsdon Date: Wed, 20 Apr 2022 17:44:44 +0800 Subject: [PATCH 6/7] Added OnGameServerStartFinish EventBus Removed those temporary warning messages as I figured out how to disable them --- .../emu/grasscutter/server/game/GameServer.java | 13 ++++++------- .../server/game/GameServerStartFinishEvent.java | 5 +++++ 2 files changed, 11 insertions(+), 7 deletions(-) create mode 100644 src/main/java/emu/grasscutter/server/game/GameServerStartFinishEvent.java diff --git a/src/main/java/emu/grasscutter/server/game/GameServer.java b/src/main/java/emu/grasscutter/server/game/GameServer.java index 155e84979..282589a4b 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServer.java +++ b/src/main/java/emu/grasscutter/server/game/GameServer.java @@ -3,7 +3,6 @@ package emu.grasscutter.server.game; import java.net.InetSocketAddress; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CountDownLatch; import emu.grasscutter.GenshinConstants; import emu.grasscutter.Grasscutter; @@ -20,7 +19,6 @@ import emu.grasscutter.game.shop.ShopManager; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail; import emu.grasscutter.netty.MihoyoKcpServer; -import jdk.internal.event.Event; import org.greenrobot.eventbus.EventBus; public final class GameServer extends MihoyoKcpServer { @@ -37,14 +35,16 @@ public final class GameServer extends MihoyoKcpServer { private final DungeonManager dungeonManager; private final CommandMap commandMap; + public EventBus OnGameServerStartFinish; public EventBus OnGameServerTick; public EventBus OnGameServerStop; public GameServer(InetSocketAddress address) { super(address); - OnGameServerTick = EventBus.builder().throwSubscriberException(true).build(); - OnGameServerStop = EventBus.builder().throwSubscriberException(true).build(); + OnGameServerStartFinish = EventBus.builder().throwSubscriberException(true).logNoSubscriberMessages(false).build(); + OnGameServerTick = EventBus.builder().throwSubscriberException(true).logNoSubscriberMessages(false).build(); + OnGameServerStop = EventBus.builder().throwSubscriberException(true).logNoSubscriberMessages(false).build(); this.setServerInitializer(new GameServerInitializer(this)); this.address = address; @@ -171,13 +171,12 @@ public final class GameServer extends MihoyoKcpServer { @Override public void onStartFinish() { Grasscutter.getLogger().info("Game Server started on port " + address.getPort()); + + OnGameServerStartFinish.post(new GameServerStartFinishEvent()); } public void onServerShutdown() { OnGameServerStop.post(new GameServerStopEvent()); - Grasscutter.getLogger().info("Ignore the 'No subscribers registered' error"); - // TODO: Remove the log once things actually listen to OnGameServerStop. - // I just added it there to prevent people from flooding #support with this error // Kick and save all players List list = new ArrayList<>(this.getPlayers().size()); diff --git a/src/main/java/emu/grasscutter/server/game/GameServerStartFinishEvent.java b/src/main/java/emu/grasscutter/server/game/GameServerStartFinishEvent.java new file mode 100644 index 000000000..5e960ad1d --- /dev/null +++ b/src/main/java/emu/grasscutter/server/game/GameServerStartFinishEvent.java @@ -0,0 +1,5 @@ +package emu.grasscutter.server.game; + +public class GameServerStartFinishEvent { + // Placeholder class for now, probably will get used later +} From 482c174ee7f49c2dc13bb80bcd38fd0ab8a57aaf Mon Sep 17 00:00:00 2001 From: Benjamin Elsdon Date: Thu, 21 Apr 2022 00:28:14 +0800 Subject: [PATCH 7/7] Reload command now reloads gacha config (again) --- src/main/java/emu/grasscutter/command/commands/Reload.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/emu/grasscutter/command/commands/Reload.java b/src/main/java/emu/grasscutter/command/commands/Reload.java index 21bf63e52..cf2bae2b1 100644 --- a/src/main/java/emu/grasscutter/command/commands/Reload.java +++ b/src/main/java/emu/grasscutter/command/commands/Reload.java @@ -15,6 +15,7 @@ public class Reload implements CommandHandler { public void execute(GenshinPlayer sender, List args) { CommandHandler.sendMessage(sender, "Reloading config."); Grasscutter.loadConfig(); + Grasscutter.getGameServer().getGachaManager().load(); Grasscutter.getDispatchServer().loadQueries(); CommandHandler.sendMessage(sender, "Reload complete."); }