From 3f1128356ee9e167739e7320f08fe11170546a0d Mon Sep 17 00:00:00 2001 From: ayy lmao Date: Wed, 11 May 2022 17:35:52 +0300 Subject: [PATCH 01/14] Fix InRouting on dispatch server --- .../java/emu/grasscutter/server/dispatch/DispatchServer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java index df1ce666d..4e09f8881 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -117,7 +117,7 @@ public final class DispatchServer { .setTitle(DISPATCH_INFO.defaultName) .setType("DEV_PUBLIC") .setDispatchUrl( - "http" + (DISPATCH_ENCRYPTION.useEncryption ? "s" : "") + "://" + "http" + (DISPATCH_ENCRYPTION.useInRouting ? "s" : "") + "://" + lr(DISPATCH_INFO.accessAddress, DISPATCH_INFO.bindAddress) + ":" + lr(DISPATCH_INFO.accessPort, DISPATCH_INFO.bindPort) + "/query_cur_region/" + defaultServerName) @@ -150,7 +150,7 @@ public final class DispatchServer { .setTitle(regionInfo.Title) .setType("DEV_PUBLIC") .setDispatchUrl( - "http" + (DISPATCH_ENCRYPTION.useEncryption ? "s" : "") + "://" + "http" + (DISPATCH_ENCRYPTION.useInRouting ? "s" : "") + "://" + lr(DISPATCH_INFO.accessAddress, DISPATCH_INFO.bindAddress) + ":" + lr(DISPATCH_INFO.accessPort, DISPATCH_INFO.bindPort) + "/query_cur_region/" + regionInfo.Name) From 89bd4b9aeb8bf5a230605becc673a084706a4218 Mon Sep 17 00:00:00 2001 From: KingRainbow44 Date: Wed, 11 May 2022 11:38:30 -0400 Subject: [PATCH 02/14] Refactor config database settings --- .../grasscutter/database/DatabaseHelper.java | 68 +++++++++---------- .../grasscutter/database/DatabaseManager.java | 34 +++++----- .../emu/grasscutter/plugin/PluginManager.java | 3 + .../grasscutter/utils/ConfigContainer.java | 29 ++++---- 4 files changed, 71 insertions(+), 63 deletions(-) diff --git a/src/main/java/emu/grasscutter/database/DatabaseHelper.java b/src/main/java/emu/grasscutter/database/DatabaseHelper.java index 8f1de0bb9..5b622aac8 100644 --- a/src/main/java/emu/grasscutter/database/DatabaseHelper.java +++ b/src/main/java/emu/grasscutter/database/DatabaseHelper.java @@ -77,25 +77,25 @@ public final class DatabaseHelper { } public static Account getAccountByName(String username) { - return DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("username", username)).first(); + return DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("username", username)).first(); } public static Account getAccountByToken(String token) { if(token == null) return null; - return DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("token", token)).first(); + return DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("token", token)).first(); } public static Account getAccountBySessionKey(String sessionKey) { if(sessionKey == null) return null; - return DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("sessionKey", sessionKey)).first(); + return DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("sessionKey", sessionKey)).first(); } public static Account getAccountById(String uid) { - return DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("_id", uid)).first(); + return DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("_id", uid)).first(); } public static Account getAccountByPlayerId(int playerId) { - return DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("playerId", playerId)).first(); + return DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("playerId", playerId)).first(); } public static void deleteAccount(Account target) { @@ -104,37 +104,37 @@ public final class DatabaseHelper { // database in an inconsistent state, but unfortunately Mongo only supports that when we have a replica set ... // Delete Mail.class data - DatabaseManager.getDatabase().getCollection("mail").deleteMany(eq("ownerUid", target.getPlayerUid())); + DatabaseManager.getGameDatabase().getCollection("mail").deleteMany(eq("ownerUid", target.getPlayerUid())); // Delete Avatar.class data - DatabaseManager.getDatabase().getCollection("avatars").deleteMany(eq("ownerId", target.getPlayerUid())); + DatabaseManager.getGameDatabase().getCollection("avatars").deleteMany(eq("ownerId", target.getPlayerUid())); // Delete GachaRecord.class data - DatabaseManager.getDatabase().getCollection("gachas").deleteMany(eq("ownerId", target.getPlayerUid())); + DatabaseManager.getGameDatabase().getCollection("gachas").deleteMany(eq("ownerId", target.getPlayerUid())); // Delete GameItem.class data - DatabaseManager.getDatabase().getCollection("items").deleteMany(eq("ownerId", target.getPlayerUid())); + DatabaseManager.getGameDatabase().getCollection("items").deleteMany(eq("ownerId", target.getPlayerUid())); // Delete friendships. // Here, we need to make sure to not only delete the deleted account's friendships, // but also all friendship entries for that account's friends. - DatabaseManager.getDatabase().getCollection("friendships").deleteMany(eq("ownerId", target.getPlayerUid())); - DatabaseManager.getDatabase().getCollection("friendships").deleteMany(eq("friendId", target.getPlayerUid())); + DatabaseManager.getGameDatabase().getCollection("friendships").deleteMany(eq("ownerId", target.getPlayerUid())); + DatabaseManager.getGameDatabase().getCollection("friendships").deleteMany(eq("friendId", target.getPlayerUid())); // Delete the player. - DatabaseManager.getDatastore().find(Player.class).filter(Filters.eq("id", target.getPlayerUid())).delete(); + DatabaseManager.getGameDatastore().find(Player.class).filter(Filters.eq("id", target.getPlayerUid())).delete(); // Finally, delete the account itself. - DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("id", target.getId())).delete(); + DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("id", target.getId())).delete(); } public static List getAllPlayers() { - return DatabaseManager.getDatastore().find(Player.class).stream().toList(); + return DatabaseManager.getGameDatastore().find(Player.class).stream().toList(); } public static Player getPlayerById(int id) { - return DatabaseManager.getDatastore().find(Player.class).filter(Filters.eq("_id", id)).first(); + return DatabaseManager.getGameDatastore().find(Player.class).filter(Filters.eq("_id", id)).first(); } public static boolean checkPlayerExists(int id) { - return DatabaseManager.getDatastore().find(Player.class).filter(Filters.eq("_id", id)).first() != null; + return DatabaseManager.getGameDatastore().find(Player.class).filter(Filters.eq("_id", id)).first() != null; } public static synchronized Player createPlayer(Player character, int reservedId) { @@ -151,7 +151,7 @@ public final class DatabaseHelper { character.setUid(id); } // Save to database - DatabaseManager.getDatastore().save(character); + DatabaseManager.getGameDatastore().save(character); return character; } @@ -170,48 +170,48 @@ public final class DatabaseHelper { } public static void savePlayer(Player character) { - DatabaseManager.getDatastore().save(character); + DatabaseManager.getGameDatastore().save(character); } public static void saveAvatar(Avatar avatar) { - DatabaseManager.getDatastore().save(avatar); + DatabaseManager.getGameDatastore().save(avatar); } public static List getAvatars(Player player) { - return DatabaseManager.getDatastore().find(Avatar.class).filter(Filters.eq("ownerId", player.getUid())).stream().toList(); + return DatabaseManager.getGameDatastore().find(Avatar.class).filter(Filters.eq("ownerId", player.getUid())).stream().toList(); } public static void saveItem(GameItem item) { - DatabaseManager.getDatastore().save(item); + DatabaseManager.getGameDatastore().save(item); } public static boolean deleteItem(GameItem item) { - DeleteResult result = DatabaseManager.getDatastore().delete(item); + DeleteResult result = DatabaseManager.getGameDatastore().delete(item); return result.wasAcknowledged(); } public static List getInventoryItems(Player player) { - return DatabaseManager.getDatastore().find(GameItem.class).filter(Filters.eq("ownerId", player.getUid())).stream().toList(); + return DatabaseManager.getGameDatastore().find(GameItem.class).filter(Filters.eq("ownerId", player.getUid())).stream().toList(); } public static List getFriends(Player player) { - return DatabaseManager.getDatastore().find(Friendship.class).filter(Filters.eq("ownerId", player.getUid())).stream().toList(); + return DatabaseManager.getGameDatastore().find(Friendship.class).filter(Filters.eq("ownerId", player.getUid())).stream().toList(); } public static List getReverseFriends(Player player) { - return DatabaseManager.getDatastore().find(Friendship.class).filter(Filters.eq("friendId", player.getUid())).stream().toList(); + return DatabaseManager.getGameDatastore().find(Friendship.class).filter(Filters.eq("friendId", player.getUid())).stream().toList(); } public static void saveFriendship(Friendship friendship) { - DatabaseManager.getDatastore().save(friendship); + DatabaseManager.getGameDatastore().save(friendship); } public static void deleteFriendship(Friendship friendship) { - DatabaseManager.getDatastore().delete(friendship); + DatabaseManager.getGameDatastore().delete(friendship); } public static Friendship getReverseFriendship(Friendship friendship) { - return DatabaseManager.getDatastore().find(Friendship.class).filter(Filters.and( + return DatabaseManager.getGameDatastore().find(Friendship.class).filter(Filters.and( Filters.eq("ownerId", friendship.getFriendId()), Filters.eq("friendId", friendship.getOwnerId()) )).first(); @@ -222,7 +222,7 @@ public final class DatabaseHelper { } public static List getGachaRecords(int ownerId, int page, int gachaType, int pageSize){ - return DatabaseManager.getDatastore().find(GachaRecord.class).filter( + return DatabaseManager.getGameDatastore().find(GachaRecord.class).filter( Filters.eq("ownerId", ownerId), Filters.eq("gachaType", gachaType) ).iterator(new FindOptions() @@ -237,7 +237,7 @@ public final class DatabaseHelper { } public static long getGachaRecordsMaxPage(int ownerId, int page, int gachaType, int pageSize){ - long count = DatabaseManager.getDatastore().find(GachaRecord.class).filter( + long count = DatabaseManager.getGameDatastore().find(GachaRecord.class).filter( Filters.eq("ownerId", ownerId), Filters.eq("gachaType", gachaType) ).count(); @@ -245,19 +245,19 @@ public final class DatabaseHelper { } public static void saveGachaRecord(GachaRecord gachaRecord){ - DatabaseManager.getDatastore().save(gachaRecord); + DatabaseManager.getGameDatastore().save(gachaRecord); } public static List getAllMail(Player player) { - return DatabaseManager.getDatastore().find(Mail.class).filter(Filters.eq("ownerUid", player.getUid())).stream().toList(); + return DatabaseManager.getGameDatastore().find(Mail.class).filter(Filters.eq("ownerUid", player.getUid())).stream().toList(); } public static void saveMail(Mail mail) { - DatabaseManager.getDatastore().save(mail); + DatabaseManager.getGameDatastore().save(mail); } public static boolean deleteMail(Mail mail) { - DeleteResult result = DatabaseManager.getDatastore().delete(mail); + DeleteResult result = DatabaseManager.getGameDatastore().delete(mail); return result.wasAcknowledged(); } } diff --git a/src/main/java/emu/grasscutter/database/DatabaseManager.java b/src/main/java/emu/grasscutter/database/DatabaseManager.java index 37bda042b..37ec0094d 100644 --- a/src/main/java/emu/grasscutter/database/DatabaseManager.java +++ b/src/main/java/emu/grasscutter/database/DatabaseManager.java @@ -23,19 +23,19 @@ import emu.grasscutter.game.player.Player; import static emu.grasscutter.Configuration.*; public final class DatabaseManager { - private static Datastore datastore; + private static Datastore gameDatastore; private static Datastore dispatchDatastore; private static final Class[] mappedClasses = new Class[] { DatabaseCounter.class, Account.class, Player.class, Avatar.class, GameItem.class, Friendship.class, GachaRecord.class, Mail.class }; - public static Datastore getDatastore() { - return datastore; + public static Datastore getGameDatastore() { + return gameDatastore; } - public static MongoDatabase getDatabase() { - return getDatastore().getDatabase(); + public static MongoDatabase getGameDatabase() { + return getGameDatastore().getDatabase(); } // Yes. I very dislike this method. However, this will be good for now. @@ -44,42 +44,42 @@ public final class DatabaseManager { if(SERVER.runMode == ServerRunMode.GAME_ONLY) { return dispatchDatastore; } else { - return datastore; + return gameDatastore; } } public static void initialize() { // Initialize - MongoClient mongoClient = MongoClients.create(DATABASE.connectionUri); + MongoClient gameMongoClient = MongoClients.create(DATABASE.game.connectionUri); // Set mapper options. MapperOptions mapperOptions = MapperOptions.builder() .storeEmpties(true).storeNulls(false).build(); // Create data store. - datastore = Morphia.createDatastore(mongoClient, DATABASE.collection, mapperOptions); + gameDatastore = Morphia.createDatastore(gameMongoClient, DATABASE.game.collection, mapperOptions); // Map classes. - datastore.getMapper().map(mappedClasses); + gameDatastore.getMapper().map(mappedClasses); // Ensure indexes try { - datastore.ensureIndexes(); + gameDatastore.ensureIndexes(); } catch (MongoCommandException exception) { Grasscutter.getLogger().info("Mongo index error: ", exception); // Duplicate index error if (exception.getCode() == 85) { // Drop all indexes and re add them - MongoIterable collections = datastore.getDatabase().listCollectionNames(); + MongoIterable collections = gameDatastore.getDatabase().listCollectionNames(); for (String name : collections) { - datastore.getDatabase().getCollection(name).dropIndexes(); + gameDatastore.getDatabase().getCollection(name).dropIndexes(); } // Add back indexes - datastore.ensureIndexes(); + gameDatastore.ensureIndexes(); } } if(SERVER.runMode == ServerRunMode.GAME_ONLY) { - MongoClient dispatchMongoClient = MongoClients.create(GAME_OPTIONS.databaseInfo.connectionUri); - dispatchDatastore = Morphia.createDatastore(dispatchMongoClient, GAME_OPTIONS.databaseInfo.collection); + MongoClient dispatchMongoClient = MongoClients.create(DATABASE.server.connectionUri); + dispatchDatastore = Morphia.createDatastore(dispatchMongoClient, DATABASE.server.collection); // Ensure indexes for dispatch server try { @@ -101,14 +101,14 @@ public final class DatabaseManager { } public static synchronized int getNextId(Class c) { - DatabaseCounter counter = getDatastore().find(DatabaseCounter.class).filter(Filters.eq("_id", c.getSimpleName())).first(); + DatabaseCounter counter = getGameDatastore().find(DatabaseCounter.class).filter(Filters.eq("_id", c.getSimpleName())).first(); if (counter == null) { counter = new DatabaseCounter(c.getSimpleName()); } try { return counter.getNextId(); } finally { - getDatastore().save(counter); + getGameDatastore().save(counter); } } diff --git a/src/main/java/emu/grasscutter/plugin/PluginManager.java b/src/main/java/emu/grasscutter/plugin/PluginManager.java index 5974cac44..5d58744a4 100644 --- a/src/main/java/emu/grasscutter/plugin/PluginManager.java +++ b/src/main/java/emu/grasscutter/plugin/PluginManager.java @@ -7,6 +7,7 @@ import emu.grasscutter.server.event.HandlerPriority; import emu.grasscutter.utils.Utils; import java.io.File; +import java.io.FileNotFoundException; import java.io.InputStreamReader; import java.lang.reflect.Method; import java.net.MalformedURLException; @@ -90,6 +91,8 @@ public final class PluginManager { fileReader.close(); // Close the file reader. } catch (ClassNotFoundException ignored) { Grasscutter.getLogger().warn("Plugin " + plugin.getName() + " has an invalid main class."); + } catch (FileNotFoundException ignored) { + Grasscutter.getLogger().warn("Plugin " + plugin.getName() + " lacks a valid config file."); } } catch (Exception exception) { Grasscutter.getLogger().error("Failed to load plugin: " + plugin.getName(), exception); diff --git a/src/main/java/emu/grasscutter/utils/ConfigContainer.java b/src/main/java/emu/grasscutter/utils/ConfigContainer.java index 0a453191c..76556700c 100644 --- a/src/main/java/emu/grasscutter/utils/ConfigContainer.java +++ b/src/main/java/emu/grasscutter/utils/ConfigContainer.java @@ -2,6 +2,8 @@ package emu.grasscutter.utils; import com.google.gson.JsonObject; import emu.grasscutter.Grasscutter; +import emu.grasscutter.Grasscutter.ServerDebugMode; +import emu.grasscutter.Grasscutter.ServerRunMode; import java.io.FileReader; import java.lang.reflect.Field; @@ -15,7 +17,7 @@ import static emu.grasscutter.Grasscutter.config; */ public class ConfigContainer { private static int version() { - return 1; + return 2; } /** @@ -69,8 +71,13 @@ public class ConfigContainer { /* Option containers. */ public static class Database { - public String connectionUri = "mongodb://localhost:27017"; - public String collection = "grasscutter"; + public DataStore server = new DataStore(); + public DataStore game = new DataStore(); + + public static class DataStore { + public String connectionUri = "mongodb://localhost:27017"; + public String collection = "grasscutter"; + } } public static class Structure { @@ -86,8 +93,8 @@ public class ConfigContainer { } public static class Server { - public Grasscutter.ServerDebugMode debugLevel = Grasscutter.ServerDebugMode.NONE; - public Grasscutter.ServerRunMode runMode = Grasscutter.ServerRunMode.HYBRID; + public ServerDebugMode debugLevel = ServerDebugMode.NONE; + public ServerRunMode runMode = ServerRunMode.HYBRID; public Dispatch dispatch = new Dispatch(); public Game game = new Game(); @@ -112,7 +119,7 @@ public class ConfigContainer { public int bindPort = 443; /* This is the port used in URLs. */ - public int accessPort = 443; + public int accessPort = 0; public Encryption encryption = new Encryption(); public Policies policies = new Policies(); @@ -128,7 +135,7 @@ public class ConfigContainer { public int bindPort = 22102; /* This is the port used in the default region. */ - public int accessPort = 22102; + public int accessPort = 0; public GameOptions gameOptions = new GameOptions(); public JoinOptions joinOptions = new JoinOptions(); @@ -155,16 +162,14 @@ public class ConfigContainer { } public static class GameOptions { - public GameOptions.InventoryLimits inventoryLimits = new GameOptions.InventoryLimits(); - public GameOptions.AvatarLimits avatarLimits = new GameOptions.AvatarLimits(); + public InventoryLimits inventoryLimits = new InventoryLimits(); + public AvatarLimits avatarLimits = new AvatarLimits(); public int worldEntityLimit = 1000; // Unenforced. TODO: Implement. public boolean watchGachaConfig = false; public boolean enableShopItems = true; public boolean staminaUsage = true; - public GameOptions.Rates rates = new GameOptions.Rates(); - - public Database databaseInfo = new Database(); + public Rates rates = new Rates(); public static class InventoryLimits { public int weapons = 2000; From 1f7f13ffe178cf0d2992446f875c457606298000 Mon Sep 17 00:00:00 2001 From: Secretboy-SMR Date: Wed, 11 May 2022 21:14:07 +0800 Subject: [PATCH 03/14] It will use the english as default rather than tell you the value is not exist if there's no translation for currently language --- src/main/java/emu/grasscutter/utils/Language.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/utils/Language.java b/src/main/java/emu/grasscutter/utils/Language.java index 3789f594a..c343e949e 100644 --- a/src/main/java/emu/grasscutter/utils/Language.java +++ b/src/main/java/emu/grasscutter/utils/Language.java @@ -160,7 +160,9 @@ public final class Language { JsonObject object = this.languageData; int index = 0; - String result = "This value does not exist. Please report this to the Discord: " + key; + String valueNotFoundPattern = "This value does not exist. Please report this to the Discord: "; + String result = valueNotFoundPattern + key; + boolean isValueFound = false; while (true) { if(index == keys.length) break; @@ -171,10 +173,18 @@ public final class Language { if(element.isJsonObject()) object = element.getAsJsonObject(); else { + isValueFound = true; result = element.getAsString(); break; } } else break; } + + if (!isValueFound && !languageCode.equals("en-US")) { + var englishValue = Grasscutter.getLanguage("en-US").get(key); + if (!englishValue.contains(valueNotFoundPattern)) { + result += "\nhere is english version:\n" + englishValue; + } + } this.cachedTranslations.put(key, result); return result; } From 1da384091a1093a6c6b1c47a5aff22de37705585 Mon Sep 17 00:00:00 2001 From: Benjamin Elsdon Date: Wed, 11 May 2022 20:35:27 +0800 Subject: [PATCH 04/14] Add verifyUser to AuthenticationHandler --- .../dispatch/authentication/AuthenticationHandler.java | 7 +++++++ .../authentication/DefaultAuthenticationHandler.java | 6 ++++++ src/main/resources/languages/en-US.json | 3 +++ 3 files changed, 16 insertions(+) diff --git a/src/main/java/emu/grasscutter/server/dispatch/authentication/AuthenticationHandler.java b/src/main/java/emu/grasscutter/server/dispatch/authentication/AuthenticationHandler.java index 92a2961ea..e644a9f1d 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/authentication/AuthenticationHandler.java +++ b/src/main/java/emu/grasscutter/server/dispatch/authentication/AuthenticationHandler.java @@ -12,5 +12,12 @@ public interface AuthenticationHandler { void handleRegister(Request req, Response res); void handleChangePassword(Request req, Response res); + /** + * Other plugins may need to verify a user's identity using details from handleLogin() + * @param details The user's unique one-time token that needs to be verified + * @return If the verification was successful + */ + boolean verifyUser(String details); + LoginResultJson handleGameLogin(Request req, LoginAccountRequestJson requestData); } diff --git a/src/main/java/emu/grasscutter/server/dispatch/authentication/DefaultAuthenticationHandler.java b/src/main/java/emu/grasscutter/server/dispatch/authentication/DefaultAuthenticationHandler.java index 67b3d4023..122e04ff6 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/authentication/DefaultAuthenticationHandler.java +++ b/src/main/java/emu/grasscutter/server/dispatch/authentication/DefaultAuthenticationHandler.java @@ -28,6 +28,12 @@ public class DefaultAuthenticationHandler implements AuthenticationHandler { res.send("Authentication is not available with the default authentication method"); } + @Override + public boolean verifyUser(String details) { + Grasscutter.getLogger().info(translate("dispatch.authentication.default_unable_to_verify")); + return false; + } + @Override public LoginResultJson handleGameLogin(Request req, LoginAccountRequestJson requestData) { LoginResultJson responseData = new LoginResultJson(); diff --git a/src/main/resources/languages/en-US.json b/src/main/resources/languages/en-US.json index c9c3c0c70..2b392b682 100644 --- a/src/main/resources/languages/en-US.json +++ b/src/main/resources/languages/en-US.json @@ -16,6 +16,9 @@ "no_keystore_error": "[Dispatch] No SSL cert found! Falling back to HTTP server.", "default_password": "[Dispatch] The default keystore password was loaded successfully. Please consider setting the password to 123456 in config.json." }, + "authentication": { + "default_unable_to_verify": "[Authentication] Something called the verifyUser method which is unavailable in the default authentication handler" + }, "no_commands_error": "Commands are not supported in dispatch only mode.", "unhandled_request_error": "[Dispatch] Potential unhandled %s request: %s", "account": { From 39796099c8f630dfaf43e92fa27f2a8ae3baff9f Mon Sep 17 00:00:00 2001 From: tester233 <105267106+tester233@users.noreply.github.com> Date: Wed, 11 May 2022 18:25:01 +0800 Subject: [PATCH 05/14] Improve text & remove extra punctuation --- src/main/resources/languages/zh-CN.json | 100 ++++++++++++------------ 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/src/main/resources/languages/zh-CN.json b/src/main/resources/languages/zh-CN.json index 2f9663f4f..01572a20b 100644 --- a/src/main/resources/languages/zh-CN.json +++ b/src/main/resources/languages/zh-CN.json @@ -55,7 +55,7 @@ "permission_error": "哼哼哼!你没有执行此命令的权限!请联系服务器管理员解决!", "console_execute_error": "此命令只能在服务器控制台执行呐~", "player_execute_error": "此命令只能在游戏内执行哦~", - "command_exist_error": "这条命令……好像找不到呢?。", + "command_exist_error": "这条命令...好像找不到呢?", "no_description_specified": "没有指定说明", "invalid": { "amount": "无效的数量。", @@ -97,19 +97,19 @@ "delete": "账号已删除。", "no_account": "账号不存在。", "command_usage": "用法:account <用户名> [uid]", - "description": "创建或删除账号。" + "description": "创建或删除账号" }, "broadcast": { "command_usage": "用法:broadcast <消息>", "message_sent": "公告已发送。", - "description": "向所有玩家发送公告。" + "description": "向所有玩家发送公告" }, "changescene": { "usage": "用法:changescene <场景ID>", "already_in_scene": "你已经在这个场景中了。", "success": "已切换至场景 %s。", "exists_error": "此场景不存在。", - "description": "切换指定场景。" + "description": "切换指定场景" }, "clear": { "command_usage": "用法:clear \nall: 所有, wp: 武器, art: 圣遗物, mat: 材料", @@ -120,32 +120,32 @@ "displays": "已清空 %s 的屏幕。", "virtuals": "已清除 %s 的所有货币和经验值。", "everything": "已清除 %s 的所有物品。", - "description": "从你的背包中删除所有未装备且已解锁的物品,包括稀有物品。" + "description": "从你的背包中删除所有未装备且已解锁的物品,包括稀有物品" }, "coop": { "usage": "用法:coop <玩家ID> <目标玩家ID>", - "success": "已强制传送 %s 到 %s 的世界", - "description": "强制传送指定用户到他人的世界。" + "success": "已强制传送 %s 到 %s 的世界。", + "description": "强制传送指定用户到他人的世界" }, "enter_dungeon": { "usage": "用法:enterdungeon <秘境ID>", - "changed": "已进入秘境 %s", + "changed": "已进入秘境 %s。", "not_found_error": "此秘境不存在。", "in_dungeon_error": "你已经在秘境中了。", - "description": "进入指定秘境。" + "description": "进入指定秘境" }, "giveAll": { "usage": "用法:giveall [玩家] [数量]", "started": "正在给予全部物品...", "success": "已给予 %s 全部物品。", "invalid_amount_or_playerId": "无效的数量/玩家ID。", - "description": "给予所有物品。" + "description": "给予所有物品" }, "giveArtifact": { "usage": "用法:giveart|gart [玩家] <圣遗物ID> <主词条ID> [<副词条ID>[,<强化次数>]]... [等级]", "id_error": "无效的圣遗物ID。", "success": "已将 %s 给予 %s。", - "description": "给予指定圣遗物。" + "description": "给予指定圣遗物" }, "giveChar": { "usage": "用法:givechar <玩家> <角色ID|角色名> [数量]", @@ -153,50 +153,50 @@ "invalid_avatar_id": "无效的角色ID。", "invalid_avatar_level": "无效的角色等级。", "invalid_avatar_or_player_id": "无效的角色ID/玩家ID。", - "description": "给予指定角色。" + "description": "给予指定角色" }, "give": { "usage": "用法:give <玩家> <物品ID|物品名> [数量] [等级] [精炼等级]", "refinement_only_applicable_weapons": "只有武器可以设置精炼等级。", "refinement_must_between_1_and_5": "精炼等级必须在 1 到 5 之间。", "given": "已将 %s 个 %s 给予 %s。", - "given_with_level_and_refinement": "已将 %s (等级 %s, 精炼 %s) %s 个给予 %s", - "given_level": "已将 %s (等级 %s) %s 个给予 %s", - "description": "给予指定物品。" + "given_with_level_and_refinement": "已将 %s (等级 %s, 精炼 %s) %s 个给予 %s。", + "given_level": "已将 %s (等级 %s) %s 个给予 %s。", + "description": "给予指定物品" }, "godmode": { - "success": "%s 的无敌模式已被设置为 %s。", - "description": "防止你受到伤害。" + "success": "%s 的上帝模式已被设置为 %s。", + "description": "防止你受到伤害" }, "heal": { "success": "已经治疗所有角色。", - "description": "治疗当前队伍的角色。" + "description": "治疗当前队伍的角色" }, "kick": { - "player_kick_player": "玩家 [%s:%s] 已将 [%s:%s] 踢出", - "server_kick_player": "正在踢出玩家 [%s:%s]", - "description": "从服务器内踢出指定玩家。" + "player_kick_player": "玩家 [%s:%s] 已将 [%s:%s] 踢出。", + "server_kick_player": "正在踢出玩家 [%s:%s]...", + "description": "从服务器内踢出指定玩家" }, "kill": { "usage": "用法:killall [玩家UID] [场景ID]", - "scene_not_found_in_player_world": "未在玩家世界中找到此场景", + "scene_not_found_in_player_world": "未在玩家世界中找到此场景。", "kill_monsters_in_scene": "已杀死场景 %s 中的 %s 个怪物。", - "description": "杀死所有怪物。" + "description": "杀死所有怪物" }, "killCharacter": { "usage": "用法:/killcharacter [玩家ID]", "success": "已杀死 %s 当前角色。", - "description": "杀死当前角色。" + "description": "杀死当前角色" }, "language": { "current_language": "当前语言是: %s", "language_changed": "语言切换至: %s", "language_not_found": "目前服务端没有这种语言: %s", - "description": "显示或切换当前语言。" + "description": "显示或切换当前语言" }, "list": { "success": "目前在线人数:%s", - "description": "查看所有玩家。" + "description": "查看所有玩家" }, "permission": { "usage": "用法:permission <用户名> <权限>", @@ -205,25 +205,25 @@ "remove": "权限已移除。", "not_have_error": "此玩家未拥有权限!", "account_error": "账号不存在。", - "description": "添加或移除指定玩家的权限。" + "description": "添加或移除指定玩家的权限" }, "position": { "success": "坐标:%s, %s, %s\n场景ID:%s", - "description": "获取所在位置。" + "description": "获取所在位置" }, "reload": { "reload_start": "正在重载配置文件和数据。", "reload_done": "重载完成。", - "description": "重载配置文件和数据。" + "description": "重载配置文件和数据" }, "resetConst": { "reset_all": "重置所有角色的命座。", "success": "已重置 %s 的命座,重新登录后生效。", - "description": "重置当前角色的命之座,执行命令后需重新登录以生效。" + "description": "重置当前角色的命之座,执行命令后需重新登录以生效" }, "resetShopLimit": { "usage": "用法:/resetshop <玩家ID>", - "description": "重置所选玩家的商店刷新时间。" + "description": "重置所选玩家的商店刷新时间" }, "sendMail": { "usage": "用法:give [玩家] <物品ID|物品名称> [数量]", @@ -246,19 +246,19 @@ "sender": "<发件人>", "arguments": "<物品ID|物品名称|finish> [数量] [等级]", "error": "错误:无效的编写阶段 %s。需要 StackTrace 请查看服务器控制台。", - "description": "向指定用户发送邮件。此命令的用法可根据附加的参数而变化。" + "description": "向指定用户发送邮件。此命令的用法可根据附加的参数而变化" }, "sendMessage": { "usage": "用法:sendmessage <玩家> <消息>", "success": "消息已发送。", - "description": "向指定玩家发送消息。" + "description": "向指定玩家发送消息" }, "setFetterLevel": { "usage": "用法:setfetterlevel <好感度等级>", "range_error": "好感度等级必须在 0 到 10 之间。", - "success": "好感度已设置为 %s 级", + "success": "好感度已设置为 %s 级。", "level_error": "无效的好感度等级。", - "description": "设置当前角色的好感度等级。" + "description": "设置当前角色的好感度等级" }, "setStats": { "usage_console": "用法:setstats|stats @ <属性> <数值>", @@ -270,23 +270,23 @@ "set_self": "%s 已设为 %s。", "set_for_uid": "将 %s (来自 %s) 设置为 %s。", "set_max_hp": "最大生命值已设为 %s。", - "description": "设置当前角色的属性。" + "description": "设置当前角色的属性" }, "setWorldLevel": { "usage": "用法:setworldlevel <等级>", "value_error": "世界等级必须设置在0-8之间。", "success": "已将世界等级设为 %s。", "invalid_world_level": "无效的世界等级。", - "description": "设置世界等级,执行命令后需重新登录以生效。" + "description": "设置世界等级,执行命令后需重新登录以生效" }, "spawn": { "usage": "用法:spawn <实体ID> [数量] [等级(仅怪物)]", "success": "已生成 %s 个 %s。", - "description": "在你附近生成一个生物。" + "description": "在你附近生成一个生物" }, "stop": { "success": "正在关闭服务器...", - "description": "停止服务器。" + "description": "停止服务器" }, "talent": { "usage_1": "设置天赋等级:/talent set <天赋ID> <数值>", @@ -303,20 +303,20 @@ "normal_attack_id": "普通攻击的 ID 为 %s。", "e_skill_id": "元素战技ID %s。", "q_skill_id": "元素爆发ID %s。", - "description": "设置当前角色的天赋等级。" + "description": "设置当前角色的天赋等级" }, "teleportAll": { - "success": "已将所有玩家传送到你的位置", + "success": "已将所有玩家传送到你的位置。", "error": "你只能在多人游戏状态下执行此命令。", - "description": "将你世界中的所有玩家传送到你所在的位置。" + "description": "将你世界中的所有玩家传送到你所在的位置" }, "teleport": { "usage_server": "用法:/tp @<玩家ID> [场景ID]", "usage": "用法:/tp [@<玩家ID>] [场景ID]", "specify_player_id": "你必须指定一个玩家ID。", "invalid_position": "无效的位置。", - "success": "传送 %s 到坐标 %s,%s,%s,场景为 %s", - "description": "改变指定玩家的位置。" + "success": "传送 %s 到坐标 %s,%s,%s,场景为 %s。", + "description": "改变指定玩家的位置" }, "tower": { "unlock_done": "深境回廊的所有层已全部解锁。" @@ -325,28 +325,28 @@ "usage": "用法:weather <天气ID> [气候ID]", "success": "已更改天气为 %s,气候为 %s。", "invalid_id": "无效的天气ID。", - "description": "更改天气。" + "description": "更改天气" }, "drop": { "command_usage": "用法:drop <物品ID|物品名称> [数量]", "success": "已丢下 %s 个 %s。", - "description": "在你附近丢下一个物品。" + "description": "在你附近丢下一个物品" }, "help": { "usage": "用法:", "aliases": "别名:", "available_commands": "可用命令:", - "description": "发送帮助信息或显示指定命令的信息。" + "description": "发送帮助信息或显示指定命令的信息" }, "restart": { - "description": "重新启动服务器。" + "description": "重新启动服务器" }, "unlocktower": { "success": "解锁完成。", - "description": "解锁深境螺旋的所有层。" + "description": "解锁深境螺旋的所有层" }, "resetshop": { - "description": "重置商店刷新时间。" + "description": "重置商店刷新时间" } } } From 07ad24262ed58a1efae3e5ddf4787cea3fed39b8 Mon Sep 17 00:00:00 2001 From: tester233 <105267106+tester233@users.noreply.github.com> Date: Wed, 11 May 2022 19:37:52 +0800 Subject: [PATCH 06/14] Improve text --- src/main/resources/languages/zh-CN.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/resources/languages/zh-CN.json b/src/main/resources/languages/zh-CN.json index 01572a20b..111e3c0c5 100644 --- a/src/main/resources/languages/zh-CN.json +++ b/src/main/resources/languages/zh-CN.json @@ -20,7 +20,7 @@ "unhandled_request_error": "[Dispatch] 潜在的未处理请求:%s %s", "account": { "login_attempt": "[Dispatch] 客户端 %s 正在尝试登录", - "login_success": "[Dispatch] 客户端 %s 已登录,UID为 %s", + "login_success": "[Dispatch] 客户端 %s 已登录,UID 为 %s", "login_token_attempt": "[Dispatch] 客户端 %s 正在尝试使用 token 登录", "login_token_error": "[Dispatch] 客户端 %s 使用 token 登录失败", "login_token_success": "[Dispatch] 客户端 %s 已通过 token 登录,UID 为 %s", @@ -96,7 +96,7 @@ "create": "已创建账号,UID 为 %s。", "delete": "账号已删除。", "no_account": "账号不存在。", - "command_usage": "用法:account <用户名> [uid]", + "command_usage": "用法:account <用户名> [UID]", "description": "创建或删除账号" }, "broadcast": { @@ -290,7 +290,7 @@ }, "talent": { "usage_1": "设置天赋等级:/talent set <天赋ID> <数值>", - "usage_2": "另一种设置天赋等级的方法:/talent <数值>", + "usage_2": "另一种设置天赋等级的方法:/talent <数值>", "usage_3": "获取天赋ID:/talent getid", "lower_16": "无效的天赋等级,天赋等级应小于等于15。", "set_id": "将天赋等级设为 %s。", From 9bdb1c762cfa7fb71bd2d4b25744aba80b84d537 Mon Sep 17 00:00:00 2001 From: mingjun97 Date: Tue, 10 May 2022 18:34:30 -0700 Subject: [PATCH 07/14] Introduce `-version` argument to display version --- build.gradle | 16 ++++++++++++++++ src/main/java/emu/grasscutter/Grasscutter.java | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/build.gradle b/build.gradle index 4434ed28e..186a6d440 100644 --- a/build.gradle +++ b/build.gradle @@ -45,6 +45,14 @@ targetCompatibility = JavaVersion.VERSION_17 group = 'xyz.grasscutters' version = '1.1.1-dev' +def gitCommitHash = { + try { + return 'git rev-parse --verify --short HEAD'.execute().text.trim() + } catch (e) { + return "GIT_NOT_FOUND" + } +} + sourceCompatibility = 17 targetCompatibility = 17 @@ -97,6 +105,7 @@ application { mainClassName = 'emu.grasscutter.Grasscutter' } + jar { manifest { attributes 'Main-Class': 'emu.grasscutter.Grasscutter' @@ -113,6 +122,13 @@ jar { from('src/main/java') { include '*.xml' } + new File(projectDir, "src/generated/main/java/emu/grasscutter/BuildConfig.java").text = """ + package emu.grasscutter; + public class BuildConfig { + public static final String VERSION = \"${version}\"; + public static final String GIT_HASH = \"${gitCommitHash()}\"; + } + """ destinationDir = file(".") } diff --git a/src/main/java/emu/grasscutter/Grasscutter.java b/src/main/java/emu/grasscutter/Grasscutter.java index 73e761e6e..a192815d1 100644 --- a/src/main/java/emu/grasscutter/Grasscutter.java +++ b/src/main/java/emu/grasscutter/Grasscutter.java @@ -29,6 +29,7 @@ import emu.grasscutter.server.dispatch.DispatchServer; import emu.grasscutter.server.game.GameServer; import emu.grasscutter.tools.Tools; import emu.grasscutter.utils.Crypto; +import emu.grasscutter.BuildConfig; import javax.annotation.Nullable; @@ -82,6 +83,9 @@ public final class Grasscutter { case "-gachamap" -> { Tools.createGachaMapping(DATA("gacha_mappings.js")); exitEarly = true; } + case "-version" -> { + System.out.println("Grasscutter version: " + BuildConfig.VERSION + "\nGit Hash: " + BuildConfig.GIT_HASH); exitEarly = true; + } } } From c00a6dbadd3d61f2b30f9ad509f2190ddebe7656 Mon Sep 17 00:00:00 2001 From: mingjun97 Date: Tue, 10 May 2022 21:11:19 -0700 Subject: [PATCH 08/14] Fix github action build issue * Move `BuildConfig.java` from `/src/generated`to `/src/main` to accomplish the building pipeline * Add BuildConfig.java to the .gitignore --- .gitignore | 1 + build.gradle | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 9fa6c9427..6fd78ed3b 100644 --- a/.gitignore +++ b/.gitignore @@ -69,6 +69,7 @@ language/ languages/ gacha-mapping.js data/gacha_mappings.js +BuildConfig.java # macOS .DS_Store diff --git a/build.gradle b/build.gradle index 186a6d440..47f433b57 100644 --- a/build.gradle +++ b/build.gradle @@ -122,7 +122,7 @@ jar { from('src/main/java') { include '*.xml' } - new File(projectDir, "src/generated/main/java/emu/grasscutter/BuildConfig.java").text = """ + new File(projectDir, "src/main/java/emu/grasscutter/BuildConfig.java").text = """ package emu.grasscutter; public class BuildConfig { public static final String VERSION = \"${version}\"; From edc75b2632318c99671cb089d6ed694750864c0c Mon Sep 17 00:00:00 2001 From: mingjun97 Date: Tue, 10 May 2022 21:36:30 -0700 Subject: [PATCH 09/14] Display version info at console starting --- src/main/java/emu/grasscutter/Grasscutter.java | 1 + src/main/resources/languages/en-US.json | 3 ++- src/main/resources/languages/pl-PL.json | 3 ++- src/main/resources/languages/zh-CN.json | 3 ++- src/main/resources/languages/zh-TW.json | 3 ++- 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/java/emu/grasscutter/Grasscutter.java b/src/main/java/emu/grasscutter/Grasscutter.java index a192815d1..b328a453b 100644 --- a/src/main/java/emu/grasscutter/Grasscutter.java +++ b/src/main/java/emu/grasscutter/Grasscutter.java @@ -190,6 +190,7 @@ public final class Grasscutter { } getLogger().info(translate("messages.status.done")); + getLogger().info(translate("messages.status.version", BuildConfig.VERSION, BuildConfig.GIT_HASH)); String input = null; boolean isLastInterrupted = false; while (true) { diff --git a/src/main/resources/languages/en-US.json b/src/main/resources/languages/en-US.json index 2b392b682..099e36eaa 100644 --- a/src/main/resources/languages/en-US.json +++ b/src/main/resources/languages/en-US.json @@ -48,7 +48,8 @@ "run_mode_error": "Invalid server run mode: %s.", "run_mode_help": "Server run mode must be 'HYBRID', 'DISPATCH_ONLY', or 'GAME_ONLY'. Unable to start Grasscutter...", "create_resources": "Creating resources folder...", - "resources_error": "Place a copy of 'BinOutput' and 'ExcelBinOutput' in the resources folder." + "resources_error": "Place a copy of 'BinOutput' and 'ExcelBinOutput' in the resources folder.", + "version": "Grasscutter version: %s, Git Hash: %s" } }, "commands": { diff --git a/src/main/resources/languages/pl-PL.json b/src/main/resources/languages/pl-PL.json index 8f76d8951..0f5d88aa9 100644 --- a/src/main/resources/languages/pl-PL.json +++ b/src/main/resources/languages/pl-PL.json @@ -45,7 +45,8 @@ "run_mode_error": "Błędny tryb pracy serwera: %s.", "run_mode_help": "Tryb pracy serwera musi być ustawiony na 'HYBRID', 'DISPATCH_ONLY', lub 'GAME_ONLY'. Nie można wystartować Grasscutter...", "create_resources": "Tworzenie folderu resources...", - "resources_error": "Umieść kopię 'BinOutput' i 'ExcelBinOutput' w folderze resources." + "resources_error": "Umieść kopię 'BinOutput' i 'ExcelBinOutput' w folderze resources.", + "version": "Grasscutter versión: %s, Git Hash: %s" } }, "commands": { diff --git a/src/main/resources/languages/zh-CN.json b/src/main/resources/languages/zh-CN.json index 111e3c0c5..5f2b02736 100644 --- a/src/main/resources/languages/zh-CN.json +++ b/src/main/resources/languages/zh-CN.json @@ -45,7 +45,8 @@ "run_mode_error": "无效的服务器运行模式:%s。", "run_mode_help": "服务器运行模式必须为 HYBRID、DISPATCH_ONLY 或 GAME_ONLY。Grasscutter 启动失败...", "create_resources": "正在创建 resources 目录...", - "resources_error": "请将 BinOutput 和 ExcelBinOutput 复制到 resources 目录。" + "resources_error": "请将 BinOutput 和 ExcelBinOutput 复制到 resources 目录。", + "version": "Grasscutter版本: %s, Git Hash: %s" } }, "commands": { diff --git a/src/main/resources/languages/zh-TW.json b/src/main/resources/languages/zh-TW.json index 9c3c99686..3cb850415 100644 --- a/src/main/resources/languages/zh-TW.json +++ b/src/main/resources/languages/zh-TW.json @@ -45,7 +45,8 @@ "run_mode_error": "無效的伺服器運行模式: %s。", "run_mode_help": "伺服器運行模式必須為 HYBRID 或者 DISPATCH_ONLY 或者 GAME_ONLY。Grasscutter 啟動失敗...", "create_resources": "正在建立 resources 資料夾...", - "resources_error": "請將 BinOutput 和 ExcelBinOutput 複製到 resources 資料夾。" + "resources_error": "請將 BinOutput 和 ExcelBinOutput 複製到 resources 資料夾。", + "version": "Grasscutter版本: %s, Git Hash: %s" } }, "commands": { From 6921ea77a9b78524ceab6f36e39aa97411d6af6b Mon Sep 17 00:00:00 2001 From: mingjun97 Date: Tue, 10 May 2022 23:23:58 -0700 Subject: [PATCH 10/14] Revise version format --- src/main/java/emu/grasscutter/Grasscutter.java | 2 +- src/main/resources/languages/en-US.json | 2 +- src/main/resources/languages/pl-PL.json | 2 +- src/main/resources/languages/zh-CN.json | 2 +- src/main/resources/languages/zh-TW.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/emu/grasscutter/Grasscutter.java b/src/main/java/emu/grasscutter/Grasscutter.java index b328a453b..6ca78dfa9 100644 --- a/src/main/java/emu/grasscutter/Grasscutter.java +++ b/src/main/java/emu/grasscutter/Grasscutter.java @@ -84,7 +84,7 @@ public final class Grasscutter { Tools.createGachaMapping(DATA("gacha_mappings.js")); exitEarly = true; } case "-version" -> { - System.out.println("Grasscutter version: " + BuildConfig.VERSION + "\nGit Hash: " + BuildConfig.GIT_HASH); exitEarly = true; + System.out.println("Grasscutter version: " + BuildConfig.VERSION + "-" + BuildConfig.GIT_HASH); exitEarly = true; } } } diff --git a/src/main/resources/languages/en-US.json b/src/main/resources/languages/en-US.json index 099e36eaa..81e97eed7 100644 --- a/src/main/resources/languages/en-US.json +++ b/src/main/resources/languages/en-US.json @@ -49,7 +49,7 @@ "run_mode_help": "Server run mode must be 'HYBRID', 'DISPATCH_ONLY', or 'GAME_ONLY'. Unable to start Grasscutter...", "create_resources": "Creating resources folder...", "resources_error": "Place a copy of 'BinOutput' and 'ExcelBinOutput' in the resources folder.", - "version": "Grasscutter version: %s, Git Hash: %s" + "version": "Grasscutter version: %s-%s" } }, "commands": { diff --git a/src/main/resources/languages/pl-PL.json b/src/main/resources/languages/pl-PL.json index 0f5d88aa9..aa06723d8 100644 --- a/src/main/resources/languages/pl-PL.json +++ b/src/main/resources/languages/pl-PL.json @@ -46,7 +46,7 @@ "run_mode_help": "Tryb pracy serwera musi być ustawiony na 'HYBRID', 'DISPATCH_ONLY', lub 'GAME_ONLY'. Nie można wystartować Grasscutter...", "create_resources": "Tworzenie folderu resources...", "resources_error": "Umieść kopię 'BinOutput' i 'ExcelBinOutput' w folderze resources.", - "version": "Grasscutter versión: %s, Git Hash: %s" + "version": "Grasscutter versión: %s-%s" } }, "commands": { diff --git a/src/main/resources/languages/zh-CN.json b/src/main/resources/languages/zh-CN.json index 5f2b02736..84d4a8c94 100644 --- a/src/main/resources/languages/zh-CN.json +++ b/src/main/resources/languages/zh-CN.json @@ -46,7 +46,7 @@ "run_mode_help": "服务器运行模式必须为 HYBRID、DISPATCH_ONLY 或 GAME_ONLY。Grasscutter 启动失败...", "create_resources": "正在创建 resources 目录...", "resources_error": "请将 BinOutput 和 ExcelBinOutput 复制到 resources 目录。", - "version": "Grasscutter版本: %s, Git Hash: %s" + "version": "Grasscutter版本: %s-%s" } }, "commands": { diff --git a/src/main/resources/languages/zh-TW.json b/src/main/resources/languages/zh-TW.json index 3cb850415..8e7a75949 100644 --- a/src/main/resources/languages/zh-TW.json +++ b/src/main/resources/languages/zh-TW.json @@ -46,7 +46,7 @@ "run_mode_help": "伺服器運行模式必須為 HYBRID 或者 DISPATCH_ONLY 或者 GAME_ONLY。Grasscutter 啟動失敗...", "create_resources": "正在建立 resources 資料夾...", "resources_error": "請將 BinOutput 和 ExcelBinOutput 複製到 resources 資料夾。", - "version": "Grasscutter版本: %s, Git Hash: %s" + "version": "Grasscutter版本: %s-%s" } }, "commands": { From 48b5873feedd7a5648ba4c35e3dee2524d76a626 Mon Sep 17 00:00:00 2001 From: mingjun97 Date: Tue, 10 May 2022 23:34:53 -0700 Subject: [PATCH 11/14] Make `injectGitHash` as a task --- build.gradle | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/build.gradle b/build.gradle index 47f433b57..ffb6a498e 100644 --- a/build.gradle +++ b/build.gradle @@ -45,13 +45,6 @@ targetCompatibility = JavaVersion.VERSION_17 group = 'xyz.grasscutters' version = '1.1.1-dev' -def gitCommitHash = { - try { - return 'git rev-parse --verify --short HEAD'.execute().text.trim() - } catch (e) { - return "GIT_NOT_FOUND" - } -} sourceCompatibility = 17 targetCompatibility = 17 @@ -122,13 +115,6 @@ jar { from('src/main/java') { include '*.xml' } - new File(projectDir, "src/main/java/emu/grasscutter/BuildConfig.java").text = """ - package emu.grasscutter; - public class BuildConfig { - public static final String VERSION = \"${version}\"; - public static final String GIT_HASH = \"${gitCommitHash()}\"; - } - """ destinationDir = file(".") } @@ -242,6 +228,26 @@ javadoc { } } +task injectGitHash { + doLast { + def gitCommitHash = { + try { + return 'git rev-parse --verify --short HEAD'.execute().text.trim() + } catch (e) { + return "GIT_NOT_FOUND" + } + } + new File(projectDir, "src/main/java/emu/grasscutter/BuildConfig.java").text = """ + package emu.grasscutter; + public class BuildConfig { + public static final String VERSION = \"${version}\"; + public static final String GIT_HASH = \"${gitCommitHash()}\"; + } + """ + } +} + processResources { dependsOn "generateProto" + dependsOn "injectGitHash" } From f16edfd58e9c9aeebc4592246c9f1da72fcb1ffe Mon Sep 17 00:00:00 2001 From: mingjun97 Date: Tue, 10 May 2022 23:55:40 -0700 Subject: [PATCH 12/14] Fix building error --- build.gradle | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/build.gradle b/build.gradle index ffb6a498e..3a8d2c34d 100644 --- a/build.gradle +++ b/build.gradle @@ -229,25 +229,22 @@ javadoc { } task injectGitHash { - doLast { - def gitCommitHash = { - try { - return 'git rev-parse --verify --short HEAD'.execute().text.trim() - } catch (e) { - return "GIT_NOT_FOUND" - } + def gitCommitHash = { + try { + return 'git rev-parse --verify --short HEAD'.execute().text.trim() + } catch (e) { + return "GIT_NOT_FOUND" } - new File(projectDir, "src/main/java/emu/grasscutter/BuildConfig.java").text = """ - package emu.grasscutter; - public class BuildConfig { - public static final String VERSION = \"${version}\"; - public static final String GIT_HASH = \"${gitCommitHash()}\"; - } - """ } + new File(projectDir, "src/main/java/emu/grasscutter/BuildConfig.java").text = """ + package emu.grasscutter; + public class BuildConfig { + public static final String VERSION = \"${version}\"; + public static final String GIT_HASH = \"${gitCommitHash()}\"; + } + """ } processResources { dependsOn "generateProto" - dependsOn "injectGitHash" } From 1d4e0e09d04f2e7427af35af355ac2dbe86a6fbe Mon Sep 17 00:00:00 2001 From: ImmuState Date: Wed, 11 May 2022 11:19:25 -0700 Subject: [PATCH 13/14] Add gacha details page. --- data/gacha_details.html | 121 ++++++++++++++++++ .../grasscutter/game/gacha/GachaBanner.java | 9 +- .../grasscutter/game/gacha/GachaManager.java | 18 ++- .../server/dispatch/DispatchServer.java | 4 + .../dispatch/http/GachaDetailsHandler.java | 90 +++++++++++++ src/main/resources/languages/en-US.json | 9 ++ src/main/resources/languages/pl-PL.json | 9 ++ src/main/resources/languages/zh-CN.json | 9 ++ src/main/resources/languages/zh-TW.json | 9 ++ 9 files changed, 275 insertions(+), 3 deletions(-) create mode 100644 data/gacha_details.html create mode 100644 src/main/java/emu/grasscutter/server/dispatch/http/GachaDetailsHandler.java diff --git a/data/gacha_details.html b/data/gacha_details.html new file mode 100644 index 000000000..16cf7313a --- /dev/null +++ b/data/gacha_details.html @@ -0,0 +1,121 @@ + + + + + + + + + Banner Details + + + +
+
+

{{TITLE}}

+ +

{{AVAILABLE_FIVE_STARS}}

+
+
    +
+ +

{{AVAILABLE_FOUR_STARS}}

+
+
    +
+ +

{{AVAILABLE_THREE_STARS}}

+
+
    +
+
+
+
+ +
+ + + + diff --git a/src/main/java/emu/grasscutter/game/gacha/GachaBanner.java b/src/main/java/emu/grasscutter/game/gacha/GachaBanner.java index dce433fcf..7a2646a4f 100644 --- a/src/main/java/emu/grasscutter/game/gacha/GachaBanner.java +++ b/src/main/java/emu/grasscutter/game/gacha/GachaBanner.java @@ -102,6 +102,11 @@ public class GachaBanner { + lr(DISPATCH_INFO.accessAddress, DISPATCH_INFO.bindAddress) + ":" + lr(DISPATCH_INFO.accessPort, DISPATCH_INFO.bindPort) + "/gacha?s=" + sessionKey + "&gachaType=" + gachaType; + String details = "http" + (DISPATCH_INFO.encryption.useInRouting ? "s" : "") + "://" + + lr(DISPATCH_INFO.accessAddress, DISPATCH_INFO.bindAddress) + ":" + + lr(DISPATCH_INFO.accessPort, DISPATCH_INFO.bindPort) + + "/gacha/details?s=" + sessionKey + "&gachaType=" + gachaType; + // Grasscutter.getLogger().info("record = " + record); GachaInfo.Builder info = GachaInfo.newBuilder() .setGachaType(this.getGachaType()) @@ -112,8 +117,8 @@ public class GachaBanner { .setCostItemNum(1) .setGachaPrefabPath(this.getPrefabPath()) .setGachaPreviewPrefabPath(this.getPreviewPrefabPath()) - .setGachaProbUrl(record) - .setGachaProbUrlOversea(record) + .setGachaProbUrl(details) + .setGachaProbUrlOversea(details) .setGachaRecordUrl(record) .setGachaRecordUrlOversea(record) .setTenCostItemId(this.getCostItem()) diff --git a/src/main/java/emu/grasscutter/game/gacha/GachaManager.java b/src/main/java/emu/grasscutter/game/gacha/GachaManager.java index 03edca09a..f0baf65a9 100644 --- a/src/main/java/emu/grasscutter/game/gacha/GachaManager.java +++ b/src/main/java/emu/grasscutter/game/gacha/GachaManager.java @@ -65,7 +65,23 @@ public class GachaManager { public Int2ObjectMap getGachaBanners() { return gachaBanners; } - + + public int[] getYellowAvatars() { + return this.yellowAvatars; + } + public int[] getYellowWeapons() { + return this.yellowWeapons; + } + public int[] getPurpleAvatars() { + return this.purpleAvatars; + } + public int[] getPurpleWeapons() { + return this.purpleWeapons; + } + public int[] getBlueWeapons() { + return this.blueWeapons; + } + public int randomRange(int min, int max) { return ThreadLocalRandom.current().nextInt(max - min + 1) + min; } diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java index 4e09f8881..c78aff7c6 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -16,6 +16,7 @@ import emu.grasscutter.net.proto.RegionInfoOuterClass.RegionInfo; import emu.grasscutter.net.proto.RegionSimpleInfoOuterClass.RegionSimpleInfo; import emu.grasscutter.server.dispatch.authentication.AuthenticationHandler; import emu.grasscutter.server.dispatch.authentication.DefaultAuthenticationHandler; +import emu.grasscutter.server.dispatch.http.GachaDetailsHandler; import emu.grasscutter.server.dispatch.http.GachaRecordHandler; import emu.grasscutter.server.dispatch.json.*; import emu.grasscutter.server.dispatch.json.ComboTokenReqJson.LoginTokenData; @@ -455,6 +456,9 @@ public final class DispatchServer { httpServer.raw().config.addSinglePageRoot("/gacha/mappings", gachaMappingsPath, Location.EXTERNAL); + // gacha details + httpServer.get("/gacha/details", new GachaDetailsHandler()); + // static file support for plugins httpServer.raw().config.precompressStaticFiles = false; // If this isn't set to false, files such as images may appear corrupted when serving static files diff --git a/src/main/java/emu/grasscutter/server/dispatch/http/GachaDetailsHandler.java b/src/main/java/emu/grasscutter/server/dispatch/http/GachaDetailsHandler.java new file mode 100644 index 000000000..d46cead40 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/dispatch/http/GachaDetailsHandler.java @@ -0,0 +1,90 @@ +package emu.grasscutter.server.dispatch.http; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.Set; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.database.DatabaseHelper; +import emu.grasscutter.game.Account; +import emu.grasscutter.game.gacha.GachaBanner; +import emu.grasscutter.game.gacha.GachaManager; +import emu.grasscutter.game.gacha.GachaBanner.BannerType; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.utils.FileUtils; +import emu.grasscutter.utils.Utils; +import express.http.HttpContextHandler; +import express.http.Request; +import express.http.Response; + +import static emu.grasscutter.utils.Language.translate; + +import static emu.grasscutter.Configuration.*; + +public final class GachaDetailsHandler implements HttpContextHandler { + private final String render_template; + + public GachaDetailsHandler() { + File template = new File(Utils.toFilePath(DATA("/gacha_details.html"))); + this.render_template = template.exists() ? new String(FileUtils.read(template)) : null; + } + + @Override + public void handle(Request req, Response res) throws IOException { + String response = this.render_template; + + // Get player info (for langauge). + String sessionKey = req.query("s"); + Account account = DatabaseHelper.getAccountBySessionKey(sessionKey); + Player player = Grasscutter.getGameServer().getPlayerByUid(account.getPlayerUid()); + + // If the template was not loaded, return an error. + if (this.render_template == null) { + res.send(translate(player, "gacha.details.template_missing")); + return; + } + + // Add translated title etc. to the page. + response = response.replace("{{TITLE}}", translate(player, "gacha.details.title")); + response = response.replace("{{AVAILABLE_FIVE_STARS}}", translate(player, "gacha.details.available_five_stars")); + response = response.replace("{{AVAILABLE_FOUR_STARS}}", translate(player, "gacha.details.available_four_stars")); + response = response.replace("{{AVAILABLE_THREE_STARS}}", translate(player, "gacha.details.available_three_stars")); + + // Get the banner info for the banner we want. + int gachaType = Integer.parseInt(req.query("gachaType")); + GachaManager manager = Grasscutter.getGameServer().getGachaManager(); + GachaBanner banner = manager.getGachaBanners().get(gachaType); + + // Add 5-star items. + Set fiveStarItems = new LinkedHashSet<>(); + + Arrays.stream(banner.getRateUpItems1()).forEach(i -> fiveStarItems.add(Integer.toString(i))); + if (banner.getBannerType() == BannerType.STANDARD || banner.getBannerType() == BannerType.EVENT) { + Arrays.stream(manager.getYellowAvatars()).forEach(i -> fiveStarItems.add(Integer.toString(i))); + } + if (banner.getBannerType() == BannerType.STANDARD || banner.getBannerType() == BannerType.WEAPON) { + Arrays.stream(manager.getYellowWeapons()).forEach(i -> fiveStarItems.add(Integer.toString(i))); + } + + response = response.replace("{{FIVE_STARS}}", "[" + String.join(",", fiveStarItems) + "]"); + + // Add 4-star items. + Set fourStarItems = new LinkedHashSet<>(); + + Arrays.stream(banner.getRateUpItems2()).forEach(i -> fourStarItems.add(Integer.toString(i))); + Arrays.stream(manager.getPurpleAvatars()).forEach(i -> fourStarItems.add(Integer.toString(i))); + Arrays.stream(manager.getPurpleWeapons()).forEach(i -> fourStarItems.add(Integer.toString(i))); + + response = response.replace("{{FOUR_STARS}}", "[" + String.join(",", fourStarItems) + "]"); + + // Add 3-star items. + Set threeStarItems = new LinkedHashSet<>(); + Arrays.stream(manager.getBlueWeapons()).forEach(i -> threeStarItems.add(Integer.toString(i))); + response = response.replace("{{THREE_STARS}}", "[" + String.join(",", threeStarItems) + "]"); + + // Done. + res.send(response); + } +} diff --git a/src/main/resources/languages/en-US.json b/src/main/resources/languages/en-US.json index 81e97eed7..42b52d7f5 100644 --- a/src/main/resources/languages/en-US.json +++ b/src/main/resources/languages/en-US.json @@ -352,5 +352,14 @@ "resetshop": { "description": "reset shop" } + }, + "gacha": { + "details": { + "title": "Banner Details", + "available_five_stars": "Available 5-star Items", + "available_four_stars": "Available 4-star Items", + "available_three_stars": "Available 3-star Items", + "template_missing": "data/gacha_details.html is missing." + } } } diff --git a/src/main/resources/languages/pl-PL.json b/src/main/resources/languages/pl-PL.json index aa06723d8..e5eff2d84 100644 --- a/src/main/resources/languages/pl-PL.json +++ b/src/main/resources/languages/pl-PL.json @@ -302,5 +302,14 @@ "resetshop": { "description": "zresetuj sklep" } + }, + "gacha": { + "details": { + "title": "Banner Details", + "available_five_stars": "Available 5-star Items", + "available_four_stars": "Available 4-star Items", + "available_three_stars": "Available 3-star Items", + "template_missing": "data/gacha_details.html is missing." + } } } \ No newline at end of file diff --git a/src/main/resources/languages/zh-CN.json b/src/main/resources/languages/zh-CN.json index 84d4a8c94..ac46370d5 100644 --- a/src/main/resources/languages/zh-CN.json +++ b/src/main/resources/languages/zh-CN.json @@ -349,5 +349,14 @@ "resetshop": { "description": "重置商店刷新时间" } + }, + "gacha": { + "details": { + "title": "Banner Details", + "available_five_stars": "Available 5-star Items", + "available_four_stars": "Available 4-star Items", + "available_three_stars": "Available 3-star Items", + "template_missing": "data/gacha_details.html is missing." + } } } diff --git a/src/main/resources/languages/zh-TW.json b/src/main/resources/languages/zh-TW.json index 8e7a75949..f6ae52c9d 100644 --- a/src/main/resources/languages/zh-TW.json +++ b/src/main/resources/languages/zh-TW.json @@ -302,5 +302,14 @@ "resetshop": { "description": "重置商店時間" } + }, + "gacha": { + "details": { + "title": "Banner Details", + "available_five_stars": "Available 5-star Items", + "available_four_stars": "Available 4-star Items", + "available_three_stars": "Available 3-star Items", + "template_missing": "data/gacha_details.html is missing." + } } } From 5ff8a4514ed797544b46e39adae87bd2541f1cff Mon Sep 17 00:00:00 2001 From: ImmuState Date: Wed, 11 May 2022 11:36:14 -0700 Subject: [PATCH 14/14] Insert language setting based on the player's account. --- data/gacha_details.html | 10 +++++----- .../server/dispatch/http/GachaDetailsHandler.java | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/data/gacha_details.html b/data/gacha_details.html index 16cf7313a..ccd775ef6 100644 --- a/data/gacha_details.html +++ b/data/gacha_details.html @@ -38,7 +38,7 @@

{{TITLE}}

- +

{{AVAILABLE_FIVE_STARS}}


    @@ -81,11 +81,11 @@