Events, Gradle Update, and Gacha Reload

Update gradle, Implemented live gacha reloading, and game server event buses
This commit is contained in:
Magix 2022-04-20 12:47:38 -04:00 committed by GitHub
commit f35f76f372
9 changed files with 109 additions and 17 deletions

View File

@ -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'
}

View File

@ -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

View File

@ -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";
public boolean AutomaticallyCreateAccounts = false;

View File

@ -15,6 +15,7 @@ public class Reload implements CommandHandler {
public void execute(GenshinPlayer sender, List<String> args) {
CommandHandler.sendMessage(sender, "Reloading config.");
Grasscutter.loadConfig();
Grasscutter.getGameServer().getGachaManager().load();
Grasscutter.getDispatchServer().loadQueries();
CommandHandler.sendMessage(sender, "Reload complete.");
}

View File

@ -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;
@ -8,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;
@ -21,17 +24,20 @@ 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;
public class GachaManager {
private final GameServer server;
private final Int2ObjectMap<GachaBanner> gachaBanners;
private GetGachaInfoRsp cachedProto;
WatchService watchService;
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 +46,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() {
@ -65,9 +72,16 @@ public class GachaManager {
public synchronized void load() {
try (FileReader fileReader = new FileReader(Grasscutter.getConfig().DATA_FOLDER + "Banners.json")) {
getGachaBanners().clear();
List<GachaBanner> 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
@ -266,6 +280,48 @@ 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, new WatchEvent.Kind[]{StandardWatchEventKinds.ENTRY_MODIFY}, SensitivityWatchEventModifier.HIGH);
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) {
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")) {
Grasscutter.getLogger().info("Change detected with banners.json. Reloading gacha config");
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();
}
}
}
private synchronized GetGachaInfoRsp createProto() {
GetGachaInfoRsp.Builder proto = GetGachaInfoRsp.newBuilder().setGachaRandom(12345);

View File

@ -19,6 +19,7 @@ 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 org.greenrobot.eventbus.EventBus;
public final class GameServer extends MihoyoKcpServer {
private final InetSocketAddress address;
@ -33,10 +34,18 @@ public final class GameServer extends MihoyoKcpServer {
private final MultiplayerManager multiplayerManager;
private final DungeonManager dungeonManager;
private final CommandMap commandMap;
public EventBus OnGameServerStartFinish;
public EventBus OnGameServerTick;
public EventBus OnGameServerStop;
public GameServer(InetSocketAddress address) {
super(address);
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;
this.packetHandler = new GameServerPacketHandler(PacketHandler.class);
@ -155,14 +164,20 @@ public final class GameServer extends MihoyoKcpServer {
for (GenshinPlayer player : this.getPlayers().values()) {
player.onTick();
}
OnGameServerTick.post(new GameServerTickEvent());
}
@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());
// Kick and save all players
List<GenshinPlayer> list = new ArrayList<>(this.getPlayers().size());
list.addAll(this.getPlayers().values());

View File

@ -0,0 +1,5 @@
package emu.grasscutter.server.game;
public class GameServerStartFinishEvent {
// Placeholder class for now, probably will get used later
}

View File

@ -0,0 +1,5 @@
package emu.grasscutter.server.game;
public class GameServerStopEvent {
// Placeholder class for now, probably will get used later
}

View File

@ -0,0 +1,5 @@
package emu.grasscutter.server.game;
public class GameServerTickEvent {
// Placeholder class for now, probably will get used later
}