diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..fbab9b375 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Grasscutter-Protos"] + path = Grasscutter-Protos + url = https://github.com/Melledy/Grasscutter-Protos diff --git a/Grasscutter-Protos b/Grasscutter-Protos new file mode 160000 index 000000000..0537e9cc4 --- /dev/null +++ b/Grasscutter-Protos @@ -0,0 +1 @@ +Subproject commit 0537e9cc4c7856a7c6f88bbbaa908a80c4ee677e diff --git a/data/Banners.json b/data/Banners.json index 163319f5e..959a8b436 100644 --- a/data/Banners.json +++ b/data/Banners.json @@ -17,39 +17,24 @@ "gachaType": 301, "scheduleId": 903, "bannerType": "EVENT", - "prefabPath": "GachaShowPanel_A076", - "previewPrefabPath": "UI_Tab_GachaShowPanel_A076", - "titlePath": "UI_GACHA_SHOW_PANEL_A076_TITLE", + "prefabPath": "GachaShowPanel_A079", + "previewPrefabPath": "UI_Tab_GachaShowPanel_A079", + "titlePath": "UI_GACHA_SHOW_PANEL_A079_TITLE", "costItem": 223, "beginTime": 0, "endTime": 1924992000, "sortId": 9998, "maxItemType": 1, - "rateUpItems1": [1066], - "rateUpItems2": [1023, 1043, 1064] - }, - { - "gachaType": 400, - "scheduleId": 913, - "bannerType": "EVENT", - "prefabPath": "GachaShowPanel_A077", - "previewPrefabPath": "UI_Tab_GachaShowPanel_A077", - "titlePath": "UI_Tab_GachaShowPanel_A077", - "costItem": 223, - "beginTime": 0, - "endTime": 1924992000, - "sortId": 9998, - "maxItemType": 1, - "rateUpItems1": [1022], - "rateUpItems2": [1023, 1043, 1064] + "rateUpItems1": [1002], + "rateUpItems2": [1053, 1020, 1045] }, { "gachaType": 302, - "scheduleId": 923, + "scheduleId": 913, "bannerType": "WEAPON", - "prefabPath": "GachaShowPanel_A078", - "previewPrefabPath": "UI_Tab_GachaShowPanel_A078", - "titlePath": "UI_GACHA_SHOW_PANEL_A078_TITLE", + "prefabPath": "GachaShowPanel_A080", + "previewPrefabPath": "UI_Tab_GachaShowPanel_A080", + "titlePath": "UI_GACHA_SHOW_PANEL_A080_TITLE", "costItem": 223, "beginTime": 0, "endTime": 1924992000, @@ -58,7 +43,7 @@ "eventChance": 75, "softPity": 80, "hardPity": 80, - "rateUpItems1": [11510, 15503], - "rateUpItems2": [11402, 12403, 13401, 14402, 15405] + "rateUpItems1": [11509, 12504], + "rateUpItems2": [11401, 12402, 13407, 14401, 15401] } -] \ No newline at end of file +] diff --git a/proxy.py b/proxy.py index 1961c90dc..b352af3ac 100644 --- a/proxy.py +++ b/proxy.py @@ -20,7 +20,7 @@ # ## -from mitmproxy import ctx, http +from mitmproxy import http class MlgmXyysd_Genshin_Impact_Proxy: @@ -53,16 +53,13 @@ class MlgmXyysd_Genshin_Impact_Proxy: "hk4e-sdk-os-static.hoyoverse.com", "sdk-os-static.hoyoverse.com", "api-account-os.hoyoverse.com", - "hk4e-sdk-os.hoyoverse.com" + "hk4e-sdk-os.hoyoverse.com", + "overseauspider.yuanshen.com" ] - if flow.request.url.startswith("http://overseauspider.yuanshen.com:8888/log"): - ctx.log.info("Block overseauspider.yuanshen.com") - flow.response = http.HTTPResponse.make(404) - elif flow.request.host in LIST_DOMAINS: - ctx.log.info("Redirect " + flow.request.host) + if flow.request.host in LIST_DOMAINS: flow.request.host = REMOTE_HOST addons = [ MlgmXyysd_Genshin_Impact_Proxy() -] \ No newline at end of file +] diff --git a/run.bat b/run.bat deleted file mode 100644 index 36530cf77..000000000 --- a/run.bat +++ /dev/null @@ -1,7 +0,0 @@ -@echo off - - - -::This will not work if your java is in a different location, plugin as necessary -::this just saves you from changing your PATH -"C:\Program Files\Java\jdk1.8.0_202\bin\java.exe" -jar ./grasscutter.jar \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/commands/Command.java b/src/main/java/emu/grasscutter/commands/Command.java index d1e181e20..d26d7fe29 100644 --- a/src/main/java/emu/grasscutter/commands/Command.java +++ b/src/main/java/emu/grasscutter/commands/Command.java @@ -7,14 +7,16 @@ import java.lang.annotation.RetentionPolicy; public @interface Command { String label() default ""; - String usage() default ""; + String usage() default "No usage specified"; + + String description() default "No description specified"; - String[] aliases() default {""}; + String[] aliases() default {}; Execution execution() default Execution.ALL; String permission() default ""; - + enum Execution { ALL, CONSOLE, diff --git a/src/main/java/emu/grasscutter/commands/CommandMap.java b/src/main/java/emu/grasscutter/commands/CommandMap.java index e9ca260e7..a802551ce 100644 --- a/src/main/java/emu/grasscutter/commands/CommandMap.java +++ b/src/main/java/emu/grasscutter/commands/CommandMap.java @@ -68,10 +68,12 @@ public final class CommandMap { * Returns a list of all registered commands. * @return All command handlers as a list. */ - public List getHandlers() { + public List getHandlersAsList() { return new LinkedList<>(this.commands.values()); } + public HashMap getHandlers() { return new LinkedHashMap<>(this.commands); } + /** * Returns a handler by label/alias. * @param label The command label. @@ -111,8 +113,7 @@ public final class CommandMap { if(player != null) { String permissionNode = this.annotations.get(label).permission(); Account account = player.getAccount(); - List permissions = account.getPermissions(); - if(!permissions.contains("*") && !permissions.contains(permissionNode)) { + if(permissionNode != "" && !account.hasPermission(permissionNode)) { CommandHandler.sendMessage(player, "You do not have permission to run this command."); return; } } diff --git a/src/main/java/emu/grasscutter/commands/PlayerCommands.java b/src/main/java/emu/grasscutter/commands/PlayerCommands.java index ea2cbfbd6..4b7a2b8e1 100644 --- a/src/main/java/emu/grasscutter/commands/PlayerCommands.java +++ b/src/main/java/emu/grasscutter/commands/PlayerCommands.java @@ -581,4 +581,4 @@ public final class PlayerCommands { player.getSession().close(); } } -} \ No newline at end of file +} diff --git a/src/main/java/emu/grasscutter/commands/ServerCommands.java b/src/main/java/emu/grasscutter/commands/ServerCommands.java index 0faf0e7f9..edeac6ca3 100644 --- a/src/main/java/emu/grasscutter/commands/ServerCommands.java +++ b/src/main/java/emu/grasscutter/commands/ServerCommands.java @@ -5,14 +5,13 @@ import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.game.Account; import emu.grasscutter.game.GenshinPlayer; -import java.util.List; -import java.util.stream.Collectors; +import java.util.*; /** * A container for server-related commands. */ public final class ServerCommands { - @Command(label = "reload", usage = "Usage: reload") + @Command(label = "reload", usage = "reload", description = "Reload server config", permission = "server.reload") public static class ReloadCommand implements CommandHandler { @Override @@ -25,12 +24,87 @@ public final class ServerCommands { @Override public void execute(GenshinPlayer player, List args) { + CommandHandler.sendMessage(player, "Reloading config."); this.execute(args); + CommandHandler.sendMessage(player, "Reload complete."); } } - + + @Command(label = "kick", usage = "kick ", description = "Kicks the specified player from the server (WIP)", permission = "server.kick") + public static class KickCommand implements CommandHandler { + @Override + public void execute(List args) { + this.execute(null, args); + } + + @Override + public void execute(GenshinPlayer player, List args) { + int target = Integer.parseInt(args.get(0)); + + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); + if(targetPlayer == null) { + CommandHandler.sendMessage(player, "Player not found."); + return; + } + + if(player != null) { + CommandHandler.sendMessage(null, String.format("Player [%s:%s] has kicked player [%s:%s]", player.getAccount().getPlayerId(), player.getAccount().getUsername(), target, targetPlayer.getAccount().getUsername())); + } + CommandHandler.sendMessage(player, String.format("Kicking player [%s:%s]", target, targetPlayer.getAccount().getUsername())); + + targetPlayer.getSession().close(); + } + } + + @Command(label = "stop", usage = "stop", description = "Stops the server", permission = "server.stop") + public static class StopCommand implements CommandHandler { + @Override + public void execute(List args) { + Grasscutter.getLogger().info("Server shutting down..."); + for (GenshinPlayer p : Grasscutter.getGameServer().getPlayers().values()) { + p.dropMessage("Server shutting down..."); + } + + System.exit(1); + } + } + + @Command(label = "broadcast", aliases = {"b"}, + usage = "broadcast ", description = "Sends a message to all the players", permission = "server.broadcast") + public static class BroadcastCommand implements CommandHandler { + @Override + public void execute(List args) { + if(args.size() < 1) { + CommandHandler.sendMessage(null, "Usage: broadcast "); return; + } + + String message = String.join(" ", args.subList(0, args.size())); + + for (GenshinPlayer p : Grasscutter.getGameServer().getPlayers().values()) { + p.dropMessage(message); + } + + CommandHandler.sendMessage(null, "Message sent."); + } + + @Override + public void execute(GenshinPlayer player, List args) { + if(args.size() < 1) { + CommandHandler.sendMessage(player, "Usage: broadcast "); return; + } + + String message = String.join(" ", args.subList(0, args.size())); + + for (GenshinPlayer p : Grasscutter.getGameServer().getPlayers().values()) { + p.dropMessage(message); + } + + CommandHandler.sendMessage(player, "Message sent."); + } + } + @Command(label = "sendmessage", aliases = {"sendmsg", "msg"}, - usage = "Usage: sendmessage ") + usage = "sendmessage ", description = "Sends a message to a player") public static class SendMessageCommand implements CommandHandler { @Override @@ -81,8 +155,8 @@ public final class ServerCommands { } @Command(label = "account", - usage = "Usage: account [uid]", - execution = Command.Execution.CONSOLE) + usage = "account [uid]", + description = "Modify user accounts", execution = Command.Execution.CONSOLE) public static class AccountCommand implements CommandHandler { @Override @@ -126,8 +200,8 @@ public final class ServerCommands { } @Command(label = "permission", - usage = "Usage: permission ", - execution = Command.Execution.CONSOLE) + usage = "permission ", + description = "Grants or removes a permission for a user", permission = "*") public static class PermissionCommand implements CommandHandler { @Override @@ -166,50 +240,93 @@ public final class ServerCommands { } @Command(label = "help", - usage = "Usage: help [command]") + usage = "help [command]", description = "Sends the help message or shows information about a specified command") public static class HelpCommand implements CommandHandler { @Override public void execute(List args) { - List handlers = CommandMap.getInstance().getHandlers(); - List annotations = handlers.stream() - .map(handler -> handler.getClass().getAnnotation(Command.class)) - .collect(Collectors.toList()); - - if(args.size() < 1) { - StringBuilder builder = new StringBuilder("Available commands:\n"); - annotations.forEach(annotation -> builder.append(annotation.usage()).append("\n")); - CommandHandler.sendMessage(null, builder.toString()); - } else { - String command = args.get(0); - CommandHandler handler = CommandMap.getInstance().getHandler(command); - if(handler == null) { - CommandHandler.sendMessage(null, "Command not found."); return; - } - - Command annotation = handler.getClass().getAnnotation(Command.class); - CommandHandler.sendMessage(null, annotation.usage()); - } + this.execute(null, args); } @Override public void execute(GenshinPlayer player, List args) { - List handlers = CommandMap.getInstance().getHandlers(); - List annotations = handlers.stream() - .map(handler -> handler.getClass().getAnnotation(Command.class)) - .collect(Collectors.toList()); - if(args.size() < 1) { - annotations.forEach(annotation -> player.dropMessage(annotation.usage())); + HashMap handlers = CommandMap.getInstance().getHandlers(); + List annotations = new ArrayList<>(); + for(String key : handlers.keySet()) { + Command annotation = handlers.get(key).getClass().getAnnotation(Command.class); + + if(!Arrays.asList(annotation.aliases()).contains(key)) { + if(player != null && !Objects.equals(annotation.permission(), "") && !player.getAccount().hasPermission(annotation.permission())) continue; + annotations.add(annotation); + } + } + + + SendAllHelpMessage(player, annotations); } else { String command = args.get(0); CommandHandler handler = CommandMap.getInstance().getHandler(command); + StringBuilder builder = new StringBuilder(player == null ? "\nHelp - " : "Help - ").append(command).append(": \n"); if(handler == null) { - CommandHandler.sendMessage(player, "Command not found."); return; + builder.append("No command found."); + } else { + Command annotation = handler.getClass().getAnnotation(Command.class); + + builder.append(" ").append(annotation.description()).append("\n"); + builder.append(" Usage: ").append(annotation.usage()); + if(annotation.aliases().length >= 1) { + builder.append("\n").append(" Aliases: "); + for (String alias : annotation.aliases()) { + builder.append(alias).append(" "); + } + } + if(player != null && !Objects.equals(annotation.permission(), "") && !player.getAccount().hasPermission(annotation.permission())) { + builder.append("\n Warning: You do not have permission to run this command."); + } } - Command annotation = handler.getClass().getAnnotation(Command.class); - CommandHandler.sendMessage(player, annotation.usage()); + CommandHandler.sendMessage(player, builder.toString()); + } + } + + void SendAllHelpMessage(GenshinPlayer player, List annotations) { + if(player == null) { + StringBuilder builder = new StringBuilder("\nAvailable commands:\n"); + annotations.forEach(annotation -> { + if (annotation.execution() != Command.Execution.PLAYER) { + builder.append(annotation.label()).append("\n"); + builder.append(" ").append(annotation.description()).append("\n"); + builder.append(" Usage: ").append(annotation.usage()); + if (annotation.aliases().length >= 1) { + builder.append("\n").append(" Aliases: "); + for (String alias : annotation.aliases()) { + builder.append(alias).append(" "); + } + } + + builder.append("\n"); + } + }); + + CommandHandler.sendMessage(null, builder.toString()); + } else { + CommandHandler.sendMessage(player, "Available commands:"); + annotations.forEach(annotation -> { + if (annotation.execution() != Command.Execution.CONSOLE) { + StringBuilder builder = new StringBuilder(annotation.label()).append("\n"); + builder.append(" ").append(annotation.description()).append("\n"); + builder.append(" Usage: ").append(annotation.usage()); + if (annotation.aliases().length >= 1) { + builder.append("\n").append(" Aliases: "); + for (String alias : annotation.aliases()) { + builder.append(alias).append(" "); + } + } + + CommandHandler.sendMessage(player, builder.toString()); + } + }); } } } diff --git a/src/main/java/emu/grasscutter/data/ResourceLoader.java b/src/main/java/emu/grasscutter/data/ResourceLoader.java index f5b9f4f72..f50067c65 100644 --- a/src/main/java/emu/grasscutter/data/ResourceLoader.java +++ b/src/main/java/emu/grasscutter/data/ResourceLoader.java @@ -197,7 +197,7 @@ public class ResourceLoader { } else { Map map = new TreeMap<>(); java.lang.reflect.Type type = new TypeToken>() {}.getType(); - String[] folderNames = {"BinOutput\\Talent\\EquipTalents\\", "BinOutput\\Talent\\AvatarTalents\\"}; + String[] folderNames = {"BinOutput/Talent/EquipTalents/", "BinOutput/Talent/AvatarTalents/"}; for (String name : folderNames) { File folder = new File(Utils.toFilePath(Grasscutter.getConfig().RESOURCE_FOLDER + name)); diff --git a/src/main/java/emu/grasscutter/game/Account.java b/src/main/java/emu/grasscutter/game/Account.java index dffbb0d86..2eeeed3b1 100644 --- a/src/main/java/emu/grasscutter/game/Account.java +++ b/src/main/java/emu/grasscutter/game/Account.java @@ -105,6 +105,10 @@ public class Account { if(this.permissions.contains(permission)) return false; this.permissions.add(permission); return true; } + + public boolean hasPermission(String permission) { + return this.permissions.contains(permission) || this.permissions.contains("*") ? true : false; + } public boolean removePermission(String permission) { return this.permissions.remove(permission); diff --git a/src/main/java/emu/grasscutter/game/managers/InventoryManager.java b/src/main/java/emu/grasscutter/game/managers/InventoryManager.java index 3d56dfad2..935652d77 100644 --- a/src/main/java/emu/grasscutter/game/managers/InventoryManager.java +++ b/src/main/java/emu/grasscutter/game/managers/InventoryManager.java @@ -471,7 +471,7 @@ public class InventoryManager { } // Consume weapon - player.getInventory().removeItem(feed); + player.getInventory().removeItem(feed, 1); // Get weapon.setRefinement(targetRefineLevel); @@ -804,6 +804,12 @@ public class InventoryManager { // Get talent int currentTalentLevel = avatar.getCoreProudSkillLevel(); int nextTalentId = ((avatar.getAvatarId() % 10000000) * 10) + currentTalentLevel + 1; + + if (avatar.getAvatarId() == 10000006) { + // Lisa is special in that her talentId starts with 4 instead of 6. + nextTalentId = 40 + currentTalentLevel + 1; + } + AvatarTalentData talentData = GenshinData.getAvatarTalentDataMap().get(nextTalentId); if (talentData == null) { diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java index da90caa42..0ac4c4fc8 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -389,6 +389,10 @@ public final class DispatchServer { "/sdk/upload", new DispatchHttpJsonHandler("{\"code\":0}") ); + server.createContext( // /perf/config/verify?device_id=xxx&platform=x&name=xxx + "/perf/config/verify", + new DispatchHttpJsonHandler("{\"code\":0}") + ); // Start server server.start(); Grasscutter.getLogger().info("Dispatch server started on port " + getAddress().getPort()); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerEnterSceneNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerEnterSceneNotify.java index 69ca9cb5f..e2db99153 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerEnterSceneNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerEnterSceneNotify.java @@ -59,6 +59,7 @@ public class PacketPlayerEnterSceneNotify extends GenshinPacket { .setEnterReason(reason.getValue()) .addSceneTagIdList(102) .addSceneTagIdList(107) + .addSceneTagIdList(109) .addSceneTagIdList(113) .addSceneTagIdList(117) .setUnk1(1) diff --git a/src/main/java/emu/grasscutter/tools/Tools.java b/src/main/java/emu/grasscutter/tools/Tools.java index 2e4a7e153..6f6466773 100644 --- a/src/main/java/emu/grasscutter/tools/Tools.java +++ b/src/main/java/emu/grasscutter/tools/Tools.java @@ -21,6 +21,7 @@ import emu.grasscutter.data.def.AvatarData; import emu.grasscutter.data.def.ItemData; import emu.grasscutter.data.def.MonsterData; import emu.grasscutter.data.def.SceneData; +import emu.grasscutter.utils.Utils; public final class Tools { @@ -29,7 +30,7 @@ public final class Tools { ResourceLoader.loadResources(); Map map; - try (FileReader fileReader = new FileReader(Grasscutter.getConfig().RESOURCE_FOLDER + "TextMapEN.json")) { + try (FileReader fileReader = new FileReader(Utils.toFilePath(Grasscutter.getConfig().RESOURCE_FOLDER + "TextMap/TextMapEN.json"))) { map = Grasscutter.getGsonFactory().fromJson(fileReader, new TypeToken>() {}.getType()); }