Implement dungeon entry

This commit is contained in:
Melledy 2022-04-28 08:20:37 -07:00
parent 472e771dc8
commit 17addc1522
19 changed files with 318 additions and 48 deletions

View File

@ -0,0 +1,8 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message PlayerEnterDungeonReq {
uint32 point_id = 1;
uint32 dungeon_id = 2;
}

View File

@ -0,0 +1,9 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message PlayerEnterDungeonRsp {
int32 retcode = 1;
uint32 point_id = 2;
uint32 dungeon_id = 3;
}

View File

@ -0,0 +1,8 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message PlayerQuitDungeonReq {
uint32 point_id = 1;
bool is_quit_immediately = 2;
}

View File

@ -0,0 +1,8 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message PlayerQuitDungeonRsp {
int32 retcode = 1;
uint32 point_id = 2;
}

View File

@ -61,6 +61,7 @@ public class GameData {
private static final Int2ObjectMap<FetterCharacterCardData> fetterCharacterCardDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<FetterCharacterCardData> fetterCharacterCardDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<RewardData> rewardDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<RewardData> rewardDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<WorldLevelData> worldLevelDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<WorldLevelData> worldLevelDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<DungeonData> dungeonDataMap = new Int2ObjectOpenHashMap<>();
// Cache // Cache
private static Map<Integer, List<Integer>> fetters = new HashMap<>(); private static Map<Integer, List<Integer>> fetters = new HashMap<>();
@ -97,6 +98,11 @@ public class GameData {
public static Map<String, ScenePointEntry> getScenePointEntries() { public static Map<String, ScenePointEntry> getScenePointEntries() {
return scenePointEntries; return scenePointEntries;
} }
// TODO optimize
public static ScenePointEntry getScenePointEntryById(int sceneId, int pointId) {
return getScenePointEntries().get(sceneId + "_" + pointId);
}
public static Int2ObjectMap<AvatarData> getAvatarDataMap() { public static Int2ObjectMap<AvatarData> getAvatarDataMap() {
return avatarDataMap; return avatarDataMap;
@ -265,4 +271,8 @@ public class GameData {
public static Int2ObjectMap<WorldLevelData> getWorldLevelDataMap() { public static Int2ObjectMap<WorldLevelData> getWorldLevelDataMap() {
return worldLevelDataMap; return worldLevelDataMap;
} }
public static Int2ObjectMap<DungeonData> getDungeonDataMap() {
return dungeonDataMap;
}
} }

View File

@ -164,6 +164,7 @@ public class ResourceLoader {
for (Map.Entry<String, JsonElement> entry : config.points.entrySet()) { for (Map.Entry<String, JsonElement> entry : config.points.entrySet()) {
PointData pointData = Grasscutter.getGsonFactory().fromJson(entry.getValue(), PointData.class); PointData pointData = Grasscutter.getGsonFactory().fromJson(entry.getValue(), PointData.class);
pointData.setId(Integer.parseInt(entry.getKey()));
ScenePointEntry sl = new ScenePointEntry(sceneId + "_" + entry.getKey(), pointData); ScenePointEntry sl = new ScenePointEntry(sceneId + "_" + entry.getKey(), pointData);
scenePointList.add(sl); scenePointList.add(sl);

View File

@ -1,43 +1,30 @@
package emu.grasscutter.data.common; package emu.grasscutter.data.common;
public class PointData { import emu.grasscutter.utils.Position;
private pos tranPos;
public pos getTranPos() { public class PointData {
private int id;
private String $type;
private Position tranPos;
private int[] dungeonIds;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getType() {
return $type;
}
public Position getTranPos() {
return tranPos; return tranPos;
} }
public void setTranPos(pos tranPos) { public int[] getDungeonIds() {
this.tranPos = tranPos; return dungeonIds;
} }
public class pos {
private float x;
private float y;
private float z;
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
public float getZ() {
return z;
}
public void setZ(float z) {
this.z = z;
}
}
} }

View File

@ -0,0 +1,28 @@
package emu.grasscutter.data.def;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.SceneType;
@ResourceType(name = "DungeonExcelConfigData.json")
public class DungeonData extends GameResource {
private int Id;
private int SceneId;
private String InvolveType; // TODO enum
@Override
public int getId() {
return this.Id;
}
public int getSceneId() {
return SceneId;
}
@Override
public void onLoad() {
}
}

View File

@ -9,8 +9,9 @@ import emu.grasscutter.game.props.SceneType;
@ResourceType(name = "SceneExcelConfigData.json") @ResourceType(name = "SceneExcelConfigData.json")
public class SceneData extends GameResource { public class SceneData extends GameResource {
private int Id; private int Id;
private SceneType SceneType; private SceneType Type;
private String ScriptData; private String ScriptData;
@Override @Override
public int getId() { public int getId() {
@ -18,7 +19,7 @@ public class SceneData extends GameResource {
} }
public SceneType getSceneType() { public SceneType getSceneType() {
return SceneType; return Type;
} }
public String getScriptData() { public String getScriptData() {
@ -27,6 +28,6 @@ public class SceneData extends GameResource {
@Override @Override
public void onLoad() { public void onLoad() {
} }
} }

View File

@ -1,6 +1,18 @@
package emu.grasscutter.game.dungeons; package emu.grasscutter.game.dungeons;
import emu.grasscutter.GameConstants;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.custom.ScenePointEntry;
import emu.grasscutter.data.def.DungeonData;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.SceneType;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.server.packet.send.PacketDungeonEntryInfoRsp;
import emu.grasscutter.server.packet.send.PacketPlayerEnterDungeonRsp;
import emu.grasscutter.utils.Position;
public class DungeonManager { public class DungeonManager {
private final GameServer server; private final GameServer server;
@ -12,4 +24,55 @@ public class DungeonManager {
public GameServer getServer() { public GameServer getServer() {
return server; return server;
} }
public void getEntryInfo(Player player, int pointId) {
ScenePointEntry entry = GameData.getScenePointEntryById(player.getScene().getId(), pointId);
if (entry == null || entry.getPointData().getDungeonIds() == null) {
// Error
player.sendPacket(new PacketDungeonEntryInfoRsp());
return;
}
player.sendPacket(new PacketDungeonEntryInfoRsp(player, entry.getPointData()));
}
public void enterDungeon(Player player, int pointId, int dungeonId) {
DungeonData data = GameData.getDungeonDataMap().get(dungeonId);
if (data == null) {
return;
}
int sceneId = data.getSceneId();
player.getWorld().transferPlayerToScene(player, sceneId, data);
player.sendPacket(new PacketPlayerEnterDungeonRsp(pointId, dungeonId));
}
public void exitDungeon(Player player) {
if (player.getScene().getSceneType() != SceneType.SCENE_DUNGEON) {
return;
}
// Get previous scene
int prevScene = player.getScene().getPrevScene() > 0 ? player.getScene().getPrevScene() : 3;
// Get previous position
DungeonData dungeonData = player.getScene().getDungeonData();
Position prevPos = new Position(GameConstants.START_POSITION);
if (dungeonData != null) {
ScenePointEntry entry = GameData.getScenePointEntryById(prevScene, dungeonData.getId());
if (entry != null) {
prevPos.set(entry.getPointData().getTranPos());
}
}
// Transfer player back to world
player.getWorld().transferPlayerToScene(player, prevScene, prevPos);
player.sendPacket(new BasePacket(PacketOpcodes.PlayerQuitDungeonRsp));
}
} }

View File

@ -3,6 +3,7 @@ package emu.grasscutter.game.world;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameDepot; import emu.grasscutter.data.GameDepot;
import emu.grasscutter.data.def.DungeonData;
import emu.grasscutter.data.def.MonsterData; import emu.grasscutter.data.def.MonsterData;
import emu.grasscutter.data.def.SceneData; import emu.grasscutter.data.def.SceneData;
import emu.grasscutter.data.def.WorldLevelData; import emu.grasscutter.data.def.WorldLevelData;
@ -41,6 +42,9 @@ public class Scene {
private int time; private int time;
private ClimateType climate; private ClimateType climate;
private int weather; private int weather;
private DungeonData dungeonData;
private int prevScene; // Id of the previous scene
public Scene(World world, SceneData sceneData) { public Scene(World world, SceneData sceneData) {
this.world = world; this.world = world;
@ -50,6 +54,7 @@ public class Scene {
this.time = 8 * 60; this.time = 8 * 60;
this.climate = ClimateType.CLIMATE_SUNNY; this.climate = ClimateType.CLIMATE_SUNNY;
this.prevScene = 3;
this.spawnedEntities = new HashSet<>(); this.spawnedEntities = new HashSet<>();
this.deadSpawnedEntities = new HashSet<>(); this.deadSpawnedEntities = new HashSet<>();
@ -111,6 +116,14 @@ public class Scene {
this.weather = weather; this.weather = weather;
} }
public int getPrevScene() {
return prevScene;
}
public void setPrevScene(int prevScene) {
this.prevScene = prevScene;
}
public boolean dontDestroyWhenEmpty() { public boolean dontDestroyWhenEmpty() {
return dontDestroyWhenEmpty; return dontDestroyWhenEmpty;
} }
@ -127,6 +140,17 @@ public class Scene {
return deadSpawnedEntities; return deadSpawnedEntities;
} }
public DungeonData getDungeonData() {
return dungeonData;
}
public void setDungeonData(DungeonData dungeonData) {
if (this.dungeonData != null || this.getSceneType() != SceneType.SCENE_DUNGEON || dungeonData.getSceneId() != this.getId()) {
return;
}
this.dungeonData = dungeonData;
}
public boolean isInScene(GameEntity entity) { public boolean isInScene(GameEntity entity) {
return this.entities.containsKey(entity.getId()); return this.entities.containsKey(entity.getId());
} }

View File

@ -17,6 +17,7 @@ import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.LifeState; import emu.grasscutter.game.props.LifeState;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.def.DungeonData;
import emu.grasscutter.data.def.SceneData; import emu.grasscutter.data.def.SceneData;
import emu.grasscutter.game.entity.EntityAvatar; import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.entity.EntityClientGadget; import emu.grasscutter.game.entity.EntityClientGadget;
@ -212,6 +213,14 @@ public class World implements Iterable<Player> {
} }
public boolean transferPlayerToScene(Player player, int sceneId, Position pos) { public boolean transferPlayerToScene(Player player, int sceneId, Position pos) {
return transferPlayerToScene(player, sceneId, null, pos);
}
public boolean transferPlayerToScene(Player player, int sceneId, DungeonData data) {
return transferPlayerToScene(player, sceneId, data, null);
}
public boolean transferPlayerToScene(Player player, int sceneId, DungeonData dungeonData, Position pos) {
if (GameData.getSceneDataMap().get(sceneId) == null) { if (GameData.getSceneDataMap().get(sceneId) == null) {
return false; return false;
} }
@ -224,25 +233,45 @@ public class World implements Iterable<Player> {
// Dont deregister scenes if the player is going to tp back into them // Dont deregister scenes if the player is going to tp back into them
if (oldScene.getId() == sceneId) { if (oldScene.getId() == sceneId) {
oldScene.setDontDestroyWhenEmpty(true); oldScene.setDontDestroyWhenEmpty(true);
} }
oldScene.removePlayer(player); oldScene.removePlayer(player);
} }
Scene newScene = this.getSceneById(sceneId); Scene newScene = this.getSceneById(sceneId);
newScene.setDungeonData(dungeonData);
newScene.addPlayer(player); newScene.addPlayer(player);
player.getPos().set(pos);
// Dungeon
if (dungeonData != null) {
// TODO set position
}
// Set player position
if (pos != null) {
player.getPos().set(pos);
} else {
pos = player.getPos();
}
if (oldScene != null) { if (oldScene != null) {
newScene.setPrevScene(oldScene.getId());
oldScene.setDontDestroyWhenEmpty(false); oldScene.setDontDestroyWhenEmpty(false);
} }
// Teleport packet // Get enter types
if (oldScene == newScene) { EnterType enterType = EnterType.ENTER_JUMP;
player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterType.ENTER_GOTO, EnterReason.TransPoint, sceneId, pos)); EnterReason enterReason = EnterReason.TransPoint;
} else {
player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterType.ENTER_JUMP, EnterReason.TransPoint, sceneId, pos)); if (dungeonData != null) {
enterType = EnterType.ENTER_DUNGEON;
enterReason = EnterReason.DungeonEnter;
} else if (oldScene == newScene) {
enterType = EnterType.ENTER_GOTO;
} }
// Teleport packet
player.sendPacket(new PacketPlayerEnterSceneNotify(player, enterType, enterReason, sceneId, pos));
return true; return true;
} }

View File

@ -2,6 +2,7 @@ package emu.grasscutter.server.packet.recv;
import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.DungeonEntryInfoReqOuterClass.DungeonEntryInfoReq;
import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.game.GameSession;
@ -10,7 +11,9 @@ public class HandlerDungeonEntryInfoReq extends PacketHandler {
@Override @Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
DungeonEntryInfoReq req = DungeonEntryInfoReq.parseFrom(payload);
session.getServer().getDungeonManager().getEntryInfo(session.getPlayer(), req.getPointId());
} }
} }

View File

@ -0,0 +1,20 @@
package emu.grasscutter.server.packet.recv;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.PlayerEnterDungeonReqOuterClass.PlayerEnterDungeonReq;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.server.game.GameSession;
@Opcodes(PacketOpcodes.PlayerEnterDungeonReq)
public class HandlerPlayerEnterDungeonReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
// Auto template
PlayerEnterDungeonReq req = PlayerEnterDungeonReq.parseFrom(payload);
session.getServer().getDungeonManager().enterDungeon(session.getPlayer(), req.getPointId(), req.getDungeonId());
}
}

View File

@ -0,0 +1,16 @@
package emu.grasscutter.server.packet.recv;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.server.game.GameSession;
@Opcodes(PacketOpcodes.PlayerQuitDungeonReq)
public class HandlerPlayerQuitDungeonReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
session.getPlayer().getServer().getDungeonManager().exitDungeon(session.getPlayer());
}
}

