diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b61ac08c8..49a9d8b53 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,10 +1,15 @@ name: "Build" on: + workflow_dispatch: ~ push: + paths: + - "**.java" branches: - "stable" - "development" pull_request: + paths: + - "**.java" types: - opened - synchronize diff --git a/README.md b/README.md index 840657f22..9d5595853 100644 --- a/README.md +++ b/README.md @@ -106,46 +106,50 @@ There is a dummy user named "Server" in every player's friends list that you can | Commands | Usage | Permission node | Availability | description | Alias | | -------------- | ------------------------------------------------- | ------------------------- | ------------ | ------------------------------------------------------------ | ----------------------------------------------- | -| account | account [UID] | | Server only | Creates an account with the specified username and the in-game UID for that account. The UID will be auto generated if not set. | | -| broadcast | broadcast | server.broadcast | Both side | Sends a message to all the players. | b | -| coop | coop | server.coop | Both side | Forces someone to join the world of others. | | -| changescene | changescene | player.changescene | Client only | Switch scenes by scene ID. | scene | +| account | account \ [UID] | | Server only | Creates an account with the specified username and the in-game UID for that account. The UID will be auto generated if not set. | | +| broadcast | broadcast \ | server.broadcast | Both side | Sends a message to all the players. | b | +| coop | coop \ \ | server.coop | Both side | Forces someone to join the world of others. | | +| changescene | changescene \ | player.changescene | Client only | Switch scenes by scene ID. | scene | | clearartifacts | clearartifacts | player.clearartifacts | Client only | Deletes all unequipped and unlocked level 0 artifacts, including 5-star rarity ones from your inventory. | clearart | | clearweapons | clearweapons | player.clearweapons | Client only | Deletes all unequipped and unlocked weapons, including 5-star rarity ones from your inventory. | clearwpns | | 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 | -| giveall | giveall [uid] [amount] | player.giveall | Both side | Gives all items. | givea | +| 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 | +| 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 | | help | help [command] | | Both side | Sends the help message or shows information about a specified command. | | -| kick | kick | server.kick | Both side | Kicks the specified player from the server. (WIP) | k | +| kick | kick \ | server.kick | Both side | Kicks the specified player from the server. (WIP) | k | | killall | killall [playerUid] [sceneId] | server.killall | Both side | Kills all entities in the current scene or specified scene of the corresponding player. | | | list | list | | Both side | Lists online players. | | -| permission | permission | * | Both side | Grants or removes a permission for a user. | | +| permission | permission \ \ | * | Both side | Grants or removes a permission for a user. | | | position | position | | Client only | Sends your current coordinates. | pos | | reload | reload | server.reload | Both side | Reloads the server config | | | resetconst | resetconst [all] | player.resetconstellation | Client only | Resets the constellation level on your currently selected character, will need to relog after using the command to see any changes. | resetconstellation | | restart | | | Both side | Restarts the current session | | -| say | say | server.sendmessage | Both side | Sends a message to a player as the server | `sendservmsg` `sendservermessage` `sendmessage` | -| setfetterlevel | setfetterlevel | player.setfetterlevel | Client only | Sets the friendship level for your currently selected character | setfetterlvl | -| setstats | setstats | player.setstats | Client only | Sets a stat for your currently selected character | stats | -| setworldlevel | setworldlevel | player.setworldlevel | Client only | Sets your world level (Relog to see proper effects) | setworldlvl | +| say | say \ \ | server.sendmessage | Both side | Sends a message to a player as the server | `sendservmsg` `sendservermessage` `sendmessage` | +| setfetterlevel | setfetterlevel \ | player.setfetterlevel | Client only | Sets the friendship level for your currently selected character | setfetterlvl | +| setstats | setstats \ \ | player.setstats | Client only | Sets a stat for your currently selected character | stats | +| setworldlevel | setworldlevel \ | player.setworldlevel | Client only | Sets your world level (Relog to see proper effects) | setworldlvl | | 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 id] [scene id] | player.teleport | Both side | Change the player's position. | tp | +| talent | talent \ \ | player.settalent | Client only | Sets talent level for your currently selected character | | +| 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 | +| weather | weather \ \ | player.weather | Client only | Changes the weather | w | ### Bonus 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 92ab9bb26..a386f0d23 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -109,18 +109,19 @@ chmod +x gradlew | -------------- | -------------------------------------------- | ------------------------- | -------- | ------------------------------------------ | ----------------------------------------------- | | account | account <用户名> [uid] | | 仅服务端 | 通过指定用户名和uid增删账户 | | | broadcast | broadcast <消息内容> | server.broadcast | 均可使用 | 给所有玩家发送公告 | b | -| coop | coop <目标uid> | server.coop | 均可使用 | 强制某位玩家进入指定玩家的多人世界 | | +| coop | coop \ <目标uid> | server.coop | 均可使用 | 强制某位玩家进入指定玩家的多人世界 | | | changescene | changescene <场景ID> | player.changescene | 仅客户端 | 切换到指定场景 | scene | | clearartifacts | clearartifacts | player.clearartifacts | 仅客户端 | 删除所有未装备及未解锁的圣遗物,包括五星 | clearart | | clearweapons | clearweapons | player.clearweapons | 仅客户端 | 删除所有未装备及未解锁的武器,包括五星 | clearwp | | drop | drop <物品ID\|物品名称> [数量] | server.drop | 仅客户端 | 在指定玩家周围掉落指定物品 | `d` `dropitem` | | give | give [uid] <物品ID\|物品名称> [数量] [等级] [精炼等级] | | | 给予指定玩家一定数量及等级的物品 (精炼等级仅适用于武器) | `g` `item` `giveitem` | -| givechar | givechar <角色ID> [等级] | player.givechar | 均可使用 | 给予指定玩家对应角色 | givec | +| givechar | givechar \ <角色ID> [等级] | player.givechar | 均可使用 | 给予指定玩家对应角色 | givec | +| giveart | giveart [uid] \<圣遗物ID> \<主属性ID> [\<副属性ID>[,<次数>]]... [等级] | player.giveart | 均可使用 | 给予玩家指定属性的圣遗物 | givea | | giveall | giveall [uid] [数量] | player.giveall | 均可使用 | 给予指定玩家全部物品 | givea | | godmode | godmode [uid] | player.godmode | 仅客户端 | 保护你不受到任何伤害(依然会被击退) | | | heal | heal | player.heal | 仅客户端 | 治疗队伍中所有角色 | h | | help | help [命令] | | 均可使用 | 显示帮助或展示指定命令的帮助 | | -| kick | kick | server.kick | 均可使用 | 从服务器中踢出指定玩家 (WIP) | k | +| kick | kick \ | server.kick | 均可使用 | 从服务器中踢出指定玩家 (WIP) | k | | killall | killall [uid] [场景ID] | server.killall | 均可使用 | 杀死指定玩家世界中所在或指定场景的全部生物 | | | list | list | | 均可使用 | 列出在线玩家 | | | permission | permission <用户名> <权限节点> | * | 均可使用 | 添加或移除玩家的权限 | | @@ -128,14 +129,14 @@ chmod +x gradlew | reload | reload | server.reload | 均可使用 | 重载服务器配置 | | | resetconst | resetconst [all] | player.resetconstellation | 仅客户端 | 重置当前角色的命座,重新登录即可生效 | resetconstellation | | restart | restart | | 均可使用 | 重启服务端 | | -| say | say <消息> | server.sendmessage | 均可使用 | 作为服务器发送消息给玩家 | `sendservmsg` `sendservermessage` `sendmessage` | +| say | say \ <消息> | server.sendmessage | 均可使用 | 作为服务器发送消息给玩家 | `sendservmsg` `sendservermessage` `sendmessage` | | setfetterlevel | setfetterlevel <好感等级> | player.setfetterlevel | 仅客户端 | 设置当前角色的好感等级 | `setfetterlvl` `setfriendship` | | setstats | setstats <属性> <数值> | player.setstats | 仅客户端 | 直接修改当前角色的面板 | stats | | setworldlevel | setworldlevel <世界等级> | player.setworldlevel | 仅客户端 | 设置世界等级(重新登陆即可生效) | setworldlvl | | spawn | spanw <实体ID\|实体名称> [等级] [数量] | server.spawn | 仅客户端 | 在你周围生成实体 | | | stop | stop | server.stop | 均可使用 | 停止服务器 | | | talent | talent <天赋ID> <等级> | player.settalent | 仅客户端 | 设置当前角色的天赋等级 | | -| teleport | teleport [@player id] [scene id] | player.teleport | 均可使用 | 传送玩家到指定坐标 | tp | +| teleport | teleport [@playerUid] \ \ \ [sceneId] | player.teleport | 均可使用 | 传送玩家到指定坐标 | tp | | tpall | | player.tpall | 仅客户端 | 传送多人世界中所有的玩家到自身地点 | | | weather | weather <天气ID> <气候ID> | player.weather | 仅客户端 | 改变天气 | w | 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 94437e0e2..cb6d1e93e 100644 --- a/src/main/java/emu/grasscutter/command/commands/GiveAllCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/GiveAllCommand.java @@ -83,6 +83,9 @@ public class GiveAllCommand implements CommandHandler { Avatar avatar = new Avatar(avatarData); avatar.setLevel(90); avatar.setPromoteLevel(6); + for(int i = 1;i <= 6;++i){ + avatar.getTalentIdList().add((avatar.getAvatarId()-10000000)*10+i); + } // This will handle stats and talents avatar.recalcStats(); player.addAvatar(avatar); @@ -95,14 +98,14 @@ public 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 new file mode 100644 index 000000000..299a14f40 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/GiveArtifactCommand.java @@ -0,0 +1,89 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.def.ItemData; +import emu.grasscutter.game.inventory.GameItem; +import emu.grasscutter.game.inventory.ItemType; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.props.ActionReason; + +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") +public final class GiveArtifactCommand implements CommandHandler { + @Override + public void execute(Player sender, List args) { + int size = args.size(), target, itemId, mainPropId, level; + ArrayList appendPropIdList = new ArrayList<>(); + String msg = "Usage: giveart|givea [player] [[,]]... [level]"; + + if (sender == null && size < 2) { + CommandHandler.sendMessage(null, msg); + return; + } + + if (size >= 2) { + try { + level = Integer.parseInt(args.get(size - 1)); + if (level <= 21) size--; + else level = 1; + target = Integer.parseInt(args.get(0)); + int fromIdx; + if (Grasscutter.getGameServer().getPlayerByUid(target) == null && sender != null) { + target = sender.getUid(); + itemId = Integer.parseInt(args.get(0)); + mainPropId = Integer.parseInt(args.get(1)); + fromIdx = 2; + } else { + target = Integer.parseInt(args.get(0)); + itemId = Integer.parseInt(args.get(1)); + mainPropId = Integer.parseInt(args.get(2)); + fromIdx = 3; + } + args.subList(fromIdx, size).forEach(it -> { + String[] arr; + int n = 1; + if ((arr = it.split(",")).length == 2) { + it = arr[0]; + n = Integer.parseInt(arr[1]); + } + appendPropIdList.addAll(Collections.nCopies(n, Integer.parseInt(it))); + }); + } catch (Exception ignored) { + CommandHandler.sendMessage(sender, msg); + return; + } + } else { + CommandHandler.sendMessage(sender, msg); + return; + } + + Player targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); + if (targetPlayer == null) { + CommandHandler.sendMessage(sender, "Player not found."); + return; + } + + ItemData itemData = GameData.getItemDataMap().get(itemId); + + if (itemData.getItemType() != ItemType.ITEM_RELIQUARY) { + CommandHandler.sendMessage(sender, "Invalid artifact ID."); + return; + } + + GameItem item = new GameItem(itemData); + item.setLevel(level); + item.setMainPropId(mainPropId); + item.getAppendPropIdList().clear();//Clear default random props first + item.getAppendPropIdList().addAll(appendPropIdList); + targetPlayer.getInventory().addItem(item, ActionReason.SubfieldDrop); + + CommandHandler.sendMessage(sender, String.format("Given %s to %s.", itemId, target)); + } +} + 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/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 dbd6ef854..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("*") ? true : false; + 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/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/EntityVehicle.java b/src/main/java/emu/grasscutter/game/entity/EntityVehicle.java index c7609f89b..09f80e15b 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityVehicle.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityVehicle.java @@ -18,24 +18,30 @@ import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo; import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo; import emu.grasscutter.net.proto.VectorOuterClass.Vector; import emu.grasscutter.net.proto.VehicleInfoOuterClass.*; - +import emu.grasscutter.net.proto.VehicleMemberOuterClass.*; import emu.grasscutter.utils.Position; import emu.grasscutter.utils.ProtoHelper; import it.unimi.dsi.fastutil.ints.Int2FloatMap; import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; +import java.util.List; +import java.util.ArrayList; + public class EntityVehicle extends EntityBaseGadget { + private final Player owner; private final Int2FloatOpenHashMap fightProp; private final Position pos; private final Position rot; - private float curStamina; private final int pointId; private final int gadgetId; + private float curStamina; + private List vehicleMembers; + public EntityVehicle(Scene scene, Player player, int gadgetId, int pointId, Position pos, Position rot) { super(scene); this.owner = player; @@ -46,6 +52,7 @@ public class EntityVehicle extends EntityBaseGadget { this.gadgetId = gadgetId; this.pointId = pointId; this.curStamina = 240; + this.vehicleMembers = new ArrayList(); } @Override @@ -61,6 +68,8 @@ public class EntityVehicle extends EntityBaseGadget { public int getPointId() { return pointId; } + public List getVehicleMembers() { return vehicleMembers; } + @Override public Int2FloatOpenHashMap getFightProperties() { return fightProp; 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 8f3bef7f2..252c0a01c 100644 --- a/src/main/java/emu/grasscutter/game/inventory/GameItem.java +++ b/src/main/java/emu/grasscutter/game/inventory/GameItem.java @@ -244,6 +244,10 @@ public class GameItem { return mainPropId; } + public void setMainPropId(int mainPropId) { + this.mainPropId = mainPropId; + } + public List getAppendPropIdList() { return appendPropIdList; } diff --git a/src/main/java/emu/grasscutter/game/player/Player.java b/src/main/java/emu/grasscutter/game/player/Player.java index f440fcb1b..0637e3d43 100644 --- a/src/main/java/emu/grasscutter/game/player/Player.java +++ b/src/main/java/emu/grasscutter/game/player/Player.java @@ -822,7 +822,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); } diff --git a/src/main/java/emu/grasscutter/game/world/Scene.java b/src/main/java/emu/grasscutter/game/world/Scene.java index ee6b9a6a9..365b55982 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_WORLD) { + 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/packet/recv/HandlerGetOnlinePlayerListReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetOnlinePlayerListReq.java new file mode 100644 index 000000000..16dc591d5 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetOnlinePlayerListReq.java @@ -0,0 +1,15 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketGetOnlinePlayerListRsp; + +@Opcodes(PacketOpcodes.GetOnlinePlayerListReq) +public class HandlerGetOnlinePlayerListReq extends PacketHandler { + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + session.send(new PacketGetOnlinePlayerListRsp(session.getPlayer())); + } +} 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/PacketGetOnlinePlayerListRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGetOnlinePlayerListRsp.java new file mode 100644 index 000000000..4ea8a02a5 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGetOnlinePlayerListRsp.java @@ -0,0 +1,36 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.GetOnlinePlayerListReqOuterClass; +import emu.grasscutter.net.proto.GetOnlinePlayerListRspOuterClass.*; +import emu.grasscutter.net.proto.MpSettingTypeOuterClass; +import emu.grasscutter.net.proto.OnlinePlayerInfoOuterClass.OnlinePlayerInfo; +import emu.grasscutter.net.proto.ProfilePictureOuterClass.ProfilePicture; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public class PacketGetOnlinePlayerListRsp extends BasePacket { + public PacketGetOnlinePlayerListRsp(Player session){ + super(PacketOpcodes.GetOnlinePlayerListRsp); + + List players = Grasscutter.getGameServer().getPlayers().values().stream().limit(50).toList(); + + GetOnlinePlayerListRsp.Builder proto = GetOnlinePlayerListRsp.newBuilder(); + + 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/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++) { diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketVehicleInteractRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketVehicleInteractRsp.java index 73476e821..989aa3876 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketVehicleInteractRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketVehicleInteractRsp.java @@ -1,6 +1,7 @@ package emu.grasscutter.server.packet.send; import emu.grasscutter.Grasscutter; +import emu.grasscutter.game.entity.EntityVehicle; import emu.grasscutter.game.player.Player; import emu.grasscutter.game.entity.GameEntity; @@ -17,16 +18,49 @@ public class PacketVehicleInteractRsp extends BasePacket { VehicleInteractRsp.Builder proto = VehicleInteractRsp.newBuilder(); GameEntity vehicle = player.getScene().getEntityById(entityId); - if(vehicle != null) { + + if(vehicle instanceof EntityVehicle) { proto.setEntityId(vehicle.getId()); - proto.setInteractType(interactType); VehicleMember vehicleMember = VehicleMember.newBuilder() .setUid(player.getUid()) .setAvatarGuid(player.getTeamManager().getCurrentCharacterGuid()) .build(); + proto.setInteractType(interactType); proto.setMember(vehicleMember); + + switch(interactType){ + case VEHICLE_INTERACT_IN -> { + ((EntityVehicle) vehicle).getVehicleMembers().add(vehicleMember); + } + case VEHICLE_INTERACT_OUT -> { + ((EntityVehicle) vehicle).getVehicleMembers().remove(vehicleMember); + } + default -> {} + } + } + this.setData(proto.build()); + } + + public PacketVehicleInteractRsp(EntityVehicle vehicle, VehicleMember vehicleMember, VehicleInteractType interactType) { + super(PacketOpcodes.VehicleInteractRsp); + VehicleInteractRsp.Builder proto = VehicleInteractRsp.newBuilder(); + + if(vehicle != null) { + proto.setEntityId(vehicle.getId()); + proto.setInteractType(interactType); + proto.setMember(vehicleMember); + + switch(interactType){ + case VEHICLE_INTERACT_IN -> { + vehicle.getVehicleMembers().add(vehicleMember); + } + case VEHICLE_INTERACT_OUT -> { + vehicle.getVehicleMembers().remove(vehicleMember); + } + default -> {} + } } this.setData(proto.build()); } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketVehicleSpawnRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketVehicleSpawnRsp.java index 69b3d8e6f..fe8b2a1f1 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketVehicleSpawnRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketVehicleSpawnRsp.java @@ -8,10 +8,16 @@ import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.PacketOpcodes; + +import emu.grasscutter.net.proto.VehicleMemberOuterClass.VehicleMember; import emu.grasscutter.net.proto.VehicleSpawnRspOuterClass.VehicleSpawnRsp; import emu.grasscutter.utils.Position; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; + +import java.util.List; + + +import static emu.grasscutter.net.proto.VehicleInteractTypeOuterClass.VehicleInteractType.VEHICLE_INTERACT_OUT; public class PacketVehicleSpawnRsp extends BasePacket { @@ -19,6 +25,23 @@ public class PacketVehicleSpawnRsp extends BasePacket { super(PacketOpcodes.VehicleSpawnRsp); VehicleSpawnRsp.Builder proto = VehicleSpawnRsp.newBuilder(); + // Eject vehicle members and Kill previous vehicles if there are any + List previousVehicles = player.getScene().getEntities().values().stream() + .filter(entity -> entity instanceof EntityVehicle + && ((EntityVehicle) entity).getGadgetId() == vehicleId + && ((EntityVehicle) entity).getOwner().equals(player)) + .toList(); + + previousVehicles.stream().forEach(entity -> { + List vehicleMembers = ((EntityVehicle) entity).getVehicleMembers().stream().toList(); + + vehicleMembers.stream().forEach(vehicleMember -> { + player.getScene().broadcastPacket(new PacketVehicleInteractRsp(((EntityVehicle) entity), vehicleMember, VEHICLE_INTERACT_OUT)); + }); + + player.getScene().killEntity(entity, 0); + }); + EntityVehicle vehicle = new EntityVehicle(player.getScene(), player, vehicleId, pointId, pos, rot); switch (vehicleId) {