Skip to content

Commit

Permalink
Merge pull request Grasscutters#74 from 4Benj/development
Browse files Browse the repository at this point in the history
Server run modes and multi-server dispatch support
  • Loading branch information
Melledy committed Apr 21, 2022
2 parents 9cf49c3 + ff6958e commit a05f827
Show file tree
Hide file tree
Showing 17 changed files with 331 additions and 160 deletions.
76 changes: 51 additions & 25 deletions src/main/java/emu/grasscutter/Config.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
package emu.grasscutter;

import java.util.ArrayList;

public final class Config {
public String DispatchServerIp = "0.0.0.0";
public String DispatchServerPublicIp = "127.0.0.1";
public int DispatchServerPort = 443;
public String DispatchServerKeystorePath = "./keystore.p12";
public String DispatchServerKeystorePassword = "";
public Boolean UseSSL = true;

public String GameServerName = "Test";
public String GameServerIp = "0.0.0.0";
public String GameServerPublicIp = "127.0.0.1";
public int GameServerPort = 22102;

public String DatabaseUrl = "mongodb://localhost:27017";
public String DatabaseCollection = "grasscutter";
Expand All @@ -21,26 +12,52 @@ public final class Config {
public String PACKETS_FOLDER = "./packets/";
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();
public String RunMode = "HYBRID"; // HYBRID, DISPATCH_ONLY, GAME_ONLY
public GameServerOptions GameServer = new GameServerOptions();
public DispatchServerOptions DispatchServer = new DispatchServerOptions();

public GameRates getGameRates() {
return Game;
public GameServerOptions getGameServerOptions() {
return GameServer;
}

public ServerOptions getServerOptions() {
return ServerOptions;
}
public DispatchServerOptions getDispatchOptions() { return DispatchServer; }

public static class DispatchServerOptions {
public String Ip = "0.0.0.0";
public String PublicIp = "127.0.0.1";
public int Port = 443;
public String KeystorePath = "./keystore.p12";
public String KeystorePassword = "";
public Boolean UseSSL = true;

public boolean AutomaticallyCreateAccounts = false;

public RegionInfo[] GameServers = {};

public RegionInfo[] getGameServers() {
return GameServers;
}

public static class GameRates {
public float ADVENTURE_EXP_RATE = 1.0f;
public float MORA_RATE = 1.0f;
public float DOMAIN_DROP_RATE = 1.0f;
public static class RegionInfo {
public String Name = "os_usa";
public String Title = "Test";
public String Ip = "127.0.0.1";
public int Port = 22102;
}
}

public static class GameServerOptions {
public String Name = "Test";
public String Ip = "0.0.0.0";
public String PublicIp = "127.0.0.1";
public int Port = 22102;

public String DispatchServerDatabaseUrl = "mongodb://localhost:27017";
public String DispatchServerDatabaseCollection = "grasscutter";

public boolean LOG_PACKETS = false;

public static class ServerOptions {
public int InventoryLimitWeapon = 2000;
public int InventoryLimitRelic = 2000;
public int InventoryLimitMaterial = 2000;
Expand All @@ -52,6 +69,15 @@ public static class ServerOptions {
public boolean WatchGacha = false;
public int[] WelcomeEmotes = {2007, 1002, 4010};
public String WelcomeMotd = "Welcome to Grasscutter emu";
public boolean AutomaticallyCreateAccounts = false;

public GameRates Game = new GameRates();

public GameRates getGameRates() { return Game; }

public static class GameRates {
public float ADVENTURE_EXP_RATE = 1.0f;
public float MORA_RATE = 1.0f;
public float DOMAIN_DROP_RATE = 1.0f;
}
}
}
32 changes: 26 additions & 6 deletions src/main/java/emu/grasscutter/Grasscutter.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,26 @@ public static void main(String[] args) throws Exception {
DatabaseManager.initialize();

// Start servers.
dispatchServer = new DispatchServer();
dispatchServer.start();

gameServer = new GameServer(new InetSocketAddress(getConfig().GameServerIp, getConfig().GameServerPort));
gameServer.start();
if(getConfig().RunMode.equalsIgnoreCase("HYBRID")) {
dispatchServer = new DispatchServer();
dispatchServer.start();

gameServer = new GameServer(new InetSocketAddress(getConfig().getGameServerOptions().Ip, getConfig().getGameServerOptions().Port));
gameServer.start();
} else if(getConfig().RunMode.equalsIgnoreCase("DISPATCH_ONLY")) {
dispatchServer = new DispatchServer();
dispatchServer.start();
} else if(getConfig().RunMode.equalsIgnoreCase("GAME_ONLY")) {
gameServer = new GameServer(new InetSocketAddress(getConfig().getGameServerOptions().Ip, getConfig().getGameServerOptions().Port));
gameServer.start();
} else {
getLogger().error("Invalid server run mode. " + getConfig().RunMode);
getLogger().error("Server run mode must be 'HYBRID', 'DISPATCH_ONLY', or 'GAME_ONLY'. Unable to start Grasscutter...");
getLogger().error("Shutting down...");
System.exit(1);
}



// Open console.
startConsole();
Expand Down Expand Up @@ -106,9 +121,14 @@ public static void startConsole() {
try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
while ((input = br.readLine()) != null) {
try {
if(getConfig().RunMode.equalsIgnoreCase("DISPATCH_ONLY")) {
getLogger().error("Commands are not supported in dispatch only mode");
return;
}
CommandMap.getInstance().invoke(null, input);
} catch (Exception e) {
Grasscutter.getLogger().error("Command error: " + e.getMessage());
Grasscutter.getLogger().error("Command error: ");
e.printStackTrace();
}
}
} catch (Exception e) {
Expand Down
12 changes: 6 additions & 6 deletions src/main/java/emu/grasscutter/database/DatabaseHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,36 +74,36 @@ public static Account createAccountWithPassword(String username, String password
}

public static void saveAccount(Account account) {
DatabaseManager.getDatastore().save(account);
DatabaseManager.getAccountDatastore().save(account);
}

public static Account getAccountByName(String username) {
MorphiaCursor<Account> cursor = DatabaseManager.getDatastore().createQuery(Account.class).field("username").equalIgnoreCase(username).find(FIND_ONE);
MorphiaCursor<Account> cursor = DatabaseManager.getAccountDatastore().createQuery(Account.class).field("username").equalIgnoreCase(username).find(FIND_ONE);
if (!cursor.hasNext()) return null;
return cursor.next();
}

public static Account getAccountByToken(String token) {
if (token == null) return null;
MorphiaCursor<Account> cursor = DatabaseManager.getDatastore().createQuery(Account.class).field("token").equal(token).find(FIND_ONE);
MorphiaCursor<Account> cursor = DatabaseManager.getAccountDatastore().createQuery(Account.class).field("token").equal(token).find(FIND_ONE);
if (!cursor.hasNext()) return null;
return cursor.next();
}

public static Account getAccountById(String uid) {
MorphiaCursor<Account> cursor = DatabaseManager.getDatastore().createQuery(Account.class).field("_id").equal(uid).find(FIND_ONE);
MorphiaCursor<Account> cursor = DatabaseManager.getAccountDatastore().createQuery(Account.class).field("_id").equal(uid).find(FIND_ONE);
if (!cursor.hasNext()) return null;
return cursor.next();
}

public static Account getAccountByPlayerId(int playerId) {
MorphiaCursor<Account> cursor = DatabaseManager.getDatastore().createQuery(Account.class).field("playerId").equal(playerId).find(FIND_ONE);
MorphiaCursor<Account> cursor = DatabaseManager.getAccountDatastore().createQuery(Account.class).field("playerId").equal(playerId).find(FIND_ONE);
if (!cursor.hasNext()) return null;
return cursor.next();
}

public static boolean deleteAccount(String username) {
Query<Account> q = DatabaseManager.getDatastore().createQuery(Account.class).field("username").equalIgnoreCase(username);
Query<Account> q = DatabaseManager.getAccountDatastore().createQuery(Account.class).field("username").equalIgnoreCase(username);
return DatabaseManager.getDatastore().findAndDelete(q) != null;
}

Expand Down
47 changes: 41 additions & 6 deletions src/main/java/emu/grasscutter/database/DatabaseManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@

public final class DatabaseManager {
private static MongoClient mongoClient;
private static MongoClient dispatchMongoClient;

private static Datastore datastore;
private static Datastore dispatchDatastore;

private static final Class<?>[] mappedClasses = new Class<?>[] {
DatabaseCounter.class, Account.class, GenshinPlayer.class, GenshinAvatar.class, GenshinItem.class, Friendship.class
Expand All @@ -26,14 +29,24 @@ public final class DatabaseManager {
public static MongoClient getMongoClient() {
return mongoClient;
}
public static Datastore getDatastore() {
return datastore;
}
public static MongoDatabase getDatabase() {

public static Datastore getDatastore() {
return datastore;
}

public static MongoDatabase getDatabase() {
return getDatastore().getDatabase();
}

// Yes. I very dislike this method. However, this will be good for now.
// TODO: Add dispatch routes for player account management
public static Datastore getAccountDatastore() {
if(Grasscutter.getConfig().RunMode.equalsIgnoreCase("GAME_ONLY")) {
return dispatchDatastore;
} else {
return datastore;
}
}

public static void initialize() {
// Initialize
Expand Down Expand Up @@ -67,6 +80,28 @@ public static void initialize() {
datastore.ensureIndexes();
}
}

if(Grasscutter.getConfig().RunMode.equalsIgnoreCase("GAME_ONLY")) {
dispatchMongoClient = new MongoClient(new MongoClientURI(Grasscutter.getConfig().getGameServerOptions().DispatchServerDatabaseUrl));
dispatchDatastore = morphia.createDatastore(dispatchMongoClient, Grasscutter.getConfig().getGameServerOptions().DispatchServerDatabaseCollection);

// Ensure indexes for dispatch server
try {
dispatchDatastore.ensureIndexes();
} catch (MongoCommandException e) {
Grasscutter.getLogger().info("Mongo index error: ", e);
// Duplicate index error
if (e.getCode() == 85) {
// Drop all indexes and re add them
MongoIterable<String> collections = dispatchDatastore.getDatabase().listCollectionNames();
for (String name : collections) {
dispatchDatastore.getDatabase().getCollection(name).dropIndexes();
}
// Add back indexes
dispatchDatastore.ensureIndexes();
}
}
}
}

public static synchronized int getNextId(Class<?> c) {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/emu/grasscutter/game/GenshinPlayer.java
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ private int getExpRequired(int level) {
}

private float getExpModifier() {
return Grasscutter.getConfig().getGameRates().ADVENTURE_EXP_RATE;
return Grasscutter.getConfig().getGameServerOptions().getGameRates().ADVENTURE_EXP_RATE;
}

// Affected by exp rate
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/emu/grasscutter/game/TeamInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class TeamInfo {

public TeamInfo() {
this.name = "";
this.avatars = new ArrayList<>(Grasscutter.getConfig().getServerOptions().MaxAvatarsInTeam);
this.avatars = new ArrayList<>(Grasscutter.getConfig().getGameServerOptions().MaxAvatarsInTeam);
}

public String getName() {
Expand All @@ -37,7 +37,7 @@ public boolean contains(GenshinAvatar avatar) {
}

public boolean addAvatar(GenshinAvatar avatar) {
if (size() >= Grasscutter.getConfig().getServerOptions().MaxAvatarsInTeam || contains(avatar)) {
if (size() >= Grasscutter.getConfig().getGameServerOptions().MaxAvatarsInTeam || contains(avatar)) {
return false;
}

Expand All @@ -57,7 +57,7 @@ public boolean removeAvatar(int slot) {
}

public void copyFrom(TeamInfo team) {
copyFrom(team, Grasscutter.getConfig().getServerOptions().MaxAvatarsInTeam);
copyFrom(team, Grasscutter.getConfig().getGameServerOptions().MaxAvatarsInTeam);
}

public void copyFrom(TeamInfo team, int maxTeamSize) {
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/emu/grasscutter/game/TeamManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -164,13 +164,13 @@ public boolean isSpawned() {

public int getMaxTeamSize() {
if (getPlayer().isInMultiplayer()) {
int max = Grasscutter.getConfig().getServerOptions().MaxAvatarsInTeamMultiplayer;
int max = Grasscutter.getConfig().getGameServerOptions().MaxAvatarsInTeamMultiplayer;
if (getPlayer().getWorld().getHost() == this.getPlayer()) {
return Math.max(1, (int) Math.ceil(max / (double) getWorld().getPlayerCount()));
}
return Math.max(1, (int) Math.floor(max / (double) getWorld().getPlayerCount()));
}
return Grasscutter.getConfig().getServerOptions().MaxAvatarsInTeam;
return Grasscutter.getConfig().getGameServerOptions().MaxAvatarsInTeam;
}

// Methods
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/emu/grasscutter/game/gacha/GachaBanner.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public int getEventChance() {
}

public GachaInfo toProto() {
String record = "http://" + (Grasscutter.getConfig().DispatchServerPublicIp.isEmpty() ? Grasscutter.getConfig().DispatchServerIp : Grasscutter.getConfig().DispatchServerPublicIp) + "/gacha";
String record = "http://" + (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getDispatchOptions().Ip : Grasscutter.getConfig().getDispatchOptions().PublicIp) + "/gacha";

GachaInfo.Builder info = GachaInfo.newBuilder()
.setGachaType(this.getGachaType())
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/emu/grasscutter/game/gacha/GachaManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ private synchronized void startWatcher(GameServer server) {

@Subscribe
public synchronized void watchBannerJson(GameServerTickEvent tickEvent) {
if(Grasscutter.getConfig().getServerOptions().WatchGacha) {
if(Grasscutter.getConfig().getGameServerOptions().WatchGacha) {
try {
WatchKey watchKey = watchService.take();

Expand Down
8 changes: 4 additions & 4 deletions src/main/java/emu/grasscutter/game/inventory/Inventory.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ public Inventory(GenshinPlayer player) {
this.store = new Long2ObjectOpenHashMap<>();
this.inventoryTypes = new Int2ObjectOpenHashMap<>();

this.createInventoryTab(ItemType.ITEM_WEAPON, new EquipInventoryTab(Grasscutter.getConfig().getServerOptions().InventoryLimitWeapon));
this.createInventoryTab(ItemType.ITEM_RELIQUARY, new EquipInventoryTab(Grasscutter.getConfig().getServerOptions().InventoryLimitRelic));
this.createInventoryTab(ItemType.ITEM_MATERIAL, new MaterialInventoryTab(Grasscutter.getConfig().getServerOptions().InventoryLimitMaterial));
this.createInventoryTab(ItemType.ITEM_FURNITURE, new MaterialInventoryTab(Grasscutter.getConfig().getServerOptions().InventoryLimitFurniture));
this.createInventoryTab(ItemType.ITEM_WEAPON, new EquipInventoryTab(Grasscutter.getConfig().getGameServerOptions().InventoryLimitWeapon));
this.createInventoryTab(ItemType.ITEM_RELIQUARY, new EquipInventoryTab(Grasscutter.getConfig().getGameServerOptions().InventoryLimitRelic));
this.createInventoryTab(ItemType.ITEM_MATERIAL, new MaterialInventoryTab(Grasscutter.getConfig().getGameServerOptions().InventoryLimitMaterial));
this.createInventoryTab(ItemType.ITEM_FURNITURE, new MaterialInventoryTab(Grasscutter.getConfig().getGameServerOptions().InventoryLimitFurniture));
}

public GenshinPlayer getPlayer() {
Expand Down
Loading

0 comments on commit a05f827

Please sign in to comment.