View File

@ -0,0 +1,37 @@
package emu.grasscutter.server.packet.send;
import java.util.Arrays;
import emu.grasscutter.data.common.PointData;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.DungeonEntryInfoOuterClass.DungeonEntryInfo;
import emu.grasscutter.net.proto.DungeonEntryInfoRspOuterClass.DungeonEntryInfoRsp;
public class PacketDungeonEntryInfoRsp extends BasePacket {
public PacketDungeonEntryInfoRsp(Player player, PointData pointData) {
super(PacketOpcodes.DungeonEntryInfoRsp);
DungeonEntryInfoRsp.Builder proto = DungeonEntryInfoRsp.newBuilder()
.setPointId(pointData.getId());
for (int dungeonId : pointData.getDungeonIds()) {
DungeonEntryInfo info = DungeonEntryInfo.newBuilder().setDungeonId(dungeonId).build();
proto.addDungeonEntryList(info);
}
this.setData(proto);
}
public PacketDungeonEntryInfoRsp() {
super(PacketOpcodes.DungeonEntryInfoRsp);
DungeonEntryInfoRsp proto = DungeonEntryInfoRsp.newBuilder()
.setRetcode(1)
.build();
this.setData(proto);
}
}

View File

@ -7,7 +7,5 @@ public class PacketPathfindingEnterSceneRsp extends BasePacket {
public PacketPathfindingEnterSceneRsp(int clientSequence) { public PacketPathfindingEnterSceneRsp(int clientSequence) {
super(PacketOpcodes.PathfindingEnterSceneRsp); super(PacketOpcodes.PathfindingEnterSceneRsp);
this.buildHeader(clientSequence);
} }
} }

View File

@ -0,0 +1,19 @@
package emu.grasscutter.server.packet.send;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.PlayerEnterDungeonRspOuterClass.PlayerEnterDungeonRsp;
public class PacketPlayerEnterDungeonRsp extends BasePacket {
public PacketPlayerEnterDungeonRsp(int pointId, int dungeonId) {
super(PacketOpcodes.PlayerEnterDungeonRsp);
PlayerEnterDungeonRsp proto = PlayerEnterDungeonRsp.newBuilder()
.setPointId(pointId)
.setDungeonId(dungeonId)
.build();
this.setData(proto);
}
}

View File

@ -65,6 +65,7 @@ public final class Utils {
private static final char[] HEX_ARRAY = "0123456789abcdef".toCharArray(); private static final char[] HEX_ARRAY = "0123456789abcdef".toCharArray();
public static String bytesToHex(byte[] bytes) { public static String bytesToHex(byte[] bytes) {
if (bytes == null) return "";
char[] hexChars = new char[bytes.length * 2]; char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) { for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF; int v = bytes[j] & 0xFF;