diff --git a/README.md b/README.md index e40ffe12a..623baf68b 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,7 @@ There is a dummy user named "Server" in every player's friends list that you can | drop | drop [amount] | server.drop | Client only | Drops an item around you. | `d` `dropitem` | | give | give [player] [amount] [level] [finement] | player.give | Both side | Gives item(s) to you or the specified player. (finement option only weapon.) | `g` `item` `giveitem` | | givechar | givechar \ \ | player.givechar | Both side | Gives the player a specified character. | givec | -| giveart | giveart [player] \ \ [\[,\]]... [level] | player.giveart | Both side | Gives the player a specified reliquary. | givea | +| giveart | giveart [player] \ \ [\[,\]]... [level] | player.giveart | Both side | Gives the player a specified artifact. | gart | | giveall | giveall [uid] [amount] | player.giveall | Both side | Gives all items. | givea | | godmode | godmode [uid] | player.godmode | Client only | Prevents you from taking damage. | | | heal | heal | player.heal | Client only | Heals all characters in your current team. | h | @@ -135,7 +135,7 @@ There is a dummy user named "Server" in every player's friends list that you can | spawn | spanw [level] [amount] | server.spawn | Client only | Spawns an entity near you | | | stop | stop | server.stop | Both side | Stops the server | | | talent | talent \ \ | player.settalent | Client only | Sets talent level for your currently selected character | | -| teleport | teleport \ \ \ | player.teleport | Client only | Change the player's position. | tp | +| teleport | teleport [@playerUid] \ \ \ [sceneId] | player.teleport | Both side | Change the player's position. | tp | | tpall | | player.tpall | Client only | Teleports all players in your world to your position | | | weather | weather \ \ | player.weather | Client only | Changes the weather | w | @@ -144,9 +144,12 @@ There is a dummy user named "Server" in every player's friends list that you can When you want to teleport to somewhere, use the ingame marking function on Map, click Confirm. You will see your character falling from a very high destination, exact location that you marked. +You can also specify a set Y coordinate by renaming the map marker. + # Quick Troubleshooting * If compiling wasn't successful, please check your JDK installation (JDK 17 and validated JDK's bin PATH variable) * My client doesn't connect, doesn't login, 4206, etc... - Mostly your proxy daemon setup is *the issue*, if using Fiddler make sure it running on another port except 8888 -* Startup sequence: Mongodb -> Grasscutter -> Proxy daemon (mitmdump, fiddler, etc.) -> Client + +* Startup sequence: Mongodb > Grasscutter > Proxy daemon (mitmdump, fiddler, etc.) > Game diff --git a/README_zh-CN.md b/README_zh-CN.md index 63a328f9f..6216cf3eb 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -105,7 +105,7 @@ chmod +x gradlew 在每个玩家的朋友列表中都有一个名为“服务器”的虚拟用户,你可以通过发送消息来使用命令。命令也适用于其他聊天室,例如私人/团队聊天。 要在游戏中使用命令,需要添加 `/` 或 `!` 前缀,如 `/pos` -| 命令 | 用法 | 权限节点 | 可用性 | 注释 | 别名 | +| 命令 | 用法 | 权限节点 | 可用性 | 注释 | 别名 | | -------------- | -------------------------------------------- | ------------------------- | -------- | ------------------------------------------ | ----------------------------------------------- | | account | account <用户名> [uid] | | 仅服务端 | 通过指定用户名和uid增删账户 | | | broadcast | broadcast <消息内容> | server.broadcast | 均可使用 | 给所有玩家发送公告 | b | @@ -116,7 +116,7 @@ chmod +x gradlew | drop | drop <物品ID\|物品名称> [数量] | server.drop | 仅客户端 | 在指定玩家周围掉落指定物品 | `d` `dropitem` | | give | give [uid] <物品ID\|物品名称> [数量] [等级] [精炼等级] | | | 给予指定玩家一定数量及等级的物品 (精炼等级仅适用于武器) | `g` `item` `giveitem` | | givechar | givechar \ <角色ID> [等级] | player.givechar | 均可使用 | 给予指定玩家对应角色 | givec | -| giveart | giveart [uid] \<圣遗物ID> \<主属性ID> [\<副属性ID>[,<次数>]]... [等级] | player.giveart | 均可使用 | 给予玩家指定属性的圣遗物 | givea | +| giveart | giveart [uid] \<圣遗物ID> \<主属性ID> [\<副属性ID>[,<次数>]]... [等级] | player.giveart | 均可使用 | 给予玩家指定属性的圣遗物 | gart | | giveall | giveall [uid] [数量] | player.giveall | 均可使用 | 给予指定玩家全部物品 | givea | | godmode | godmode [uid] | player.godmode | 仅客户端 | 保护你不受到任何伤害(依然会被击退) | | | heal | heal | player.heal | 仅客户端 | 治疗队伍中所有角色 | h | @@ -136,7 +136,7 @@ chmod +x gradlew | spawn | spanw <实体ID\|实体名称> [等级] [数量] | server.spawn | 仅客户端 | 在你周围生成实体 | | | stop | stop | server.stop | 均可使用 | 停止服务器 | | | talent | talent <天赋ID> <等级> | player.settalent | 仅客户端 | 设置当前角色的天赋等级 | | -| teleport | teleport \ \ \ | player.teleport | 仅客户端 | 传送玩家到指定坐标 | tp | +| teleport | teleport [@playerUid] \ \ \ [sceneId] | player.teleport | 均可使用 | 传送玩家到指定坐标 | tp | | tpall | | player.tpall | 仅客户端 | 传送多人世界中所有的玩家到自身地点 | | | weather | weather <天气ID> <气候ID> | player.weather | 仅客户端 | 改变天气 | w | diff --git a/proto/GetFriendShowAvatarInfoReq.proto b/proto/GetFriendShowAvatarInfoReq.proto new file mode 100644 index 000000000..33c888d42 --- /dev/null +++ b/proto/GetFriendShowAvatarInfoReq.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +option java_package = "emu.grasscutter.net.proto"; + +message GetFriendShowAvatarInfoReq { + enum CmdId { + option allow_alias = true; + ENET_CHANNEL_ID = 0; + NONE = 0; + ENET_IS_RELIABLE = 1; + IS_ALLOW_CLIENT = 1; + CMD_ID = 4007; + } + + uint32 uid = 1; +} diff --git a/proto/GetFriendShowAvatarInfoRsp.proto b/proto/GetFriendShowAvatarInfoRsp.proto new file mode 100644 index 000000000..abd7d2aae --- /dev/null +++ b/proto/GetFriendShowAvatarInfoRsp.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +option java_package = "emu.grasscutter.net.proto"; + +import "ShowAvatarInfo.proto"; + +message GetFriendShowAvatarInfoRsp { + enum CmdId { + option allow_alias = true; + NONE = 0; + ENET_CHANNEL_ID = 0; + ENET_IS_RELIABLE = 1; + CMD_ID = 4008; + } + + int32 retcode = 1; + uint32 uid = 2; + repeated ShowAvatarInfo show_avatar_info_list = 3; +} diff --git a/proto/ShowAvatarInfo.proto b/proto/ShowAvatarInfo.proto new file mode 100644 index 000000000..61be51d80 --- /dev/null +++ b/proto/ShowAvatarInfo.proto @@ -0,0 +1,24 @@ +syntax = "proto3"; + +option java_package = "emu.grasscutter.net.proto"; + +import "AvatarFetterInfo.proto"; +import "AvatarExcelInfo.proto"; +import "PropValue.proto"; +import "ShowEquip.proto"; + +message ShowAvatarInfo { + uint32 avatar_id = 1; + map prop_map = 2; + repeated uint32 talent_id_list = 3; + map fight_prop_map = 4; + uint32 skill_depot_id = 5; + uint32 core_proud_skill_level = 6; + repeated uint32 inherent_proud_skill_list = 7; + map skill_level_map = 8; + map proud_skill_extra_level_map = 9; + repeated ShowEquip equip_list = 10; + AvatarFetterInfo fetter_info = 11; + uint32 costume_id = 12; + AvatarExcelInfo excel_info = 13; +} diff --git a/proto/ShowEquip.proto b/proto/ShowEquip.proto new file mode 100644 index 000000000..a35882de1 --- /dev/null +++ b/proto/ShowEquip.proto @@ -0,0 +1,14 @@ +syntax = "proto3"; + +option java_package = "emu.grasscutter.net.proto"; + +import "Reliquary.proto"; +import "Weapon.proto"; + +message ShowEquip { + oneof Detail { + Reliquary reliquary = 2; + Weapon weapon = 3; + } + uint32 item_id = 1; +} diff --git a/src/main/java/emu/grasscutter/Config.java b/src/main/java/emu/grasscutter/Config.java index 22e32947c..03c6fb1f8 100644 --- a/src/main/java/emu/grasscutter/Config.java +++ b/src/main/java/emu/grasscutter/Config.java @@ -1,5 +1,7 @@ package emu.grasscutter; +import emu.grasscutter.game.mail.Mail; + public final class Config { public String DatabaseUrl = "mongodb://localhost:27017"; @@ -71,10 +73,15 @@ public final class Config { public int MaxAvatarsInTeamMultiplayer = 4; public int MaxEntityLimit = 1000; // Max entity limit per world. // TODO: Enforce later. public boolean WatchGacha = false; + public String ServerNickname = "Server"; + public int ServerAvatarId = 10000007; public int[] WelcomeEmotes = {2007, 1002, 4010}; public String WelcomeMotd = "Welcome to Grasscutter emu"; public String WelcomeMailContent = "Hi there!\r\nFirst of all, welcome to Grasscutter. If you have any issues, please let us know so that Lawnmower can help you! \r\n\r\nCheck out our:\r\n "; - public int[] WelcomeMailItems = {13509}; + public Mail.MailItem[] WelcomeMailItems = { + new Mail.MailItem(13509, 1, 1), + new Mail.MailItem(201, 10000, 1), + }; public boolean EnableOfficialShop = true; diff --git a/src/main/java/emu/grasscutter/GameConstants.java b/src/main/java/emu/grasscutter/GameConstants.java index dc07c32e1..39b28b736 100644 --- a/src/main/java/emu/grasscutter/GameConstants.java +++ b/src/main/java/emu/grasscutter/GameConstants.java @@ -11,6 +11,8 @@ public final class GameConstants { public static final int MAX_TEAMS = 4; public static final int MAIN_CHARACTER_MALE = 10000005; public static final int MAIN_CHARACTER_FEMALE = 10000007; + public static final String SERVER_AVATAR_NAME = Grasscutter.getConfig().getGameServerOptions().ServerNickname; + public static final int SERVER_AVATAR_ID = Grasscutter.getConfig().getGameServerOptions().ServerAvatarId; public static final Position START_POSITION = new Position(2747, 194, -1719); public static final int MAX_FRIENDS = 45; diff --git a/src/main/java/emu/grasscutter/Grasscutter.java b/src/main/java/emu/grasscutter/Grasscutter.java index f7669d30b..60d042b11 100644 --- a/src/main/java/emu/grasscutter/Grasscutter.java +++ b/src/main/java/emu/grasscutter/Grasscutter.java @@ -6,6 +6,7 @@ import java.io.FileReader; import java.io.FileWriter; import java.io.InputStreamReader; import java.net.InetSocketAddress; +import java.util.Calendar; import emu.grasscutter.command.CommandMap; import emu.grasscutter.plugin.PluginManager; @@ -32,6 +33,8 @@ public final class Grasscutter { private static final Gson gson = new GsonBuilder().setPrettyPrinting().create(); private static final File configFile = new File("./config.json"); + private static int day; // Current day of week + public static RunMode MODE = RunMode.BOTH; private static DispatchServer dispatchServer; private static GameServer gameServer; @@ -67,8 +70,10 @@ public final class Grasscutter { Grasscutter.getLogger().info("Starting Grasscutter..."); // Load all resources. + Grasscutter.updateDayOfWeek(); ResourceLoader.loadAll(); ScriptLoader.init(); + // Database DatabaseManager.initialize(); @@ -179,4 +184,13 @@ public final class Grasscutter { public static PluginManager getPluginManager() { return pluginManager; } + + public static void updateDayOfWeek() { + Calendar calendar = Calendar.getInstance(); + day = calendar.get(Calendar.DAY_OF_WEEK); + } + + public static int getCurrentDayOfWeek() { + return day; + } } diff --git a/src/main/java/emu/grasscutter/command/commands/GiveAllCommand.java b/src/main/java/emu/grasscutter/command/commands/GiveAllCommand.java index e9d26d2cd..e78b381e2 100644 --- a/src/main/java/emu/grasscutter/command/commands/GiveAllCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/GiveAllCommand.java @@ -98,14 +98,14 @@ public final class GiveAllCommand implements CommandHandler { if (isTestItem(itemdata.getId())) continue; if (itemdata.isEquip()) { - for (int i = 0; i < 5; ++i) { - GameItem item = new GameItem(itemdata); - if (itemdata.getItemType() == ItemType.ITEM_WEAPON) { + if (itemdata.getItemType() == ItemType.ITEM_WEAPON) { + for (int i = 0; i < 5; ++i) { + GameItem item = new GameItem(itemdata); item.setLevel(90); item.setPromoteLevel(6); item.setRefinement(4); + itemList.add(item); } - itemList.add(item); } } else { diff --git a/src/main/java/emu/grasscutter/command/commands/GiveArtifactCommand.java b/src/main/java/emu/grasscutter/command/commands/GiveArtifactCommand.java index e33a287e3..382c55ec2 100644 --- a/src/main/java/emu/grasscutter/command/commands/GiveArtifactCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/GiveArtifactCommand.java @@ -14,13 +14,13 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -@Command(label = "giveart", usage = "giveart [player] [[,]]... [level]", description = "Gives the player a specified reliquary", aliases = {"givea"}, permission = "player.giveart") +@Command(label = "giveart", usage = "giveart [player] [[,]]... [level]", description = "Gives the player a specified artifact", aliases = {"gart"}, permission = "player.giveart") public final class GiveArtifactCommand implements CommandHandler { @Override public void execute(Player sender, List args) { - int size = args.size(), target, itemId, mainPropId, level; + int size = args.size(), target, itemId, mainPropId, level = 1; ArrayList appendPropIdList = new ArrayList<>(); - String msg = "Usage: giveart|givea [player] [[,]]... [level]"; + String msg = "Usage: giveart|gart [player] [[,]]... [level]"; if (sender == null && size < 2) { CommandHandler.sendMessage(null, msg); @@ -29,9 +29,14 @@ public final class GiveArtifactCommand implements CommandHandler { if (size >= 2) { try { - level = Integer.parseInt(args.get(size - 1)); - if (level <= 21) size--; - else level = 1; + try { + int last = Integer.parseInt(args.get(size - 1)); + if (last >= 1 && last <= 21) { + level = last; + size--; + } + } catch (NumberFormatException ignored) { + } target = Integer.parseInt(args.get(0)); int fromIdx; if (Grasscutter.getGameServer().getPlayerByUid(target) == null && sender != null) { @@ -79,6 +84,7 @@ public final class GiveArtifactCommand implements CommandHandler { GameItem item = new GameItem(itemData); item.setLevel(level); item.setMainPropId(mainPropId); + item.getAppendPropIdList().clear(); item.getAppendPropIdList().addAll(appendPropIdList); targetPlayer.getInventory().addItem(item, ActionReason.SubfieldDrop); diff --git a/src/main/java/emu/grasscutter/command/commands/GiveCommand.java b/src/main/java/emu/grasscutter/command/commands/GiveCommand.java index 03147a44b..52670efa7 100644 --- a/src/main/java/emu/grasscutter/command/commands/GiveCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/GiveCommand.java @@ -163,20 +163,28 @@ public final class GiveCommand implements CommandHandler { List items = new LinkedList<>(); for (int i = 0; i < amount; i++) { GameItem item = new GameItem(itemData); + if (item.isEquipped()) { + // check item max level + if (item.getItemType() == ItemType.ITEM_WEAPON) { + if (lvl > 90) lvl = 90; + } else { + if (lvl > 21) lvl = 21; + } + } item.setCount(amount); item.setLevel(lvl); - if (lvl > 20 && lvl < 40) { - item.setPromoteLevel(1); - } else if (lvl > 40 && lvl <= 50) { - item.setPromoteLevel(2); - } else if (lvl > 50 && lvl <= 60) { - item.setPromoteLevel(3); - } else if (lvl > 60 && lvl <= 70) { - item.setPromoteLevel(4); - } else if (lvl > 70 && lvl <= 80) { - item.setPromoteLevel(5); - } else if (lvl > 80 && lvl <= 90) { + if (lvl > 80) { item.setPromoteLevel(6); + } else if (lvl > 70) { + item.setPromoteLevel(5); + } else if (lvl > 60) { + item.setPromoteLevel(4); + } else if (lvl > 50) { + item.setPromoteLevel(3); + } else if (lvl > 40) { + item.setPromoteLevel(2); + } else if (lvl > 20) { + item.setPromoteLevel(1); } if (item.getItemType() == ItemType.ITEM_WEAPON) { if (refinement > 0) { diff --git a/src/main/java/emu/grasscutter/command/commands/TeleportCommand.java b/src/main/java/emu/grasscutter/command/commands/TeleportCommand.java index c478118e3..238dccfa1 100644 --- a/src/main/java/emu/grasscutter/command/commands/TeleportCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/TeleportCommand.java @@ -1,5 +1,6 @@ package emu.grasscutter.command.commands; +import emu.grasscutter.Grasscutter; import emu.grasscutter.command.Command; import emu.grasscutter.command.CommandHandler; import emu.grasscutter.game.player.Player; @@ -7,21 +8,39 @@ import emu.grasscutter.utils.Position; import java.util.List; -@Command(label = "teleport", usage = "teleport ", aliases = {"tp"}, +@Command(label = "teleport", usage = "teleport [@player id] [scene id]", aliases = {"tp"}, description = "Change the player's position.", permission = "player.teleport") public final class TeleportCommand implements CommandHandler { @Override public void execute(Player sender, List args) { - if (sender == null) { - CommandHandler.sendMessage(null, "Run this command in-game."); + int target; + if (args.size() < (sender == null ? 4 : 3)) { + CommandHandler.sendMessage(sender, sender == null ? "Usage: /tp @ [scene id]" : + "Usage: /tp [@] [scene id]"); return; } + if (args.get(0).startsWith("@")) { + try { + target = Integer.parseInt(args.get(0).substring(1)); + } catch (NumberFormatException e) { + CommandHandler.sendMessage(sender, "Invalid player id."); + return; + } + } else { + if (sender == null) { + CommandHandler.sendMessage(null, "You must specify a player id."); + return; + } + target = sender.getUid(); + } - if (args.size() < 3){ - CommandHandler.sendMessage(sender, "Usage: /tp [scene id]"); + Player targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); + if (targetPlayer == null) { + CommandHandler.sendMessage(sender, "Player not found or offline."); return; } + args = args.subList(args.get(0).startsWith("@") ? 1 : 0, args.size()); try { float x = 0f; @@ -29,39 +48,41 @@ public final class TeleportCommand implements CommandHandler { float z = 0f; if (args.get(0).contains("~")) { if (args.get(0).equals("~")) { - x = sender.getPos().getX(); + x = targetPlayer.getPos().getX(); } else { - x = Float.parseFloat(args.get(0).replace("~", "")) + sender.getPos().getX(); + x = Float.parseFloat(args.get(0).replace("~", "")) + targetPlayer.getPos().getX(); } } else { x = Float.parseFloat(args.get(0)); } if (args.get(1).contains("~")) { if (args.get(1).equals("~")) { - y = sender.getPos().getY(); + y = targetPlayer.getPos().getY(); } else { - y = Float.parseFloat(args.get(1).replace("~", "")) + sender.getPos().getY(); + y = Float.parseFloat(args.get(1).replace("~", "")) + targetPlayer.getPos().getY(); } } else { y = Float.parseFloat(args.get(1)); } if (args.get(2).contains("~")) { if (args.get(2).equals("~")) { - z = sender.getPos().getZ(); + z = targetPlayer.getPos().getZ(); } else { - z = Float.parseFloat(args.get(2).replace("~", "")) + sender.getPos().getZ(); + z = Float.parseFloat(args.get(2).replace("~", "")) + targetPlayer.getPos().getZ(); } } else { z = Float.parseFloat(args.get(2)); } - int sceneId = sender.getSceneId(); + int sceneId = targetPlayer.getSceneId(); if (args.size() == 4){ sceneId = Integer.parseInt(args.get(3)); } - Position target = new Position(x, y, z); - boolean result = sender.getWorld().transferPlayerToScene(sender, sceneId, target); + Position target_pos = new Position(x, y, z); + boolean result = targetPlayer.getWorld().transferPlayerToScene(targetPlayer, sceneId, target_pos); if (!result) { CommandHandler.sendMessage(sender, "Invalid position."); + } else { + CommandHandler.sendMessage(sender, "Teleported " + targetPlayer.getNickname() + " to " + x + "," + y + "," + z + " in scene " + sceneId); } } catch (NumberFormatException ignored) { CommandHandler.sendMessage(sender, "Invalid position."); diff --git a/src/main/java/emu/grasscutter/data/GameData.java b/src/main/java/emu/grasscutter/data/GameData.java index 45fad21a4..c187a1819 100644 --- a/src/main/java/emu/grasscutter/data/GameData.java +++ b/src/main/java/emu/grasscutter/data/GameData.java @@ -15,6 +15,8 @@ import emu.grasscutter.data.def.*; import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; public class GameData { // BinOutputs @@ -61,12 +63,14 @@ public class GameData { private static final Int2ObjectMap fetterCharacterCardDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap rewardDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap worldLevelDataMap = new Int2ObjectOpenHashMap<>(); + private static final Int2ObjectMap dailyDungeonDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap dungeonDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap shopGoodsDataMap = new Int2ObjectOpenHashMap<>(); // Cache private static Map> fetters = new HashMap<>(); private static Map> shopGoods = new HashMap<>(); + private static final IntList scenePointIdList = new IntArrayList(); public static char EJWOA = 's'; @@ -280,6 +284,10 @@ public class GameData { return dungeonDataMap; } + public static Int2ObjectMap getDailyDungeonDataMap() { + return dailyDungeonDataMap; + } + public static Map> getShopGoodsDataEntries() { if (shopGoods.isEmpty()) { shopGoodsDataMap.forEach((k, v) -> { @@ -291,4 +299,8 @@ public class GameData { return shopGoods; } + + public static IntList getScenePointIdList() { + return scenePointIdList; + } } diff --git a/src/main/java/emu/grasscutter/data/ResourceLoader.java b/src/main/java/emu/grasscutter/data/ResourceLoader.java index 6b48645d8..180080e51 100644 --- a/src/main/java/emu/grasscutter/data/ResourceLoader.java +++ b/src/main/java/emu/grasscutter/data/ResourceLoader.java @@ -48,11 +48,12 @@ public class ResourceLoader { loadOpenConfig(); // Load resources loadResources(); - loadScenePoints(); // Process into depots GameDepot.load(); // Load spawn data loadSpawnData(); + // Load scene points - must be done AFTER resources are loaded + loadScenePoints(); // Custom - TODO move this somewhere else try { GameData.getAvatarSkillDepotDataMap().get(504).setAbilities( @@ -168,6 +169,9 @@ public class ResourceLoader { ScenePointEntry sl = new ScenePointEntry(sceneId + "_" + entry.getKey(), pointData); scenePointList.add(sl); + GameData.getScenePointIdList().add(pointData.getId()); + + pointData.updateDailyDungeon(); } for (ScenePointEntry entry : scenePointList) { diff --git a/src/main/java/emu/grasscutter/data/common/PointData.java b/src/main/java/emu/grasscutter/data/common/PointData.java index 147eee81a..fa3891d7c 100644 --- a/src/main/java/emu/grasscutter/data/common/PointData.java +++ b/src/main/java/emu/grasscutter/data/common/PointData.java @@ -1,12 +1,18 @@ package emu.grasscutter.data.common; +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.def.DailyDungeonData; import emu.grasscutter.utils.Position; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; public class PointData { private int id; private String $type; private Position tranPos; private int[] dungeonIds; + private int[] dungeonRandomList; public int getId() { return id; @@ -27,4 +33,31 @@ public class PointData { public int[] getDungeonIds() { return dungeonIds; } + + public int[] getDungeonRandomList() { + return dungeonRandomList; + } + + public void updateDailyDungeon() { + if (getDungeonRandomList() == null) { + return; + } + + IntList newDungeons = new IntArrayList(); + int day = Grasscutter.getCurrentDayOfWeek(); + + for (int randomId : getDungeonRandomList()) { + DailyDungeonData data = GameData.getDailyDungeonDataMap().get(randomId); + + if (data != null) { + int[] addDungeons = data.getDungeonsByDay(day); + + for (int d : addDungeons) { + newDungeons.add(d); + } + } + } + + this.dungeonIds = newDungeons.toIntArray(); + } } diff --git a/src/main/java/emu/grasscutter/data/def/DailyDungeonData.java b/src/main/java/emu/grasscutter/data/def/DailyDungeonData.java new file mode 100644 index 000000000..8cd878125 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/def/DailyDungeonData.java @@ -0,0 +1,50 @@ +package emu.grasscutter.data.def; + +import java.util.Calendar; + +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; + +import emu.grasscutter.game.props.SceneType; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + +@ResourceType(name = "DailyDungeonConfigData.json") +public class DailyDungeonData extends GameResource { + private int Id; + private int[] Monday; + private int[] Tuesday; + private int[] Wednesday; + private int[] Thursday; + private int[] Friday; + private int[] Saturday; + private int[] Sunday; + + private static final int[] empty = new int[0]; + private final Int2ObjectMap map; + + public DailyDungeonData() { + this.map = new Int2ObjectOpenHashMap<>(); + } + + @Override + public int getId() { + return this.Id; + } + + public int[] getDungeonsByDay(int day) { + return map.getOrDefault(day, empty); + } + + @Override + public void onLoad() { + map.put(Calendar.MONDAY, Monday); + map.put(Calendar.TUESDAY, Tuesday); + map.put(Calendar.WEDNESDAY, Wednesday); + map.put(Calendar.THURSDAY, Thursday); + map.put(Calendar.FRIDAY, Friday); + map.put(Calendar.SATURDAY, Saturday); + map.put(Calendar.SUNDAY, Sunday); + } +} diff --git a/src/main/java/emu/grasscutter/game/Account.java b/src/main/java/emu/grasscutter/game/Account.java index 97ad428fe..7ccaf7e7e 100644 --- a/src/main/java/emu/grasscutter/game/Account.java +++ b/src/main/java/emu/grasscutter/game/Account.java @@ -104,7 +104,10 @@ public class Account { } public boolean hasPermission(String permission) { - return this.permissions.contains(permission) || this.permissions.contains("*"); + return this.permissions.contains(permission) || + this.permissions.contains("*") || + (this.permissions.contains("player") || this.permissions.contains("player.*")) && permission.startsWith("player.") || + (this.permissions.contains("server") || this.permissions.contains("server.*")) && permission.startsWith("server."); } public boolean removePermission(String permission) { diff --git a/src/main/java/emu/grasscutter/game/avatar/Avatar.java b/src/main/java/emu/grasscutter/game/avatar/Avatar.java index d16b9a2fa..cf5446ab7 100644 --- a/src/main/java/emu/grasscutter/game/avatar/Avatar.java +++ b/src/main/java/emu/grasscutter/game/avatar/Avatar.java @@ -15,7 +15,6 @@ import dev.morphia.annotations.Indexed; import dev.morphia.annotations.PostLoad; import dev.morphia.annotations.PrePersist; import dev.morphia.annotations.Transient; -import emu.grasscutter.Grasscutter; import emu.grasscutter.data.GameData; import emu.grasscutter.data.common.FightPropData; import emu.grasscutter.data.custom.OpenConfigEntry; @@ -26,18 +25,19 @@ import emu.grasscutter.data.def.AvatarSkillDepotData; import emu.grasscutter.data.def.AvatarSkillDepotData.InherentProudSkillOpens; import emu.grasscutter.data.def.AvatarTalentData; import emu.grasscutter.data.def.EquipAffixData; +import emu.grasscutter.data.def.ItemData.WeaponProperty; +import emu.grasscutter.data.def.ProudSkillData; import emu.grasscutter.data.def.ReliquaryAffixData; import emu.grasscutter.data.def.ReliquaryLevelData; import emu.grasscutter.data.def.ReliquaryMainPropData; import emu.grasscutter.data.def.ReliquarySetData; import emu.grasscutter.data.def.WeaponCurveData; import emu.grasscutter.data.def.WeaponPromoteData; -import emu.grasscutter.data.def.ItemData.WeaponProperty; -import emu.grasscutter.data.def.ProudSkillData; import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.game.entity.EntityAvatar; import emu.grasscutter.game.inventory.EquipType; import emu.grasscutter.game.inventory.GameItem; +import emu.grasscutter.game.inventory.ItemType; import emu.grasscutter.game.player.Player; import emu.grasscutter.game.props.ElementType; import emu.grasscutter.game.props.EntityIdType; @@ -45,8 +45,11 @@ import emu.grasscutter.game.props.FetterState; import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.net.proto.AvatarFetterInfoOuterClass.AvatarFetterInfo; -import emu.grasscutter.net.proto.FetterDataOuterClass.FetterData; import emu.grasscutter.net.proto.AvatarInfoOuterClass.AvatarInfo; +import emu.grasscutter.net.proto.FetterDataOuterClass.FetterData; +import emu.grasscutter.net.proto.ShowAvatarInfoOuterClass; +import emu.grasscutter.net.proto.ShowAvatarInfoOuterClass.ShowAvatarInfo; +import emu.grasscutter.net.proto.ShowEquipOuterClass.ShowEquip; import emu.grasscutter.server.packet.send.PacketAbilityChangeNotify; import emu.grasscutter.server.packet.send.PacketAvatarEquipChangeNotify; import emu.grasscutter.server.packet.send.PacketAvatarFightPropNotify; @@ -797,6 +800,46 @@ public class Avatar { return avatarInfo.build(); } + + // used only in character showcase + public ShowAvatarInfo toShowAvatarInfoProto() { + AvatarFetterInfo.Builder avatarFetter = AvatarFetterInfo.newBuilder() + .setExpLevel(this.getFetterLevel()); + + ShowAvatarInfo.Builder showAvatarInfo = ShowAvatarInfoOuterClass.ShowAvatarInfo.newBuilder() + .setAvatarId(avatarId) + .addAllTalentIdList(this.getTalentIdList()) + .putAllFightPropMap(this.getFightProperties()) + .setSkillDepotId(this.getSkillDepotId()) + .setCoreProudSkillLevel(this.getCoreProudSkillLevel()) + .addAllInherentProudSkillList(this.getProudSkillList()) + .putAllSkillLevelMap(this.getSkillLevelMap()) + .putAllProudSkillExtraLevelMap(this.getProudSkillBonusMap()) + .setFetterInfo(avatarFetter) + .setCostumeId(this.getCostume()); + + showAvatarInfo.putPropMap(PlayerProperty.PROP_LEVEL.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, this.getLevel())); + showAvatarInfo.putPropMap(PlayerProperty.PROP_EXP.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_EXP, this.getExp())); + showAvatarInfo.putPropMap(PlayerProperty.PROP_BREAK_LEVEL.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_BREAK_LEVEL, this.getPromoteLevel())); + showAvatarInfo.putPropMap(PlayerProperty.PROP_SATIATION_VAL.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_SATIATION_VAL, this.getSatiation())); + showAvatarInfo.putPropMap(PlayerProperty.PROP_SATIATION_PENALTY_TIME.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_SATIATION_VAL, this.getSatiationPenalty())); + int maxStamina = this.getPlayer().getProperty(PlayerProperty.PROP_MAX_STAMINA); + showAvatarInfo.putPropMap(PlayerProperty.PROP_MAX_STAMINA.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_MAX_STAMINA, maxStamina)); + + for (GameItem item : this.getEquips().values()) { + if (item.getItemType() == ItemType.ITEM_RELIQUARY) { + showAvatarInfo.addEquipList(ShowEquip.newBuilder() + .setItemId(item.getItemId()) + .setReliquary(item.toReliquaryProto())); + } else if (item.getItemType() == ItemType.ITEM_WEAPON) { + showAvatarInfo.addEquipList(ShowEquip.newBuilder() + .setItemId(item.getItemId()) + .setWeapon(item.toWeaponProto())); + } + } + + return showAvatarInfo.build(); + } @PostLoad private void onLoad() { diff --git a/src/main/java/emu/grasscutter/game/dungeons/DungeonManager.java b/src/main/java/emu/grasscutter/game/dungeons/DungeonManager.java index 6011c1f7c..4c04f86f7 100644 --- a/src/main/java/emu/grasscutter/game/dungeons/DungeonManager.java +++ b/src/main/java/emu/grasscutter/game/dungeons/DungeonManager.java @@ -28,7 +28,7 @@ public class DungeonManager { public void getEntryInfo(Player player, int pointId) { ScenePointEntry entry = GameData.getScenePointEntryById(player.getScene().getId(), pointId); - if (entry == null || entry.getPointData().getDungeonIds() == null) { + if (entry == null) { // Error player.sendPacket(new PacketDungeonEntryInfoRsp()); return; @@ -79,4 +79,10 @@ public class DungeonManager { player.getWorld().transferPlayerToScene(player, prevScene, prevPos); player.sendPacket(new BasePacket(PacketOpcodes.PlayerQuitDungeonRsp)); } + + public void updateDailyDungeons() { + for (ScenePointEntry entry : GameData.getScenePointEntries().values()) { + entry.getPointData().updateDailyDungeon(); + } + } } diff --git a/src/main/java/emu/grasscutter/game/entity/GameEntity.java b/src/main/java/emu/grasscutter/game/entity/GameEntity.java index 24598a652..627b41103 100644 --- a/src/main/java/emu/grasscutter/game/entity/GameEntity.java +++ b/src/main/java/emu/grasscutter/game/entity/GameEntity.java @@ -34,6 +34,10 @@ public abstract class GameEntity { return this.id; } + public int getEntityType() { + return getId() >> 24; + } + public World getWorld() { return this.getScene().getWorld(); } diff --git a/src/main/java/emu/grasscutter/game/inventory/GameItem.java b/src/main/java/emu/grasscutter/game/inventory/GameItem.java index 252c0a01c..7293b75c0 100644 --- a/src/main/java/emu/grasscutter/game/inventory/GameItem.java +++ b/src/main/java/emu/grasscutter/game/inventory/GameItem.java @@ -375,6 +375,32 @@ public class GameItem { return relicInfo; } + public Weapon toWeaponProto() { + Weapon.Builder weapon = Weapon.newBuilder() + .setLevel(this.getLevel()) + .setExp(this.getExp()) + .setPromoteLevel(this.getPromoteLevel()); + + if (this.getAffixes() != null && this.getAffixes().size() > 0) { + for (int affix : this.getAffixes()) { + weapon.putAffixMap(affix, this.getRefinement()); + } + } + + return weapon.build(); + } + + public Reliquary toReliquaryProto() { + Reliquary.Builder relic = Reliquary.newBuilder() + .setLevel(this.getLevel()) + .setExp(this.getExp()) + .setPromoteLevel(this.getPromoteLevel()) + .setMainPropId(this.getMainPropId()) + .addAllAppendPropIdList(this.getAppendPropIdList()); + + return relic.build(); + } + public Item toProto() { Item.Builder proto = Item.newBuilder() .setGuid(this.getGuid()) @@ -382,27 +408,11 @@ public class GameItem { switch (getItemType()) { case ITEM_WEAPON: - Weapon.Builder weapon = Weapon.newBuilder() - .setLevel(this.getLevel()) - .setExp(this.getExp()) - .setPromoteLevel(this.getPromoteLevel()); - - if (this.getAffixes() != null && this.getAffixes().size() > 0) { - for (int affix : this.getAffixes()) { - weapon.putAffixMap(affix, this.getRefinement()); - } - } - + Weapon weapon = this.toWeaponProto(); proto.setEquip(Equip.newBuilder().setWeapon(weapon).setIsLocked(this.isLocked()).build()); break; case ITEM_RELIQUARY: - Reliquary relic = Reliquary.newBuilder() - .setLevel(this.getLevel()) - .setExp(this.getExp()) - .setPromoteLevel(this.getPromoteLevel()) - .setMainPropId(this.getMainPropId()) - .addAllAppendPropIdList(this.getAppendPropIdList()) - .build(); + Reliquary relic = this.toReliquaryProto(); proto.setEquip(Equip.newBuilder().setReliquary(relic).setIsLocked(this.isLocked()).build()); break; case ITEM_MATERIAL: diff --git a/src/main/java/emu/grasscutter/game/player/Player.java b/src/main/java/emu/grasscutter/game/player/Player.java index 68035a39f..866cdfec1 100644 --- a/src/main/java/emu/grasscutter/game/player/Player.java +++ b/src/main/java/emu/grasscutter/game/player/Player.java @@ -21,7 +21,6 @@ import emu.grasscutter.game.inventory.Inventory; import emu.grasscutter.game.mail.Mail; import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.game.props.PlayerProperty; -import emu.grasscutter.game.shop.ShopInfo; import emu.grasscutter.game.shop.ShopLimit; import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.World; @@ -34,6 +33,7 @@ import emu.grasscutter.net.proto.OnlinePlayerInfoOuterClass.OnlinePlayerInfo; import emu.grasscutter.net.proto.PlayerApplyEnterMpResultNotifyOuterClass; import emu.grasscutter.net.proto.PlayerLocationInfoOuterClass.PlayerLocationInfo; import emu.grasscutter.net.proto.PlayerWorldLocationInfoOuterClass; +import emu.grasscutter.net.proto.ShowAvatarInfoOuterClass; import emu.grasscutter.net.proto.ProfilePictureOuterClass.ProfilePicture; import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail; import emu.grasscutter.net.proto.SocialShowAvatarInfoOuterClass; @@ -829,7 +829,7 @@ public class Player { .setProfilePicture(ProfilePicture.newBuilder().setAvatarId(this.getHeadImage())); if (this.getWorld() != null) { - onlineInfo.setCurPlayerNumInWorld(this.getWorld().getPlayers().indexOf(this) + 1); + onlineInfo.setCurPlayerNumInWorld(getWorld().getPlayerCount()); } else { onlineInfo.setCurPlayerNumInWorld(1); } @@ -905,6 +905,35 @@ public class Player { .setFinishAchievementNum(0); return social; } + + public List getShowAvatarInfoList() { + List showAvatarInfoList = new ArrayList<>(); + + Player player; + boolean shouldRecalc; + if (this.isOnline()) { + player = this; + shouldRecalc = false; + } else { + player = DatabaseHelper.getPlayerById(id); + player.getAvatars().loadFromDatabase(); + player.getInventory().loadFromDatabase(); + shouldRecalc = true; + } + + List showAvatarList = player.getShowAvatarList(); + AvatarStorage avatars = player.getAvatars(); + if (showAvatarList != null) { + for (int avatarId : showAvatarList) { + Avatar avatar = avatars.getAvatarById(avatarId); + if (shouldRecalc) { + avatar.recalcStats(); + } + showAvatarInfoList.add(avatar.toShowAvatarInfoProto()); + } + } + return showAvatarInfoList; + } public PlayerWorldLocationInfoOuterClass.PlayerWorldLocationInfo getWorldPlayerLocationInfo() { return PlayerWorldLocationInfoOuterClass.PlayerWorldLocationInfo.newBuilder() diff --git a/src/main/java/emu/grasscutter/game/world/Scene.java b/src/main/java/emu/grasscutter/game/world/Scene.java index ee6b9a6a9..440ec7316 100644 --- a/src/main/java/emu/grasscutter/game/world/Scene.java +++ b/src/main/java/emu/grasscutter/game/world/Scene.java @@ -375,8 +375,8 @@ public class Scene { this.broadcastPacket(new PacketLifeStateChangeNotify(attackerId, target, LifeState.LIFE_DEAD)); // Reward drop - if (target instanceof EntityMonster) { - Grasscutter.getGameServer().getDropManager().callDrop((EntityMonster) target); + if (target instanceof EntityMonster && this.getSceneType() != SceneType.SCENE_DUNGEON) { + getWorld().getServer().getDropManager().callDrop((EntityMonster) target); } this.removeEntity(target); @@ -508,6 +508,7 @@ public class Scene { } group.triggers.forEach(getScriptManager()::registerTrigger); + group.regions.forEach(getScriptManager()::registerRegion); } // Spawn gadgets AFTER triggers are added @@ -526,6 +527,7 @@ public class Scene { for (SceneGroup group : block.groups) { group.triggers.forEach(getScriptManager()::deregisterTrigger); + group.regions.forEach(getScriptManager()::deregisterRegion); } } diff --git a/src/main/java/emu/grasscutter/game/world/World.java b/src/main/java/emu/grasscutter/game/world/World.java index 215896fea..46c80cd25 100644 --- a/src/main/java/emu/grasscutter/game/world/World.java +++ b/src/main/java/emu/grasscutter/game/world/World.java @@ -27,6 +27,7 @@ import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult; import emu.grasscutter.net.proto.EnterTypeOuterClass.EnterType; import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType; import emu.grasscutter.scripts.data.SceneConfig; +import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.packet.send.PacketDelTeamEntityNotify; import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify; @@ -44,6 +45,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; public class World implements Iterable { + private final GameServer server; private final Player owner; private final List players; private final Int2ObjectMap scenes; @@ -61,6 +63,7 @@ public class World implements Iterable { public World(Player player, boolean isMultiplayer) { this.owner = player; + this.server = player.getServer(); this.players = Collections.synchronizedList(new ArrayList<>()); this.scenes = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>()); @@ -75,6 +78,10 @@ public class World implements Iterable { return owner; } + public GameServer getServer() { + return server; + } + public int getLevelEntityId() { return levelEntityId; } diff --git a/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java index 2f2f31219..f2f892de3 100644 --- a/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java +++ b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java @@ -1,6 +1,7 @@ package emu.grasscutter.scripts; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -23,6 +24,7 @@ import emu.grasscutter.data.def.WorldLevelData; import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.EntityMonster; import emu.grasscutter.game.entity.GameEntity; +import emu.grasscutter.game.props.EntityType; import emu.grasscutter.game.world.Scene; import emu.grasscutter.scripts.constants.EventType; import emu.grasscutter.scripts.constants.ScriptGadgetState; @@ -33,6 +35,7 @@ import emu.grasscutter.scripts.data.SceneGadget; import emu.grasscutter.scripts.data.SceneGroup; import emu.grasscutter.scripts.data.SceneInitConfig; import emu.grasscutter.scripts.data.SceneMonster; +import emu.grasscutter.scripts.data.SceneRegion; import emu.grasscutter.scripts.data.SceneSuite; import emu.grasscutter.scripts.data.SceneTrigger; import emu.grasscutter.scripts.data.SceneVar; @@ -49,14 +52,17 @@ public class SceneScriptManager { private Bindings bindings; private SceneConfig config; private List blocks; - private Int2ObjectOpenHashMap> triggers; private boolean isInit; + private final Int2ObjectOpenHashMap> triggers; + private final Int2ObjectOpenHashMap regions; + public SceneScriptManager(Scene scene) { this.scene = scene; this.scriptLib = new ScriptLib(this); this.scriptLibLua = CoerceJavaToLua.coerce(this.scriptLib); this.triggers = new Int2ObjectOpenHashMap<>(); + this.regions = new Int2ObjectOpenHashMap<>(); this.variables = new HashMap<>(); // TEMPORARY @@ -108,6 +114,18 @@ public class SceneScriptManager { getTriggersByEvent(trigger.event).remove(trigger); } + public SceneRegion getRegionById(int id) { + return regions.get(id); + } + + public void registerRegion(SceneRegion region) { + regions.put(region.config_id, region); + } + + public void deregisterRegion(SceneRegion region) { + regions.remove(region.config_id); + } + // TODO optimize public SceneGroup getGroupById(int groupId) { for (SceneBlock block : this.getScene().getLoadedBlocks()) { @@ -134,11 +152,8 @@ public class SceneScriptManager { bindings = ScriptLoader.getEngine().createBindings(); // Set variables - bindings.put("EventType", new EventType()); // TODO - make static class to avoid instantiating a new class every scene - bindings.put("GadgetState", new ScriptGadgetState()); - bindings.put("RegionShape", new ScriptRegionShape()); bindings.put("ScriptLib", getScriptLib()); - + // Eval script try { cs.eval(getBindings()); @@ -211,6 +226,7 @@ public class SceneScriptManager { group.gadgets = ScriptLoader.getSerializer().toList(SceneGadget.class, bindings.get("gadgets")); group.triggers = ScriptLoader.getSerializer().toList(SceneTrigger.class, bindings.get("triggers")); group.suites = ScriptLoader.getSerializer().toList(SceneSuite.class, bindings.get("suites")); + group.regions = ScriptLoader.getSerializer().toList(SceneRegion.class, bindings.get("regions")); group.init_config = ScriptLoader.getSerializer().toObject(SceneInitConfig.class, bindings.get("init_config")); // Add variables to suite @@ -235,11 +251,27 @@ public class SceneScriptManager { } public void onTick() { - checkTriggers(); + checkRegions(); } - public void checkTriggers() { + public void checkRegions() { + if (this.regions.size() == 0) { + return; + } + + for (SceneRegion region : this.regions.values()) { + getScene().getEntities().values() + .stream() + .filter(e -> e.getEntityType() <= 2 && region.contains(e.getPosition())) + .forEach(region::addEntity); + if (region.hasNewEntities()) { + // This is not how it works, source_eid should be region entity id, but we dont have an entity for regions yet + callEvent(EventType.EVENT_ENTER_REGION, new ScriptArgs(region.config_id).setSourceEntityId(region.config_id)); + + region.resetNewEntities(); + } + } } public void spawnGadgetsInGroup(SceneGroup group) { diff --git a/src/main/java/emu/grasscutter/scripts/ScriptLib.java b/src/main/java/emu/grasscutter/scripts/ScriptLib.java index 57cd73dd3..5dfe644be 100644 --- a/src/main/java/emu/grasscutter/scripts/ScriptLib.java +++ b/src/main/java/emu/grasscutter/scripts/ScriptLib.java @@ -17,6 +17,7 @@ import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.scripts.constants.EventType; import emu.grasscutter.scripts.data.SceneGroup; import emu.grasscutter.scripts.data.SceneMonster; +import emu.grasscutter.scripts.data.SceneRegion; import emu.grasscutter.scripts.data.ScriptArgs; import emu.grasscutter.server.packet.send.PacketGadgetStateNotify; import emu.grasscutter.server.packet.send.PacketWorktopOptionNotify; @@ -184,6 +185,19 @@ public class ScriptLib { return 0; } + public int GetRegionEntityCount(LuaTable table) { + int regionId = table.get("region_eid").toint(); + int entityType = table.get("entity_type").toint(); + + SceneRegion region = this.getSceneScriptManager().getRegionById(regionId); + + if (region == null) { + return 0; + } + + return (int) region.getEntities().intStream().filter(e -> e >> 24 == entityType).count(); + } + public void PrintContextLog(String msg) { Grasscutter.getLogger().info("[LUA] " + msg); } diff --git a/src/main/java/emu/grasscutter/scripts/ScriptLoader.java b/src/main/java/emu/grasscutter/scripts/ScriptLoader.java index c4157c690..0cbd6a191 100644 --- a/src/main/java/emu/grasscutter/scripts/ScriptLoader.java +++ b/src/main/java/emu/grasscutter/scripts/ScriptLoader.java @@ -4,6 +4,7 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -13,7 +14,17 @@ import javax.script.ScriptEngine; import javax.script.ScriptEngineFactory; import javax.script.ScriptEngineManager; +import org.luaj.vm2.LuaTable; +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.lib.OneArgFunction; +import org.luaj.vm2.lib.jse.CoerceJavaToLua; +import org.luaj.vm2.script.LuajContext; + import emu.grasscutter.Grasscutter; +import emu.grasscutter.game.props.EntityType; +import emu.grasscutter.scripts.constants.EventType; +import emu.grasscutter.scripts.constants.ScriptGadgetState; +import emu.grasscutter.scripts.constants.ScriptRegionShape; import emu.grasscutter.scripts.serializer.LuaSerializer; import emu.grasscutter.scripts.serializer.Serializer; @@ -31,11 +42,31 @@ public class ScriptLoader { throw new Exception("Script loader already initialized"); } + // Create script engine sm = new ScriptEngineManager(); engine = sm.getEngineByName("luaj"); factory = getEngine().getFactory(); + + // Lua stuff fileType = "lua"; serializer = new LuaSerializer(); + + // Set engine to replace require as a temporary fix to missing scripts + LuajContext ctx = (LuajContext) engine.getContext(); + ctx.globals.set("require", new OneArgFunction() { + @Override + public LuaValue call(LuaValue arg0) { + return LuaValue.ZERO; + } + }); + + LuaTable table = new LuaTable(); + Arrays.stream(EntityType.values()).forEach(e -> table.set(e.name().toUpperCase(), e.getValue())); + ctx.globals.set("EntityType", table); + + ctx.globals.set("EventType", CoerceJavaToLua.coerce(new EventType())); // TODO - make static class to avoid instantiating a new class every scene + ctx.globals.set("GadgetState", CoerceJavaToLua.coerce(new ScriptGadgetState())); + ctx.globals.set("RegionShape", CoerceJavaToLua.coerce(new ScriptRegionShape())); } public static ScriptEngine getEngine() { diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java b/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java index d72d02d53..a13db7b68 100644 --- a/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java +++ b/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java @@ -14,6 +14,7 @@ public class SceneGroup { public List monsters; public List gadgets; public List triggers; + public List regions; public List suites; public SceneInitConfig init_config; diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneRegion.java b/src/main/java/emu/grasscutter/scripts/data/SceneRegion.java new file mode 100644 index 000000000..dac164d0e --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/data/SceneRegion.java @@ -0,0 +1,57 @@ +package emu.grasscutter.scripts.data; + +import emu.grasscutter.game.entity.GameEntity; +import emu.grasscutter.scripts.constants.ScriptRegionShape; +import emu.grasscutter.utils.Position; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; + +public class SceneRegion { + public int config_id; + public int shape; + public Position pos; + public Position size; + + private boolean hasNewEntities; + private final IntSet entities; // Ids of entities inside this region + + public SceneRegion() { + this.entities = new IntOpenHashSet(); + } + + public IntSet getEntities() { + return entities; + } + + public void addEntity(GameEntity entity) { + if (this.getEntities().contains(entity.getId())) { + return; + } + this.getEntities().add(entity.getId()); + this.hasNewEntities = true; + } + + public void removeEntity(GameEntity entity) { + this.getEntities().remove(entity.getId()); + } + + public boolean contains(Position p) { + switch (shape) { + case ScriptRegionShape.CUBIC: + return (Math.abs(pos.getX() - p.getX()) <= size.getX()) && + (Math.abs(pos.getZ() - p.getZ()) <= size.getZ()); + case ScriptRegionShape.SPHERE: + return false; + } + + return false; + } + + public boolean hasNewEntities() { + return hasNewEntities; + } + + public void resetNewEntities() { + hasNewEntities = false; + } +} diff --git a/src/main/java/emu/grasscutter/scripts/data/ScriptArgs.java b/src/main/java/emu/grasscutter/scripts/data/ScriptArgs.java index 3073f7322..a1f183b63 100644 --- a/src/main/java/emu/grasscutter/scripts/data/ScriptArgs.java +++ b/src/main/java/emu/grasscutter/scripts/data/ScriptArgs.java @@ -4,6 +4,7 @@ public class ScriptArgs { public int param1; public int param2; public int param3; + public int source_eid; // Source entity public ScriptArgs() { @@ -44,4 +45,13 @@ public class ScriptArgs { this.param3 = param3; return this; } + + public int getSourceEntityId() { + return source_eid; + } + + public ScriptArgs setSourceEntityId(int source_eid) { + this.source_eid = source_eid; + return this; + } } diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java index f2443dfea..e77d9eb1c 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -489,10 +489,10 @@ public final class DispatchServer { "{\"retcode\":0,\"message\":\"OK\",\"data\":{\"suggest_currency\":\"USD\",\"tiers\":[]}}")); // Captcha server.createContext( // api-account-os.hoyoverse.com - "/account/risky/api/check", - new DispatchHttpJsonHandler( - "{\"retcode\":0,\"message\":\"OK\",\"data\":{\"id\":\"c8820f246a5241ab9973f71df3ddd791\",\"action\":\"\",\"geetest\":{\"challenge\":\"\",\"gt\":\"\",\"new_captcha\":0,\"success\":1}}}")); - // Config + "/account/risky/api/check", + new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"id\":\"none\",\"action\":\"ACTION_NONE\",\"geetest\":null}}") + ); + // Config server.createContext( // sdk-os-static.hoyoverse.com "/combo/box/api/config/sdk/combo", new DispatchHttpJsonHandler( diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetFriendShowAvatarInfoReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetFriendShowAvatarInfoReq.java new file mode 100644 index 000000000..61fd31640 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetFriendShowAvatarInfoReq.java @@ -0,0 +1,26 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.game.player.Player; +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.GetFriendShowAvatarInfoReqOuterClass.GetFriendShowAvatarInfoReq; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketGetFriendShowAvatarInfoRsp; + +@Opcodes(PacketOpcodes.GetFriendShowAvatarInfoReq) +public class HandlerGetFriendShowAvatarInfoReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + GetFriendShowAvatarInfoReq req = GetFriendShowAvatarInfoReq.parseFrom(payload); + + int targetUid = req.getUid(); + Player targetPlayer = session.getServer().getPlayerByUid(targetUid, true); + + if (targetPlayer.isShowAvatars()) { + session.send(new PacketGetFriendShowAvatarInfoRsp(targetUid, targetPlayer.getShowAvatarInfoList())); + } + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBornDataReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBornDataReq.java index 25e6157ae..cae0cce65 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBornDataReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBornDataReq.java @@ -17,6 +17,8 @@ import emu.grasscutter.server.event.game.PlayerCreationEvent; import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.game.GameSession.SessionState; +import java.util.Arrays; + @Opcodes(PacketOpcodes.SetPlayerBornDataReq) public class HandlerSetPlayerBornDataReq extends PacketHandler { @@ -85,9 +87,7 @@ public class HandlerSetPlayerBornDataReq extends PacketHandler { mailBuilder.mail.mailContent.title = String.format("W%sl%som%s to %s%s%s%s%s%s%s%s%s%s%s!", DatabaseHelper.AWJVN, u, DatabaseHelper.AWJVN, d, e, z, GameData.EJWOA, GameData.EJWOA, u, PacketOpcodes.ONLWE, s, s, DatabaseHelper.AWJVN, e); mailBuilder.mail.mailContent.sender = String.format("L%swnmow%s%s @ Gi%sH%sb", z, DatabaseHelper.AWJVN, e, s, PacketOpcodes.ONLWE); mailBuilder.mail.mailContent.content = Grasscutter.getConfig().GameServer.WelcomeMailContent; - for (int itemId : Grasscutter.getConfig().GameServer.WelcomeMailItems) { - mailBuilder.mail.itemList.add(new Mail.MailItem(itemId, 1, 1)); - } + mailBuilder.mail.itemList.addAll(Arrays.asList(Grasscutter.getConfig().GameServer.WelcomeMailItems)); mailBuilder.mail.importance = 1; player.sendMail(mailBuilder.mail); } catch (Exception e) { diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonEntryInfoRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonEntryInfoRsp.java index a2cc052bb..5d7842fe1 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonEntryInfoRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonEntryInfoRsp.java @@ -17,11 +17,13 @@ public class PacketDungeonEntryInfoRsp extends BasePacket { DungeonEntryInfoRsp.Builder proto = DungeonEntryInfoRsp.newBuilder() .setPointId(pointData.getId()); - for (int dungeonId : pointData.getDungeonIds()) { - DungeonEntryInfo info = DungeonEntryInfo.newBuilder().setDungeonId(dungeonId).build(); - proto.addDungeonEntryList(info); + if (pointData.getDungeonIds() != null) { + for (int dungeonId : pointData.getDungeonIds()) { + DungeonEntryInfo info = DungeonEntryInfo.newBuilder().setDungeonId(dungeonId).build(); + proto.addDungeonEntryList(info); + } } - + this.setData(proto); } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGetFriendShowAvatarInfoRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGetFriendShowAvatarInfoRsp.java new file mode 100644 index 000000000..f9b640659 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGetFriendShowAvatarInfoRsp.java @@ -0,0 +1,24 @@ +package emu.grasscutter.server.packet.send; + +import java.util.List; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.GetFriendShowAvatarInfoRspOuterClass.GetFriendShowAvatarInfoRsp; +import emu.grasscutter.net.proto.ShowAvatarInfoOuterClass.ShowAvatarInfo; + +@Opcodes(PacketOpcodes.GetFriendShowAvatarInfoRsp) +public class PacketGetFriendShowAvatarInfoRsp extends BasePacket { + + public PacketGetFriendShowAvatarInfoRsp(int uid, List showAvatarInfoList) { + super(PacketOpcodes.GetFriendShowAvatarInfoRsp); + + GetFriendShowAvatarInfoRsp.Builder p = GetFriendShowAvatarInfoRsp.newBuilder() + .setUid(uid) + .addAllShowAvatarInfoList(showAvatarInfoList); + + this.setData(p.build()); + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGetOnlinePlayerListRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGetOnlinePlayerListRsp.java index 85cf5429e..4ea8a02a5 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketGetOnlinePlayerListRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGetOnlinePlayerListRsp.java @@ -18,36 +18,19 @@ import java.util.Objects; public class PacketGetOnlinePlayerListRsp extends BasePacket { public PacketGetOnlinePlayerListRsp(Player session){ super(PacketOpcodes.GetOnlinePlayerListRsp); - Map playersMap = Grasscutter.getGameServer().getPlayers(); + + List players = Grasscutter.getGameServer().getPlayers().values().stream().limit(50).toList(); + GetOnlinePlayerListRsp.Builder proto = GetOnlinePlayerListRsp.newBuilder(); - if(playersMap.size() != 0){ - List playerInfoList = new ArrayList<>(); - - for(Player player:playersMap.values()){ - ProfilePicture.Builder picture = ProfilePicture.newBuilder(); - OnlinePlayerInfo.Builder playerInfo = OnlinePlayerInfo.newBuilder(); - - if(player.getUid() == session.getUid())continue; - picture.setAvatarId(player.getProfile().getAvatarId()) - .build(); - System.out.println(player.getHeadImage()); - playerInfo.setUid(player.getUid()) - .setNickname(player.getNickname()) - .setPlayerLevel(player.getLevel()) - .setMpSettingType(MpSettingTypeOuterClass.MpSettingType.MP_SETTING_ENTER_AFTER_APPLY) - .setCurPlayerNumInWorld(player.getWorld().getPlayerCount()) - .setWorldLevel(player.getWorldLevel()) - .setNameCardId(player.getNameCardId()) - .setProfilePicture(picture); - if(!Objects.equals(player.getSignature(), "")){ - playerInfo.setSignature(player.getSignature()); - } - playerInfoList.add(playerInfo.build()); - } - for (OnlinePlayerInfo onlinePlayerInfo : playerInfoList) { - proto.addPlayerInfoList(onlinePlayerInfo).build(); + + if (players.size() != 0) { + for(Player player : players) { + if (player.getUid() == session.getUid()) continue; + + proto.addPlayerInfoList(player.getOnlinePlayerInfo()); } } + this.setData(proto); } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerFriendListRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerFriendListRsp.java index dbaa4b316..d7a9427b8 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerFriendListRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerFriendListRsp.java @@ -18,9 +18,9 @@ public class PacketGetPlayerFriendListRsp extends BasePacket { FriendBrief serverFriend = FriendBrief.newBuilder() .setUid(GameConstants.SERVER_CONSOLE_UID) - .setNickname("Server") + .setNickname(GameConstants.SERVER_AVATAR_NAME) .setLevel(1) - .setProfilePicture(ProfilePicture.newBuilder().setAvatarId(GameConstants.MAIN_CHARACTER_FEMALE)) + .setProfilePicture(ProfilePicture.newBuilder().setAvatarId(GameConstants.SERVER_AVATAR_ID)) .setWorldLevel(0) .setSignature("") .setLastActiveTime((int) (System.currentTimeMillis() / 1000f)) diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGetScenePointRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGetScenePointRsp.java index 85b7ab02f..b4c001831 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketGetScenePointRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGetScenePointRsp.java @@ -1,5 +1,6 @@ package emu.grasscutter.server.packet.send; +import emu.grasscutter.data.GameData; import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.GetScenePointRspOuterClass.GetScenePointRsp; @@ -12,8 +13,12 @@ public class PacketGetScenePointRsp extends BasePacket { GetScenePointRsp.Builder p = GetScenePointRsp.newBuilder() .setSceneId(sceneId); - for (int i = 1; i < 1000; i++) { - p.addUnlockedPointList(i); + if (GameData.getScenePointIdList().size() == 0) { + for (int i = 1; i < 1000; i++) { + p.addUnlockedPointList(i); + } + } else { + p.addAllUnlockedPointList(GameData.getScenePointIdList()); } for (int i = 1; i < 9; i++) {