From c38cf693ba41db17d6d0e842debbe8b610d75bd7 Mon Sep 17 00:00:00 2001 From: Melledy <121644117+Melledy@users.noreply.github.com> Date: Wed, 25 Oct 2023 08:53:16 -0700 Subject: [PATCH] Rewrite how scenes load entities to support spawning the correct rogue monsters --- .../java/emu/lunarcore/data/GameData.java | 1 + .../data/excel/RogueMonsterExcel.java | 20 +++ .../lunarcore/data/excel/RogueRoomExcel.java | 10 +- .../emu/lunarcore/game/enums/PlaneType.java | 26 ++-- .../emu/lunarcore/game/rogue/RogueData.java | 2 + .../game/rogue/RogueEntityLoader.java | 40 ++++++ .../lunarcore/game/rogue/RogueManager.java | 26 ++-- .../java/emu/lunarcore/game/scene/Scene.java | 119 +++++------------- .../game/scene/SceneEntityLoader.java | 103 +++++++++++++++ 9 files changed, 240 insertions(+), 107 deletions(-) create mode 100644 src/main/java/emu/lunarcore/data/excel/RogueMonsterExcel.java create mode 100644 src/main/java/emu/lunarcore/game/rogue/RogueEntityLoader.java create mode 100644 src/main/java/emu/lunarcore/game/scene/SceneEntityLoader.java diff --git a/src/main/java/emu/lunarcore/data/GameData.java b/src/main/java/emu/lunarcore/data/GameData.java index ec25b8f..9168385 100644 --- a/src/main/java/emu/lunarcore/data/GameData.java +++ b/src/main/java/emu/lunarcore/data/GameData.java @@ -37,6 +37,7 @@ public class GameData { @Getter private static Int2ObjectMap rogueAreaExcelMap = new Int2ObjectOpenHashMap<>(); @Getter private static Int2ObjectMap rogueManagerExcelMap = new Int2ObjectOpenHashMap<>(); @Getter private static Int2ObjectMap rogueRoomExcelMap = new Int2ObjectOpenHashMap<>(); + @Getter private static Int2ObjectMap rogueMonsterExcelMap = new Int2ObjectOpenHashMap<>(); private static Int2ObjectMap avatarPromotionExcelMap = new Int2ObjectOpenHashMap<>(); private static Int2ObjectMap avatarSkillTreeExcelMap = new Int2ObjectOpenHashMap<>(); diff --git a/src/main/java/emu/lunarcore/data/excel/RogueMonsterExcel.java b/src/main/java/emu/lunarcore/data/excel/RogueMonsterExcel.java new file mode 100644 index 0000000..a83e630 --- /dev/null +++ b/src/main/java/emu/lunarcore/data/excel/RogueMonsterExcel.java @@ -0,0 +1,20 @@ +package emu.lunarcore.data.excel; + +import emu.lunarcore.data.GameResource; +import emu.lunarcore.data.ResourceType; + +import lombok.Getter; + +@Getter +@ResourceType(name = {"RogueMonster.json"}) +public class RogueMonsterExcel extends GameResource { + private int RogueMonsterID; + private int NpcMonsterID; + private int EventID; + + @Override + public int getId() { + return RogueMonsterID; + } + +} diff --git a/src/main/java/emu/lunarcore/data/excel/RogueRoomExcel.java b/src/main/java/emu/lunarcore/data/excel/RogueRoomExcel.java index 89f07c4..5f1c85f 100644 --- a/src/main/java/emu/lunarcore/data/excel/RogueRoomExcel.java +++ b/src/main/java/emu/lunarcore/data/excel/RogueRoomExcel.java @@ -1,10 +1,8 @@ package emu.lunarcore.data.excel; -import java.util.Map; - import emu.lunarcore.data.GameResource; import emu.lunarcore.data.ResourceType; - +import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import lombok.Getter; @Getter @@ -14,11 +12,15 @@ public class RogueRoomExcel extends GameResource { private int RogueRoomType; private int MapEntrance; private int GroupID; - private Map GroupWithContent; + private Int2IntOpenHashMap GroupWithContent; @Override public int getId() { return RogueRoomID; } + public int getGroupContent(int id) { + return GroupWithContent.get(id); + } + } diff --git a/src/main/java/emu/lunarcore/game/enums/PlaneType.java b/src/main/java/emu/lunarcore/game/enums/PlaneType.java index d5eaa17..77891ac 100644 --- a/src/main/java/emu/lunarcore/game/enums/PlaneType.java +++ b/src/main/java/emu/lunarcore/game/enums/PlaneType.java @@ -1,22 +1,30 @@ package emu.lunarcore.game.enums; +import emu.lunarcore.game.rogue.RogueEntityLoader; +import emu.lunarcore.game.scene.SceneEntityLoader; import lombok.Getter; @Getter public enum PlaneType { - Unknown (0), - Town (1), - Maze (2), - Train (3), - Challenge (4), - Rogue (5), - Raid (6), - AetherDivide (7), - TrialActivity (8); + Unknown (0), + Town (1), + Maze (2), + Train (3), + Challenge (4), + Rogue (5, new RogueEntityLoader()), + Raid (6), + AetherDivide (7), + TrialActivity (8); private final int val; + private final SceneEntityLoader sceneEntityLoader; private PlaneType(int value) { + this(value, new SceneEntityLoader()); + } + + private PlaneType(int value, SceneEntityLoader entityLoader) { this.val = value; + this.sceneEntityLoader = entityLoader; } } \ No newline at end of file diff --git a/src/main/java/emu/lunarcore/game/rogue/RogueData.java b/src/main/java/emu/lunarcore/game/rogue/RogueData.java index 01a5b89..e284347 100644 --- a/src/main/java/emu/lunarcore/game/rogue/RogueData.java +++ b/src/main/java/emu/lunarcore/game/rogue/RogueData.java @@ -58,6 +58,8 @@ public class RogueData { public RogueRoomData getCurrentRoom() { return this.rooms.get(this.getCurrentSiteId()); } + + // Serialization public RogueCurrentInfo toProto() { var proto = RogueCurrentInfo.newInstance() diff --git a/src/main/java/emu/lunarcore/game/rogue/RogueEntityLoader.java b/src/main/java/emu/lunarcore/game/rogue/RogueEntityLoader.java new file mode 100644 index 0000000..f608d35 --- /dev/null +++ b/src/main/java/emu/lunarcore/game/rogue/RogueEntityLoader.java @@ -0,0 +1,40 @@ +package emu.lunarcore.game.rogue; + +import emu.lunarcore.data.GameData; +import emu.lunarcore.data.config.GroupInfo; +import emu.lunarcore.data.config.MonsterInfo; +import emu.lunarcore.data.excel.NpcMonsterExcel; +import emu.lunarcore.data.excel.RogueMonsterExcel; +import emu.lunarcore.game.scene.Scene; +import emu.lunarcore.game.scene.SceneEntityLoader; +import emu.lunarcore.game.scene.entity.EntityMonster; + +public class RogueEntityLoader extends SceneEntityLoader { + + public EntityMonster loadMonster(Scene scene, GroupInfo group, MonsterInfo monsterInfo) { + // Make sure player is in a rogue instance + RogueData rogue = scene.getPlayer().getRogueData(); + if (rogue == null) return null; + + // Get rogue group content + int content = rogue.getCurrentRoom().getExcel().getGroupContent(group.getId()); + if (content <= 0) return null; + + // Get rogue monster excel and npc monster excel + RogueMonsterExcel rogueMonster = GameData.getRogueMonsterExcelMap().get((content * 10) + 1); + if (rogueMonster == null) return null; + + NpcMonsterExcel npcMonster = GameData.getNpcMonsterExcelMap().get(rogueMonster.getNpcMonsterID()); + if (npcMonster == null) return null; + + // Actually create the monster now + EntityMonster monster = new EntityMonster(scene, npcMonster, monsterInfo.getPos()); + monster.getRot().set(monsterInfo.getRot()); + monster.setGroupId(group.getId()); + monster.setInstId(monsterInfo.getID()); + monster.setEventId(rogueMonster.getEventID()); + monster.setOverrideStageId(rogueMonster.getEventID()); + + return monster; + } +} diff --git a/src/main/java/emu/lunarcore/game/rogue/RogueManager.java b/src/main/java/emu/lunarcore/game/rogue/RogueManager.java index 27b5c49..850fbd0 100644 --- a/src/main/java/emu/lunarcore/game/rogue/RogueManager.java +++ b/src/main/java/emu/lunarcore/game/rogue/RogueManager.java @@ -30,6 +30,12 @@ public class RogueManager extends BasePlayerManager { } public void startRogue(int areaId, RepeatedInt avatarIdList) { + // Make sure player already isnt in a rogue instance + if (getPlayer().getRogueData() != null) { + getPlayer().sendPacket(new PacketStartRogueScRsp()); + return; + } + // Get excel var excel = GameData.getRogueAreaExcelMap().get(areaId); if (excel == null || !excel.isValid()) { @@ -72,13 +78,13 @@ public class RogueManager extends BasePlayerManager { getPlayer().sendPacket(new PacketStartRogueScRsp()); return; } - - // Load scene groups - RogueRoomExcel roomExcel = data.getCurrentRoom().getExcel(); - for (var entry : roomExcel.getGroupWithContent().entrySet()) { - getPlayer().getScene().loadGroup(entry.getKey()); - } + // Set rogue data + getPlayer().setRogueData(data); + + // Get room excel + RogueRoomExcel roomExcel = data.getCurrentRoom().getExcel(); + // Move player to rogue start position AnchorInfo anchor = getPlayer().getScene().getFloorInfo().getAnchorInfo(roomExcel.getGroupID(), 1); if (anchor != null) { @@ -86,8 +92,12 @@ public class RogueManager extends BasePlayerManager { getPlayer().getRot().set(anchor.getRot()); } - // Set rogue data and send packet - getPlayer().setRogueData(data); + // Load scene groups. THIS NEEDS TO BE LAST + for (int key : roomExcel.getGroupWithContent().keySet()) { + getPlayer().getScene().loadGroup(key); + } + + // Done getPlayer().sendPacket(new PacketStartRogueScRsp(getPlayer())); } diff --git a/src/main/java/emu/lunarcore/game/scene/Scene.java b/src/main/java/emu/lunarcore/game/scene/Scene.java index b9e433b..8ccdc97 100644 --- a/src/main/java/emu/lunarcore/game/scene/Scene.java +++ b/src/main/java/emu/lunarcore/game/scene/Scene.java @@ -7,12 +7,8 @@ import emu.lunarcore.data.GameData; import emu.lunarcore.data.config.*; import emu.lunarcore.data.config.GroupInfo.GroupLoadSide; import emu.lunarcore.data.excel.MazePlaneExcel; -import emu.lunarcore.data.excel.NpcMonsterExcel; -import emu.lunarcore.data.excel.PropExcel; import emu.lunarcore.game.avatar.GameAvatar; import emu.lunarcore.game.enums.PlaneType; -import emu.lunarcore.game.enums.PropState; -import emu.lunarcore.game.enums.PropType; import emu.lunarcore.game.player.PlayerLineup; import emu.lunarcore.game.scene.entity.*; import emu.lunarcore.game.scene.triggers.PropTrigger; @@ -23,6 +19,7 @@ import emu.lunarcore.proto.SceneGroupStateOuterClass.SceneGroupState; import emu.lunarcore.proto.SceneInfoOuterClass.SceneInfo; import emu.lunarcore.server.packet.send.PacketActivateFarmElementScRsp; import emu.lunarcore.server.packet.send.PacketSceneGroupRefreshScNotify; + import it.unimi.dsi.fastutil.ints.*; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import lombok.Getter; @@ -32,6 +29,7 @@ public class Scene { private final Player player; private final MazePlaneExcel excel; private final FloorInfo floorInfo; + private final SceneEntityLoader entityLoader; private final int planeId; private final int floorId; private int entryId; @@ -50,7 +48,7 @@ public class Scene { // Cache private List triggers; private List healingSprings; - + public Scene(Player player, MazePlaneExcel excel, int floorId) { this.player = player; this.excel = excel; @@ -65,7 +63,11 @@ public class Scene { this.healingSprings = new ObjectArrayList<>(); this.triggers = new ObjectArrayList<>(); + + // Use singleton to avoid allocating memory for a new entity loader everytime we create a scene + this.entityLoader = getExcel().getPlaneType().getSceneEntityLoader(); + // Add avatar entities PlayerLineup lineup = getPlayer().getCurrentLineup(); for (int avatarId : lineup.getAvatars()) { @@ -115,97 +117,36 @@ public class Scene { // Add monsters if (group.getMonsterList() != null && group.getMonsterList().size() > 0) { for (MonsterInfo monsterInfo : group.getMonsterList()) { - // Don't spawn entity if they have the IsDelete flag in group info - if (monsterInfo.isIsDelete()) continue; - - // Get excels from game data - NpcMonsterExcel npcMonsterExcel = GameData.getNpcMonsterExcelMap().get(monsterInfo.getNPCMonsterID()); - if (npcMonsterExcel == null) continue; - - // Create monster with excels - EntityMonster monster = new EntityMonster(this, npcMonsterExcel, monsterInfo.getPos()); - monster.getRot().set(monsterInfo.getRot()); - monster.setInstId(monsterInfo.getID()); - monster.setEventId(monsterInfo.getEventID()); - monster.setGroupId(group.getId()); - monster.setWorldLevel(this.getPlayer().getWorldLevel()); - - // Add to monsters - this.addEntity(monster); + try { + EntityMonster monster = this.getEntityLoader().loadMonster(this, group, monsterInfo); + this.addEntity(monster); + } catch (Exception e) { + // Ignored + } } } // Add props if (group.getPropList() != null && group.getPropList().size() > 0) { for (PropInfo propInfo : group.getPropList()) { - // Don't spawn entity if they have the IsDelete flag in group info - if (propInfo.isIsDelete()) continue; - - // Get prop excel - PropExcel propExcel = GameData.getPropExcelMap().get(propInfo.getPropID()); - if (propExcel == null) { - continue; + try { + EntityProp prop = this.getEntityLoader().loadNpc(this, group, propInfo); + this.addEntity(prop); + } catch (Exception e) { + // Ignored } - - // Create prop from prop info - EntityProp prop = new EntityProp(this, propExcel, propInfo.getPos()); - prop.setState(propInfo.getState()); - prop.getRot().set(propInfo.getRot()); - prop.setInstId(propInfo.getID()); - prop.setGroupId(group.getId()); - prop.setPropInfo(propInfo); - - // Cache - if (prop.getPropId() == 1003) { - // Hacky fix to open simulated universe - if (propInfo.getMappingInfoID() == 2220) { - // Regular simulated universe is locked behind a mission requirement by default - prop.setState(PropState.Open); - } else { - // Skip tutorial simulated universe - continue; - } - } else if (prop.getExcel().getPropType() == PropType.PROP_SPRING) { - // Cache teleport anchors - this.getHealingSprings().add(prop); - } - - // Add trigger - if (propInfo.getTrigger() != null) { - this.getTriggers().add(propInfo.getTrigger()); - } - - // Add to monsters - this.addEntity(prop); } } - // Add npcs + // Add NPCs if (group.getNPCList() != null && group.getNPCList().size() > 0) { for (NpcInfo npcInfo : group.getNPCList()) { - // Don't spawn entity if they have the IsDelete flag in group info - if (npcInfo.isIsDelete() || !GameData.getNpcExcelMap().containsKey(npcInfo.getNPCID())) { - continue; + try { + EntityNpc npc = this.getEntityLoader().loadNpc(this, group, npcInfo); + this.addEntity(npc); + } catch (Exception e) { + // Ignored } - - // Dont spawn duplicate NPCs - boolean haseDuplicateNpcId = false; - for (GameEntity entity : this.getEntities().values()) { - if (entity instanceof EntityNpc eNpc && eNpc.getNpcId() == npcInfo.getNPCID()) { - haseDuplicateNpcId = true; - break; - } - } - if (haseDuplicateNpcId) continue; - - // Create npc from npc info - EntityNpc npc = new EntityNpc(this, npcInfo.getNPCID(), npcInfo.getPos()); - npc.getRot().set(npcInfo.getRot()); - npc.setInstId(npcInfo.getID()); - npc.setGroupId(group.getId()); - - // Add to monsters - this.addEntity(npc); } } } @@ -325,13 +266,18 @@ public class Scene { } public synchronized void addEntity(GameEntity entity, boolean sendPacket) { - // Dont add if monster id already exists - if (entity.getEntityId() != 0) return; + // Sanity checks - Also dont add entity if it already exists + if (entity == null || entity.getEntityId() != 0) { + return; + } + // Set entity id and add monster to entity map entity.setEntityId(this.getNextEntityId()); this.getEntities().put(entity.getEntityId(), entity); + // Entity add callback entity.onAdd(); + // Send packet if (sendPacket) { player.sendPacket(new PacketSceneGroupRefreshScNotify(entity, null)); @@ -346,7 +292,8 @@ public class Scene { GameEntity entity = this.getEntities().remove(entityId); if (entity != null) { - // Entity remove callback + // Reset entity id and run event + entity.setEntityId(0); entity.onRemove(); // Send packet player.sendPacket(new PacketSceneGroupRefreshScNotify(null, entity)); diff --git a/src/main/java/emu/lunarcore/game/scene/SceneEntityLoader.java b/src/main/java/emu/lunarcore/game/scene/SceneEntityLoader.java new file mode 100644 index 0000000..119c331 --- /dev/null +++ b/src/main/java/emu/lunarcore/game/scene/SceneEntityLoader.java @@ -0,0 +1,103 @@ +package emu.lunarcore.game.scene; + +import emu.lunarcore.data.GameData; +import emu.lunarcore.data.config.GroupInfo; +import emu.lunarcore.data.config.MonsterInfo; +import emu.lunarcore.data.config.NpcInfo; +import emu.lunarcore.data.config.PropInfo; +import emu.lunarcore.data.excel.NpcMonsterExcel; +import emu.lunarcore.data.excel.PropExcel; +import emu.lunarcore.game.enums.PropState; +import emu.lunarcore.game.enums.PropType; +import emu.lunarcore.game.scene.entity.EntityMonster; +import emu.lunarcore.game.scene.entity.EntityNpc; +import emu.lunarcore.game.scene.entity.EntityProp; +import emu.lunarcore.game.scene.entity.GameEntity; + +public class SceneEntityLoader { + + public EntityMonster loadMonster(Scene scene, GroupInfo group, MonsterInfo monsterInfo) { + // Don't spawn entity if they have the IsDelete flag in group info + if (monsterInfo.isIsDelete()) return null; + + // Get excels from game data + NpcMonsterExcel npcMonsterExcel = GameData.getNpcMonsterExcelMap().get(monsterInfo.getNPCMonsterID()); + if (npcMonsterExcel == null) return null; + + // Create monster with excels + EntityMonster monster = new EntityMonster(scene, npcMonsterExcel, monsterInfo.getPos()); + monster.getRot().set(monsterInfo.getRot()); + monster.setGroupId(group.getId()); + monster.setInstId(monsterInfo.getID()); + monster.setEventId(monsterInfo.getEventID()); + monster.setWorldLevel(scene.getPlayer().getWorldLevel()); + + return monster; + } + + public EntityProp loadNpc(Scene scene, GroupInfo group, PropInfo propInfo) { + // Don't spawn entity if they have the IsDelete flag in group info + if (propInfo.isIsDelete()) return null; + + // Get prop excel + PropExcel propExcel = GameData.getPropExcelMap().get(propInfo.getPropID()); + if (propExcel == null) { + return null; + } + + // Create prop from prop info + EntityProp prop = new EntityProp(scene, propExcel, propInfo.getPos()); + prop.setState(propInfo.getState()); + prop.getRot().set(propInfo.getRot()); + prop.setInstId(propInfo.getID()); + prop.setGroupId(group.getId()); + prop.setPropInfo(propInfo); + + // Cache + if (prop.getPropId() == 1003) { + // Hacky fix to open simulated universe + if (propInfo.getMappingInfoID() == 2220) { + // Regular simulated universe is locked behind a mission requirement by default + prop.setState(PropState.Open); + } else { + // Skip tutorial simulated universe + return null; + } + } else if (prop.getExcel().getPropType() == PropType.PROP_SPRING) { + // Cache teleport anchors + scene.getHealingSprings().add(prop); + } + + // Add trigger + if (propInfo.getTrigger() != null) { + scene.getTriggers().add(propInfo.getTrigger()); + } + + return prop; + } + + public EntityNpc loadNpc(Scene scene, GroupInfo group, NpcInfo npcInfo) { + // Don't spawn entity if they have the IsDelete flag in group info + if (npcInfo.isIsDelete() || !GameData.getNpcExcelMap().containsKey(npcInfo.getNPCID())) { + return null; + } + + // Dont spawn duplicate NPCs + boolean haseDuplicateNpcId = false; + for (GameEntity entity : scene.getEntities().values()) { + if (entity instanceof EntityNpc eNpc && eNpc.getNpcId() == npcInfo.getNPCID()) { + haseDuplicateNpcId = true; + break; + } + } + if (haseDuplicateNpcId) return null; + + // Create npc from npc info + EntityNpc npc = new EntityNpc(scene, npcInfo.getNPCID(), npcInfo.getPos()); + npc.getRot().set(npcInfo.getRot()); + npc.setInstId(npcInfo.getID()); + npc.setGroupId(group.getId()); + + return npc; + } +}