diff --git a/Common/Data/Excel/MarbleMatchInfoExcel.cs b/Common/Data/Excel/MarbleMatchInfoExcel.cs new file mode 100644 index 00000000..6d3ddb6a --- /dev/null +++ b/Common/Data/Excel/MarbleMatchInfoExcel.cs @@ -0,0 +1,17 @@ +namespace EggLink.DanhengServer.Data.Excel; + +[ResourceEntity("MarbleMatchInfo.json")] +public class MarbleMatchInfoExcel : ExcelResource +{ + public int ID { get; set; } + + public override int GetId() + { + return ID; + } + + public override void Loaded() + { + GameData.MarbleMatchInfoData.Add(ID, this); + } +} \ No newline at end of file diff --git a/Common/Data/Excel/MarbleSealExcel.cs b/Common/Data/Excel/MarbleSealExcel.cs new file mode 100644 index 00000000..69c29de1 --- /dev/null +++ b/Common/Data/Excel/MarbleSealExcel.cs @@ -0,0 +1,23 @@ +namespace EggLink.DanhengServer.Data.Excel; + +[ResourceEntity("MarbleSeal.json")] +public class MarbleSealExcel : ExcelResource +{ + public int ID { get; set; } + public int Attack { get; set; } + public float MaxSpeed { get; set; } + public float Mass { get; set; } + public int Hp { get; set; } + public int ActionPriority { get; set; } + public float Size { get; set; } + + public override int GetId() + { + return ID; + } + + public override void Loaded() + { + GameData.MarbleSealData.Add(ID, this); + } +} \ No newline at end of file diff --git a/Common/Data/GameData.cs b/Common/Data/GameData.cs index 734ad83e..14aa1502 100644 --- a/Common/Data/GameData.cs +++ b/Common/Data/GameData.cs @@ -18,6 +18,13 @@ public static class GameData public static ActivityConfig ActivityConfig { get; set; } = new(); + #region Marble + + public static Dictionary MarbleMatchInfoData { get; private set; } = []; + public static Dictionary MarbleSealData { get; private set; } = []; + + #endregion + #endregion #region Banners diff --git a/Common/Enums/Fight/MarbleNetWorkMsgEnum.cs b/Common/Enums/Fight/MarbleNetWorkMsgEnum.cs new file mode 100644 index 00000000..69cf111b --- /dev/null +++ b/Common/Enums/Fight/MarbleNetWorkMsgEnum.cs @@ -0,0 +1,21 @@ +namespace EggLink.DanhengServer.Enums.Fight; + +public enum MarbleNetWorkMsgEnum +{ + None = 0, + PlayerEnter = 1, + PerformanceFinish = 2, + LoadFinish = 3, + SimulateFinish = 4, + Launch = 5, + UseTech = 6, + TechFinish = 7, + Emoji = 8, + SnapShot = 9, + SyncBatch = 10, + GameStart = 11, + SyncNotify = 12, + GameFinish = 13, + SyncSnapShot = 14, + Operation = 15 +} \ No newline at end of file diff --git a/Common/Enums/Fight/MarblePlayerPhaseEnum.cs b/Common/Enums/Fight/MarblePlayerPhaseEnum.cs new file mode 100644 index 00000000..92de9d2d --- /dev/null +++ b/Common/Enums/Fight/MarblePlayerPhaseEnum.cs @@ -0,0 +1,12 @@ +namespace EggLink.DanhengServer.Enums.Fight; + +public enum MarblePlayerPhaseEnum +{ + NotEnter = 0, + EnterGame = 1, + LoadFinish = 2, + PerformanceFinish = 3, + Gaming = 4, + Launching = 5, + UseTech = 6 +} \ No newline at end of file diff --git a/GameServer/Game/Friend/FriendManager.cs b/GameServer/Game/Friend/FriendManager.cs index 51ff4953..84324655 100644 --- a/GameServer/Game/Friend/FriendManager.cs +++ b/GameServer/Game/Friend/FriendManager.cs @@ -126,7 +126,7 @@ public class FriendManager(PlayerInstance player) : BasePlayerManager(player) } // receive message - var recvPlayer = Listener.GetActiveConnection(recvUid)?.Player!; + var recvPlayer = Listener.GetActiveConnection(recvUid)?.Player; if (recvPlayer != null) { await recvPlayer.FriendManager!.ReceiveMessage(sendUid, recvUid, message, extraId); @@ -148,6 +148,19 @@ public class FriendManager(PlayerInstance player) : BasePlayerManager(player) } } + public async ValueTask SendInviteMessage(int sendUid, int recvUid, LobbyInviteInfo info) + { + var proto = new PacketRevcMsgScNotify((uint)recvUid, (uint)sendUid, info); + await Player.SendPacket(proto); + + // receive message + var recvPlayer = Listener.GetActiveConnection(recvUid)?.Player; + if (recvPlayer != null) + { + await recvPlayer.FriendManager!.ReceiveInviteMessage(sendUid, recvUid, info); + } + } + public async ValueTask ReceiveMessage(int sendUid, int recvUid, string? message = null, int? extraId = null) { var data = new FriendChatData @@ -176,6 +189,13 @@ public class FriendManager(PlayerInstance player) : BasePlayerManager(player) await Player.SendPacket(proto); } + public async ValueTask ReceiveInviteMessage(int sendUid, int recvUid, LobbyInviteInfo info) + { + var proto = new PacketRevcMsgScNotify((uint)recvUid, (uint)sendUid, info); + + await Player.SendPacket(proto); + } + public List GetHistoryInfo(int uid) { if (!FriendData.ChatHistory.TryGetValue(uid, out var history)) return []; diff --git a/GameServer/Game/Lobby/LobbyRoomInstance.cs b/GameServer/Game/Lobby/LobbyRoomInstance.cs new file mode 100644 index 00000000..21fd029e --- /dev/null +++ b/GameServer/Game/Lobby/LobbyRoomInstance.cs @@ -0,0 +1,113 @@ +using EggLink.DanhengServer.GameServer.Game.Lobby.Player; +using EggLink.DanhengServer.GameServer.Game.Player; +using EggLink.DanhengServer.GameServer.Server; +using EggLink.DanhengServer.GameServer.Server.Packet.Send.Lobby; +using EggLink.DanhengServer.GameServer.Server.Packet.Send.Multiplayer; +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Game.Lobby; + +public class LobbyRoomInstance(PlayerInstance owner, long roomId, FightGameMode gameMode, int lobbyMode) +{ + public long RoomId { get; set; } = roomId; + public FightGameMode GameMode { get; set; } = gameMode; + public List Players { get; set; } = []; + public int LobbyMode { get; set; } = lobbyMode; + public bool IsInGame { get; set; } + + public async ValueTask BroadCastToRoom(BasePacket packet) + { + foreach (var player in Players) + { + await player.Player.SendPacket(packet); + } + } + + public async ValueTask AddPlayer(PlayerInstance player, List sealList, LobbyCharacterType characterType) + { + await AddPlayer(new LobbyPlayerInstance(player, characterType) + { + EquippedSealList = sealList + }); + } + + public async ValueTask AddPlayer(LobbyPlayerInstance player) + { + if (Players.Any(x => x.Player.Uid == player.Player.Uid)) return; + await BroadCastToRoom(new PacketLobbySyncInfoScNotify(player.Player.Uid, this, LobbyModifyType.JoinLobby)); + Players.Add(player); + } + + public async ValueTask RemovePlayer(int uid) + { + var remove = Players.RemoveAll(x => x.Player.Uid == uid); + if (remove == 0) return; + + await BroadCastToRoom(new PacketLobbySyncInfoScNotify(uid, this, LobbyModifyType.QuitLobby)); + if (Players.Count == 0) + { + // remove from manager + ServerUtils.LobbyServerManager.RemoveLobbyRoom(RoomId); + } + } + + public async ValueTask LobbyStartFight() + { + // check status + if (IsInGame) + return Retcode.RetLobbyRoomPalyerFighting; + + if (Players.Count(x => x.CharacterType != LobbyCharacterType.LobbyCharacterWatcher) != 2) + return Retcode.RetLobbyRoomPalyerNotReady; + + if (Players.Any(x => + x.CharacterType == LobbyCharacterType.LobbyCharacterMember && + x.CharacterStatus != LobbyCharacterStatus.Ready)) return Retcode.RetLobbyRoomPalyerNotReady; + + if (Players.Any(x => + x.CharacterType != LobbyCharacterType.LobbyCharacterWatcher && + x.EquippedSealList.Count != 3)) return Retcode.RetLobbyRoomPalyerNotReady; + + var leader = Players.Find(x => x.CharacterType == LobbyCharacterType.LobbyCharacterLeader); + if (leader == null) return Retcode.RetLobbyRoomPalyerFighting; + + // start fight + foreach (var instance in Players) + { + instance.CharacterStatus = LobbyCharacterStatus.LobbyStartFight; + } + + await BroadCastToRoom(new PacketLobbySyncInfoScNotify(leader.Player.Uid, this, LobbyModifyType.LobbyStartFight)); + return Retcode.RetSucc; + } + + public async ValueTask StartFight() + { + // alrdy check status in lobby start fight + if (IsInGame) + return Retcode.RetLobbyRoomPalyerFighting; + + // create fight room + var fightRoom = ServerUtils.MultiPlayerGameServerManager.CreateRoom(this); + if (fightRoom == null) return Retcode.RetLobbyRoomNotExist; + + IsInGame = true; + await BroadCastToRoom(new PacketMultiplayerFightGameStartScNotify(fightRoom)); + + // start fight + foreach (var instance in Players) + { + instance.CharacterStatus = LobbyCharacterStatus.Fighting; + } + + await BroadCastToRoom(new PacketLobbySyncInfoScNotify(0, this, LobbyModifyType.FightStart)); + return Retcode.RetSucc; + } + + public LobbyPlayerInstance? GetPlayerByUid(int uid) + { + var player = Players.FirstOrDefault(x => x.Player.Uid == uid); + return player; + } +} \ No newline at end of file diff --git a/GameServer/Game/Lobby/LobbyServerManager.cs b/GameServer/Game/Lobby/LobbyServerManager.cs new file mode 100644 index 00000000..30c9b32e --- /dev/null +++ b/GameServer/Game/Lobby/LobbyServerManager.cs @@ -0,0 +1,42 @@ +using EggLink.DanhengServer.GameServer.Game.Player; +using EggLink.DanhengServer.GameServer.Server.Packet.Send.Lobby; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Game.Lobby; + +/// +/// Server Manager: +/// a manager would be only initialized when the server is started +/// +public class LobbyServerManager +{ + public long CurLobbyRoomId { get; set; } + public Dictionary LobbyRoomInstances { get; set; } = []; + + public async ValueTask CreateLobbyRoom(PlayerInstance ownerPlayer, int lobbyMode, List sealList) + { + var roomId = ++CurLobbyRoomId; + var room = new LobbyRoomInstance(ownerPlayer, roomId, FightGameMode.Marble, lobbyMode); + LobbyRoomInstances.Add(roomId, room); + await room.AddPlayer(ownerPlayer, sealList, LobbyCharacterType.LobbyCharacterLeader); + + return room; + } + + public void RemoveLobbyRoom(long roomId) + { + LobbyRoomInstances.Remove(roomId, out _); + } + + public LobbyRoomInstance? GetPlayerJoinedRoom(int uid) + { + foreach (var room in LobbyRoomInstances.Values) + { + if (room.Players.Any(x => x.Player.Uid == uid)) + { + return room; + } + } + return null; + } +} \ No newline at end of file diff --git a/GameServer/Game/Lobby/Player/LobbyPlayerInstance.cs b/GameServer/Game/Lobby/Player/LobbyPlayerInstance.cs new file mode 100644 index 00000000..01843005 --- /dev/null +++ b/GameServer/Game/Lobby/Player/LobbyPlayerInstance.cs @@ -0,0 +1,33 @@ +using EggLink.DanhengServer.GameServer.Game.Player; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Game.Lobby.Player; + +public class LobbyPlayerInstance(PlayerInstance player, LobbyCharacterType characterType) +{ + public PlayerInstance Player { get; } = player; + public List EquippedSealList { get; set; } = []; + public LobbyCharacterType CharacterType { get; set; } = characterType; + public LobbyCharacterStatus CharacterStatus { get; set; } = LobbyCharacterStatus.Idle; + + public LobbyBasicInfo ToProto() + { + return new LobbyBasicInfo + { + PlayerLobbyInfo = new LobbyPlayerInfo + { + CharacterType = CharacterType, + Status = CharacterStatus + }, + StageInfo = new LobbyGameInfo + { + LobbyMarbleInfo = new LobbyMarbleInfo + { + LobbySealList = { EquippedSealList.Select(x => (uint)x) }, + Rank = 1 + } + }, + BasicInfo = Player.Data.ToLobbyProto() + }; + } +} \ No newline at end of file diff --git a/GameServer/Game/MultiPlayer/BaseGamePlayerInstance.cs b/GameServer/Game/MultiPlayer/BaseGamePlayerInstance.cs new file mode 100644 index 00000000..13b12e80 --- /dev/null +++ b/GameServer/Game/MultiPlayer/BaseGamePlayerInstance.cs @@ -0,0 +1,20 @@ +using EggLink.DanhengServer.GameServer.Game.Lobby.Player; +using EggLink.DanhengServer.GameServer.Server; +using EggLink.DanhengServer.Kcp; + +namespace EggLink.DanhengServer.GameServer.Game.MultiPlayer; + +public abstract class BaseGamePlayerInstance(LobbyPlayerInstance lobby) +{ + public LobbyPlayerInstance LobbyPlayer { get; } = lobby; + public bool EnterGame { get; set; } + public Connection? Connection { get; set; } + + public async ValueTask SendPacket(BasePacket packet) + { + if (Connection == null) return; + if (!Connection.IsOnline) return; + + await Connection.SendPacket(packet); + } +} \ No newline at end of file diff --git a/GameServer/Game/MultiPlayer/BaseMultiPlayerGameRoomInstance.cs b/GameServer/Game/MultiPlayer/BaseMultiPlayerGameRoomInstance.cs new file mode 100644 index 00000000..cb2a3b50 --- /dev/null +++ b/GameServer/Game/MultiPlayer/BaseMultiPlayerGameRoomInstance.cs @@ -0,0 +1,26 @@ +using EggLink.DanhengServer.GameServer.Game.Lobby; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Game.MultiPlayer; + +public abstract class BaseMultiPlayerGameRoomInstance(long roomId, LobbyRoomInstance parentLobby) +{ + public FightGameMode GameMode { get; } = parentLobby.GameMode; + public long RoomId { get; } = roomId; + public LobbyRoomInstance ParentLobby { get; } = parentLobby; + public List Players { get; } = []; + + public BaseGamePlayerInstance? GetPlayerById(int uid) + { + return Players.FirstOrDefault(player => player.LobbyPlayer.Player.Uid == uid); + } + + public FightSessionInfo ToSessionInfo() + { + return new FightSessionInfo + { + SessionGameMode = GameMode, + SessionRoomId = (ulong)RoomId + }; + } +} \ No newline at end of file diff --git a/GameServer/Game/MultiPlayer/MarbleGame/MarbleGamePlayerInstance.cs b/GameServer/Game/MultiPlayer/MarbleGame/MarbleGamePlayerInstance.cs new file mode 100644 index 00000000..e48c3f18 --- /dev/null +++ b/GameServer/Game/MultiPlayer/MarbleGame/MarbleGamePlayerInstance.cs @@ -0,0 +1,64 @@ +using EggLink.DanhengServer.Data; +using EggLink.DanhengServer.Enums.Fight; +using EggLink.DanhengServer.GameServer.Game.Lobby.Player; +using EggLink.DanhengServer.GameServer.Game.MultiPlayer.MarbleGame.Seal; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Game.MultiPlayer.MarbleGame; + +public class MarbleGamePlayerInstance : BaseGamePlayerInstance +{ + public Dictionary SealList { get; set; } = []; + public MarbleTeamType TeamType { get; set; } + public MarblePlayerPhaseEnum Phase { get; set; } = MarblePlayerPhaseEnum.NotEnter; + public int CurItemId { get; set; } + public int Score { get; set; } + public HashSet AllowMoveSealList { get; set; } = []; + + public MarbleGamePlayerInstance(LobbyPlayerInstance lobby, MarbleTeamType type) : base(lobby) + { + TeamType = type; + CurItemId = TeamType == MarbleTeamType.TeamA ? 200 : 300; + + var posXBaseValue = TeamType == MarbleTeamType.TeamA ? -1 : 1; + var index = 0; + foreach (var seal in lobby.EquippedSealList) + { + if (!GameData.MarbleSealData.TryGetValue(seal, out var marbleSeal)) continue; + + var posY = (index - 1) * 1.5f; + var posX = posXBaseValue * (Math.Abs(index - 1) * 0.5f + 2); + var rotX = posXBaseValue * -1f; + AllowMoveSealList.Add(CurItemId); + + SealList.Add(CurItemId, new MarbleGameSealInstance(CurItemId++, seal) + { + Position = new MarbleSealVector + { + X = posX, + Y = posY + }, + Rotation = new MarbleSealVector + { + X = rotX + }, + Attack = marbleSeal.Attack, + CurHp = marbleSeal.Hp, + MaxHp = marbleSeal.Hp, + Size = marbleSeal.Size, + Mass = marbleSeal.Mass, + MaxSpeed = marbleSeal.MaxSpeed + }); + + index++; + } + } + + public void ChangeRound() + { + foreach (var instance in SealList.Values) + { + AllowMoveSealList.Add(instance.Id); + } + } +} \ No newline at end of file diff --git a/GameServer/Game/MultiPlayer/MarbleGame/MarbleGameRoomInstance.cs b/GameServer/Game/MultiPlayer/MarbleGame/MarbleGameRoomInstance.cs new file mode 100644 index 00000000..fb4ac3e7 --- /dev/null +++ b/GameServer/Game/MultiPlayer/MarbleGame/MarbleGameRoomInstance.cs @@ -0,0 +1,135 @@ +using EggLink.DanhengServer.Enums.Fight; +using EggLink.DanhengServer.GameServer.Game.Lobby; +using EggLink.DanhengServer.GameServer.Game.MultiPlayer.MarbleGame.Seal; +using EggLink.DanhengServer.GameServer.Game.MultiPlayer.MarbleGame.Sync; +using EggLink.DanhengServer.GameServer.Server.Packet.Send.Fight; +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Game.MultiPlayer.MarbleGame; + +public class MarbleGameRoomInstance : BaseMultiPlayerGameRoomInstance +{ + public MarbleTeamType CurMoveTeamType { get; set; } + public int CurRound { get; set; } + + public MarbleGameRoomInstance(long roomId, LobbyRoomInstance parentLobby) : base(roomId, parentLobby) + { + // random move team type + CurMoveTeamType = (MarbleTeamType)Random.Shared.Next(1, 3); + // set player + foreach (var player in parentLobby.Players) + { + Players.Add(new MarbleGamePlayerInstance(player, (MarbleTeamType)(parentLobby.Players.IndexOf( + player) + 1))); + } + } + + public async ValueTask BroadCastToRoom(BasePacket packet) + { + foreach (var player in Players) + { + await player.SendPacket(packet); + } + } + + public async ValueTask EnterGame(int uid) + { + var player = GetPlayerById(uid); + if (player == null) return; + player.EnterGame = true; + + if (player is MarbleGamePlayerInstance marblePlayer) + { + marblePlayer.Phase = MarblePlayerPhaseEnum.EnterGame; + } + + if (Players.All(x => x.EnterGame)) + { + // send basic info + await BroadCastToRoom(new PacketFightGeneralScNotify(MarbleNetWorkMsgEnum.SyncBatch, + MarbleNetWorkMsgEnum.GameStart, this)); + } + } + + #region Handler + + public async ValueTask HandleGeneralRequest(MarbleGamePlayerInstance player, uint msgType, byte[] reqData) + { + var messageType = (MarbleNetWorkMsgEnum)msgType; + + switch (messageType) + { + case MarbleNetWorkMsgEnum.LoadFinish: + await LoadFinish(player); + break; + case MarbleNetWorkMsgEnum.PerformanceFinish: + await PerformanceFinish(player); + break; + default: + break; + } + } + + public async ValueTask LoadFinish(MarbleGamePlayerInstance player) + { + player.Phase = MarblePlayerPhaseEnum.LoadFinish; + if (Players.OfType().ToList().All(x => x.Phase == MarblePlayerPhaseEnum.LoadFinish)) + { + // next phase (performance) + await BroadCastToRoom(new PacketFightGeneralScNotify(MarbleNetWorkMsgEnum.SyncBatch, + [new MarblePerformanceSyncData(MarbleNetWorkMsgEnum.SyncNotify)])); + } + } + + public async ValueTask PerformanceFinish(MarbleGamePlayerInstance player) + { + player.Phase = MarblePlayerPhaseEnum.PerformanceFinish; + if (Players.OfType().ToList().All(x => x.Phase == MarblePlayerPhaseEnum.PerformanceFinish)) + { + // next phase (round start) + await RoundStart(); + } + } + + #endregion + + #region Round + + public async ValueTask RoundStart() + { + CurRound++; + foreach (var player in Players.OfType()) + { + player.ChangeRound(); + } + + await BroadCastToRoom(new PacketFightGeneralScNotify(MarbleNetWorkMsgEnum.SyncBatch, + [ + new MarbleGameInfoSyncData(MarbleNetWorkMsgEnum.SyncNotify, MarbleSyncType.RoundStart, this, + Players.OfType().SelectMany(x => x.SealList.Values) + .Select(x => new MarbleGameSealSyncData(x, MarbleFrameType.RoundStart)).ToList()) + ])); + } + + #endregion + + public MarbleGameInfo ToProto() + { + return new MarbleGameInfo + { + LobbyBasicInfo = { ParentLobby.Players.Select(x => x.ToProto()) }, + CurActionTeamType = CurMoveTeamType, + LevelId = (uint)Random.Shared.Next(100, 103), + TeamAPlayer = (uint)Players[0].LobbyPlayer.Player.Uid, + TeamBPlayer = (uint)Players[1].LobbyPlayer.Player.Uid, + TeamARank = 1, + TeamBRank = 1, + TeamASealList = { (Players[0] as MarbleGamePlayerInstance)!.SealList.Select(x => (uint)x.Value.SealId) }, + TeamBSealList = { (Players[1] as MarbleGamePlayerInstance)!.SealList.Select(x => (uint)x.Value.SealId) }, + PlayerAScore = (uint)(Players[0] as MarbleGamePlayerInstance)!.Score, + PlayerBScore = (uint)(Players[1] as MarbleGamePlayerInstance)!.Score, + ControlByServer = true + }; + } +} \ No newline at end of file diff --git a/GameServer/Game/MultiPlayer/MarbleGame/Seal/MarbleGameSealInstance.cs b/GameServer/Game/MultiPlayer/MarbleGame/Seal/MarbleGameSealInstance.cs new file mode 100644 index 00000000..ca0d9857 --- /dev/null +++ b/GameServer/Game/MultiPlayer/MarbleGame/Seal/MarbleGameSealInstance.cs @@ -0,0 +1,34 @@ +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Game.MultiPlayer.MarbleGame.Seal; + +public class MarbleGameSealInstance(int itemId, int sealId) +{ + public int Id { get; set; } = itemId; + public int SealId { get; set; } = sealId; + public int CurHp { get; set; } + public int MaxHp { get; set; } + public int Attack { get; set; } + public float Mass { get; set; } + public float MaxSpeed { get; set; } + public float Size { get; set; } + public bool OnStage { get; set; } + public MarbleSealVector Position { get; set; } = new(); + public MarbleSealVector Rotation { get; set; } = new(); + + public MarbleGameSealInstance Clone() + { + return new MarbleGameSealInstance(Id, SealId) + { + CurHp = CurHp, + MaxHp = MaxHp, + Attack = Attack, + Size = Size, + Mass = Mass, + MaxSpeed = MaxSpeed, + OnStage = OnStage, + Position = Position.Clone(), + Rotation = Rotation.Clone() + }; + } +} \ No newline at end of file diff --git a/GameServer/Game/MultiPlayer/MarbleGame/Seal/MarbleGameSealSyncData.cs b/GameServer/Game/MultiPlayer/MarbleGame/Seal/MarbleGameSealSyncData.cs new file mode 100644 index 00000000..18ca0118 --- /dev/null +++ b/GameServer/Game/MultiPlayer/MarbleGame/Seal/MarbleGameSealSyncData.cs @@ -0,0 +1,24 @@ +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Game.MultiPlayer.MarbleGame.Seal; + +public class MarbleGameSealSyncData(MarbleGameSealInstance inst, MarbleFrameType frameType) +{ + public MarbleGameSealInstance Instance { get; set; } = inst.Clone(); + + public MarbleGameSyncData ToProto() + { + return new MarbleGameSyncData + { + Attack = Instance.Attack, + Id = (uint)Instance.Id, + Hp = Instance.CurHp, + MaxHp = Instance.MaxHp, + SealPosition = Instance.Position, + SealRotation = Instance.Rotation, + SealOnStage = Instance.OnStage, + SealSize = Instance.Size, + FrameType = frameType + }; + } +} \ No newline at end of file diff --git a/GameServer/Game/MultiPlayer/MarbleGame/Sync/MarbleGameBaseSyncData.cs b/GameServer/Game/MultiPlayer/MarbleGame/Sync/MarbleGameBaseSyncData.cs new file mode 100644 index 00000000..ea36632e --- /dev/null +++ b/GameServer/Game/MultiPlayer/MarbleGame/Sync/MarbleGameBaseSyncData.cs @@ -0,0 +1,10 @@ +using EggLink.DanhengServer.Enums.Fight; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Game.MultiPlayer.MarbleGame.Sync; + +public abstract class MarbleGameBaseSyncData(MarbleNetWorkMsgEnum type) +{ + public MarbleNetWorkMsgEnum MessageType { get; set; } = type; + public abstract MarbleGameSyncInfo ToProto(); +} \ No newline at end of file diff --git a/GameServer/Game/MultiPlayer/MarbleGame/Sync/MarbleGameInfoSyncData.cs b/GameServer/Game/MultiPlayer/MarbleGame/Sync/MarbleGameInfoSyncData.cs new file mode 100644 index 00000000..9f0bebb6 --- /dev/null +++ b/GameServer/Game/MultiPlayer/MarbleGame/Sync/MarbleGameInfoSyncData.cs @@ -0,0 +1,18 @@ +using EggLink.DanhengServer.Enums.Fight; +using EggLink.DanhengServer.GameServer.Game.MultiPlayer.MarbleGame.Seal; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Game.MultiPlayer.MarbleGame.Sync; + +public class MarbleGameInfoSyncData(MarbleNetWorkMsgEnum type, MarbleSyncType syncType, MarbleGameRoomInstance room, List syncDatas) : MarbleGameBaseSyncData(type) +{public override MarbleGameSyncInfo ToProto() + { + return new MarbleGameSyncInfo + { + MarbleSyncType = syncType, + CurRound = (uint)room.CurRound, + AllowedMoveSealList = { (room.Players[(int)room.CurMoveTeamType - 1] as MarbleGamePlayerInstance)!.AllowMoveSealList.Select(x => (uint)x) }, + MarbleGameSyncData = { syncDatas.Select(x => x.ToProto()) } + }; + } +} \ No newline at end of file diff --git a/GameServer/Game/MultiPlayer/MarbleGame/Sync/MarblePerformanceSyncData.cs b/GameServer/Game/MultiPlayer/MarbleGame/Sync/MarblePerformanceSyncData.cs new file mode 100644 index 00000000..1cd2d602 --- /dev/null +++ b/GameServer/Game/MultiPlayer/MarbleGame/Sync/MarblePerformanceSyncData.cs @@ -0,0 +1,15 @@ +using EggLink.DanhengServer.Enums.Fight; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Game.MultiPlayer.MarbleGame.Sync; + +public class MarblePerformanceSyncData(MarbleNetWorkMsgEnum type) : MarbleGameBaseSyncData(type) +{ + public override MarbleGameSyncInfo ToProto() + { + return new MarbleGameSyncInfo + { + MarbleSyncType = MarbleSyncType.Performance + }; + } +} \ No newline at end of file diff --git a/GameServer/Game/MultiPlayer/MultiPlayerGameServerManager.cs b/GameServer/Game/MultiPlayer/MultiPlayerGameServerManager.cs new file mode 100644 index 00000000..3cd56584 --- /dev/null +++ b/GameServer/Game/MultiPlayer/MultiPlayerGameServerManager.cs @@ -0,0 +1,39 @@ +using EggLink.DanhengServer.GameServer.Game.Lobby; +using EggLink.DanhengServer.GameServer.Game.MultiPlayer.MarbleGame; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Game.MultiPlayer; + +public class MultiPlayerGameServerManager +{ + public Dictionary Rooms { get; } = []; + public long CurRoomId { get; set; } + + public BaseMultiPlayerGameRoomInstance? CreateRoom(LobbyRoomInstance lobbyRoom) + { + if (lobbyRoom.GameMode != FightGameMode.Marble) return null; + var roomId = ++CurRoomId; + var room = new MarbleGameRoomInstance(roomId, lobbyRoom); + Rooms.Add(roomId, room); + + return room; + } + + public void RemoveRoom(long roomId) + { + Rooms.Remove(roomId, out _); + } + + public BaseMultiPlayerGameRoomInstance? GetPlayerJoinedRoom(int uid) + { + foreach (var room in Rooms.Values) + { + if (room.Players.Any(x => x.LobbyPlayer.Player.Uid == uid)) + { + return room; + } + } + + return null; + } +} \ No newline at end of file diff --git a/GameServer/Server/Connection.cs b/GameServer/Server/Connection.cs index 6140c41c..303b56c1 100644 --- a/GameServer/Server/Connection.cs +++ b/GameServer/Server/Connection.cs @@ -1,6 +1,7 @@ using System.Buffers; using System.Net; using System.Reflection; +using EggLink.DanhengServer.GameServer.Game.MultiPlayer.MarbleGame; using EggLink.DanhengServer.GameServer.Game.Player; using EggLink.DanhengServer.GameServer.Server.Packet; using EggLink.DanhengServer.Kcp; @@ -17,6 +18,8 @@ public class Connection(KcpConversation conversation, IPEndPoint remote) : Danhe private static readonly Logger Logger = new("GameServer"); public PlayerInstance? Player { get; set; } + public MarbleGameRoomInstance? MarbleRoom { get; set; } + public MarbleGamePlayerInstance? MarblePlayer { get; set; } public override async void Start() { diff --git a/GameServer/Server/Packet/Recv/Chat/HandlerSendMsgCsReq.cs b/GameServer/Server/Packet/Recv/Chat/HandlerSendMsgCsReq.cs index 2b412e1d..3334db93 100644 --- a/GameServer/Server/Packet/Recv/Chat/HandlerSendMsgCsReq.cs +++ b/GameServer/Server/Packet/Recv/Chat/HandlerSendMsgCsReq.cs @@ -18,5 +18,7 @@ public class HandlerSendMsgCsReq : Handler else if (req.MessageType == MsgType.Emoji) await connection.Player!.FriendManager!.SendMessage(connection.Player!.Uid, (int)req.TargetList[0], null, (int)req.ExtraId); + else if (req.MessageType == MsgType.Invite) + await connection.Player!.FriendManager!.SendInviteMessage(connection.Player!.Uid, (int)req.TargetList[0], req.InviteInfo); } } \ No newline at end of file diff --git a/GameServer/Server/Packet/Recv/Fight/HandlerFightEnterCsReq.cs b/GameServer/Server/Packet/Recv/Fight/HandlerFightEnterCsReq.cs new file mode 100644 index 00000000..1ab673fa --- /dev/null +++ b/GameServer/Server/Packet/Recv/Fight/HandlerFightEnterCsReq.cs @@ -0,0 +1,63 @@ +using EggLink.DanhengServer.GameServer.Game.MultiPlayer; +using EggLink.DanhengServer.GameServer.Server.Packet.Send.Fight; +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; +using EggLink.DanhengServer.Util.Security; +using EggLink.DanhengServer.Util; +using System.Security.Cryptography; +using EggLink.DanhengServer.GameServer.Game.MultiPlayer.MarbleGame; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Fight; + +[Opcode(CmdIds.FightEnterCsReq)] +public class HandlerFightEnterCsReq : Handler +{ + public override async Task OnHandle(Connection connection, byte[] header, byte[] data) + { + var req = FightEnterCsReq.Parser.ParseFrom(data); + if (!ServerUtils.MultiPlayerGameServerManager.Rooms.TryGetValue((long)req.EnterRoomId, out var room)) + { + await connection.SendPacket(new PacketFightEnterScRsp(Retcode.RetFightRoomNotExist)); + connection.Stop(); + return; + } + + if (room is not MarbleGameRoomInstance marbleGame) + { + await connection.SendPacket(new PacketFightEnterScRsp(Retcode.RetFightRoomNotExist)); + connection.Stop(); + return; + } + + var player = room.GetPlayerById((int)req.Uid); + if (player is not MarbleGamePlayerInstance marble) + { + await connection.SendPacket(new PacketFightEnterScRsp(Retcode.RetFightRoomNotExist)); + connection.Stop(); + return; + } + + connection.MarblePlayer = marble; + marble.Connection = connection; + connection.MarbleRoom = marbleGame; + + if (ConfigManager.Config.GameServer.UsePacketEncryption) + { + var tempRandom = new MT19937((ulong)DateTimeOffset.Now.ToUnixTimeSeconds()); + connection.ClientSecretKeySeed = tempRandom.NextUInt64(); + } + + connection.State = SessionStateEnum.ACTIVE; + connection.DebugFile = Path.Combine(ConfigManager.Config.Path.LogPath, "Debug/", $"{req.Uid}/", + $"Debug-{DateTime.Now:yyyy-MM-dd HH-mm-ss}-FightGame.log"); + + await connection.SendPacket(new PacketFightEnterScRsp(connection.ClientSecretKeySeed)); + + if (ConfigManager.Config.GameServer.UsePacketEncryption) + { + connection.XorKey = Crypto.GenerateXorKey(connection.ClientSecretKeySeed); + } + + await marbleGame.EnterGame(player.LobbyPlayer.Player.Uid); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Recv/Fight/HandlerFightGeneralCsReq.cs b/GameServer/Server/Packet/Recv/Fight/HandlerFightGeneralCsReq.cs new file mode 100644 index 00000000..faa49e24 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Fight/HandlerFightGeneralCsReq.cs @@ -0,0 +1,25 @@ +using EggLink.DanhengServer.GameServer.Server.Packet.Send.Fight; +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Fight; + +[Opcode(CmdIds.FightGeneralCsReq)] +public class HandlerFightGeneralCsReq : Handler +{ + public override async Task OnHandle(Connection connection, byte[] header, byte[] data) + { + var req = FightGeneralCsReq.Parser.ParseFrom(data); + + if (connection.MarbleRoom == null || connection.MarblePlayer == null) + { + await connection.SendPacket(new PacketFightGeneralScRsp(Retcode.RetFightRoomNotExist)); + return; + } + + await connection.MarbleRoom.HandleGeneralRequest(connection.MarblePlayer, req.NetworkMsgType, + req.FightGeneralInfo?.ToByteArray() ?? []); + + await connection.SendPacket(new PacketFightGeneralScRsp(req.NetworkMsgType)); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Recv/Fight/HandlerFightHeartBeatCsReq.cs b/GameServer/Server/Packet/Recv/Fight/HandlerFightHeartBeatCsReq.cs new file mode 100644 index 00000000..3db0b294 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Fight/HandlerFightHeartBeatCsReq.cs @@ -0,0 +1,16 @@ +using EggLink.DanhengServer.GameServer.Server.Packet.Send.Fight; +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Fight; + +[Opcode(CmdIds.FightHeartBeatCsReq)] +public class HandlerFightHeartBeatCsReq : Handler +{ + public override async Task OnHandle(Connection connection, byte[] header, byte[] data) + { + var req = FightHeartBeatCsReq.Parser.ParseFrom(data); + + await connection.SendPacket(new PacketFightHeartBeatScRsp(req.ClientTimeMs)); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Recv/Lobby/HandlerLobbyCreateCsReq.cs b/GameServer/Server/Packet/Recv/Lobby/HandlerLobbyCreateCsReq.cs new file mode 100644 index 00000000..cf7639d2 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Lobby/HandlerLobbyCreateCsReq.cs @@ -0,0 +1,27 @@ +using EggLink.DanhengServer.GameServer.Game.Lobby; +using EggLink.DanhengServer.GameServer.Server.Packet.Send.Lobby; +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Lobby; + +[Opcode(CmdIds.LobbyCreateCsReq)] +public class HandlerLobbyCreateCsReq : Handler +{ + public override async Task OnHandle(Connection connection, byte[] header, byte[] data) + { + var req = LobbyCreateCsReq.Parser.ParseFrom(data); + + if (req.FightGameMode != FightGameMode.Marble) + { + await connection.SendPacket(new PacketLobbyCreateScRsp(Retcode.RetTimeout)); + return; + } + + var lobbyMode = req.LobbyMode; + var marbleList = req.LobbyGameInfo.LobbyMarbleInfo.LobbySealList.Select(x => (int)x).ToList(); + + var room = await ServerUtils.LobbyServerManager.CreateLobbyRoom(connection.Player!, (int)lobbyMode, marbleList); + await connection.SendPacket(new PacketLobbyCreateScRsp(room)); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Recv/Lobby/HandlerLobbyJoinCsReq.cs b/GameServer/Server/Packet/Recv/Lobby/HandlerLobbyJoinCsReq.cs new file mode 100644 index 00000000..48d483d1 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Lobby/HandlerLobbyJoinCsReq.cs @@ -0,0 +1,40 @@ +using EggLink.DanhengServer.GameServer.Server.Packet.Send.Lobby; +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Lobby; + +[Opcode(CmdIds.LobbyJoinCsReq)] +public class HandlerLobbyJoinCsReq : Handler +{ + public override async Task OnHandle(Connection connection, byte[] header, byte[] data) + { + var req = LobbyJoinCsReq.Parser.ParseFrom(data); + + var room = ServerUtils.LobbyServerManager.LobbyRoomInstances.GetValueOrDefault((long)req.RoomId); + + if (room == null) + { + await connection.SendPacket(new PacketLobbyJoinScRsp(Retcode.RetLobbyRoomNotExist)); + return; + } + + if (room.Players.Count >= 2) + { + await connection.SendPacket(new PacketLobbyJoinScRsp(Retcode.RetLobbyRoomPalyerFull)); + return; + } + + var player = room.GetPlayerByUid(connection.Player!.Uid); + if (player != null) + { + await connection.SendPacket(new PacketLobbyJoinScRsp(Retcode.RetLobbyRoomPalyerFull)); + return; + } + + await room.AddPlayer(connection.Player!, req.LobbyGameInfo.LobbyMarbleInfo.LobbySealList.Select(x => (int)x).ToList(), + LobbyCharacterType.LobbyCharacterMember); + + await connection.SendPacket(new PacketLobbyJoinScRsp(room)); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Recv/Lobby/HandlerLobbyModifyPlayerInfoCsReq.cs b/GameServer/Server/Packet/Recv/Lobby/HandlerLobbyModifyPlayerInfoCsReq.cs new file mode 100644 index 00000000..998fd454 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Lobby/HandlerLobbyModifyPlayerInfoCsReq.cs @@ -0,0 +1,39 @@ +using EggLink.DanhengServer.GameServer.Server.Packet.Send.Lobby; +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Lobby; + +[Opcode(CmdIds.LobbyModifyPlayerInfoCsReq)] +public class HandlerLobbyModifyPlayerInfoCsReq : Handler +{ + public override async Task OnHandle(Connection connection, byte[] header, byte[] data) + { + var req = LobbyModifyPlayerInfoCsReq.Parser.ParseFrom(data); + + var room = ServerUtils.LobbyServerManager.GetPlayerJoinedRoom(connection.Player!.Uid); + if (room == null) + { + await connection.SendPacket(new PacketLobbyModifyPlayerInfoScRsp(Retcode.RetLobbyRoomNotExist)); + return; + } + + var player = room.GetPlayerByUid(connection.Player.Uid); + if (player == null) + { + await connection.SendPacket(new PacketLobbyModifyPlayerInfoScRsp(Retcode.RetLobbyRoomNotExist)); + return; + } + + player.EquippedSealList = req.LobbyGameInfo.LobbyMarbleInfo.LobbySealList.Select(x => (int)x).ToList(); + player.CharacterStatus = req.Type switch + { + LobbyModifyType.Ready => LobbyCharacterStatus.Ready, + LobbyModifyType.Operating => LobbyCharacterStatus.Operating, + _ => player.CharacterStatus + }; + + await room.BroadCastToRoom(new PacketLobbySyncInfoScNotify(player.Player.Uid, room, req.Type)); + await connection.SendPacket(new PacketLobbyModifyPlayerInfoScRsp(Retcode.RetSucc)); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Recv/Lobby/HandlerLobbyQuitCsReq.cs b/GameServer/Server/Packet/Recv/Lobby/HandlerLobbyQuitCsReq.cs new file mode 100644 index 00000000..4ed17257 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Lobby/HandlerLobbyQuitCsReq.cs @@ -0,0 +1,22 @@ +using EggLink.DanhengServer.GameServer.Server.Packet.Send.Lobby; +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Lobby; + +[Opcode(CmdIds.LobbyQuitCsReq)] +public class HandlerLobbyQuitCsReq : Handler +{ + public override async Task OnHandle(Connection connection, byte[] header, byte[] data) + { + var room = ServerUtils.LobbyServerManager.GetPlayerJoinedRoom(connection.Player!.Uid); + if (room == null) + { + await connection.SendPacket(new PacketLobbyQuitScRsp(Retcode.RetLobbyRoomNotExist)); + return; + } + + await room.RemovePlayer(connection.Player.Uid); + await connection.SendPacket(new PacketLobbyQuitScRsp(Retcode.RetSucc)); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Recv/Lobby/HandlerLobbyStartFightCsReq.cs b/GameServer/Server/Packet/Recv/Lobby/HandlerLobbyStartFightCsReq.cs new file mode 100644 index 00000000..535a357a --- /dev/null +++ b/GameServer/Server/Packet/Recv/Lobby/HandlerLobbyStartFightCsReq.cs @@ -0,0 +1,24 @@ +using EggLink.DanhengServer.GameServer.Server.Packet.Send.Lobby; +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Lobby; + +[Opcode(CmdIds.LobbyStartFightCsReq)] +public class HandlerLobbyStartFightCsReq : Handler +{ + public override async Task OnHandle(Connection connection, byte[] header, byte[] data) + { + var room = ServerUtils.LobbyServerManager.GetPlayerJoinedRoom(connection.Player!.Uid); + if (room == null) + { + await connection.SendPacket(new PacketLobbyStartFightScRsp(Retcode.RetLobbyRoomNotExist)); + return; + } + + var code = await room.LobbyStartFight(); + await connection.SendPacket(new PacketLobbyStartFightScRsp(code)); + + await room.StartFight(); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Recv/Marble/HandlerMarbleGetDataCsReq.cs b/GameServer/Server/Packet/Recv/Marble/HandlerMarbleGetDataCsReq.cs new file mode 100644 index 00000000..a805f2ea --- /dev/null +++ b/GameServer/Server/Packet/Recv/Marble/HandlerMarbleGetDataCsReq.cs @@ -0,0 +1,13 @@ +using EggLink.DanhengServer.GameServer.Server.Packet.Send.Marble; +using EggLink.DanhengServer.Kcp; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Marble; + +[Opcode(CmdIds.MarbleGetDataCsReq)] +public class HandlerMarbleGetDataCsReq : Handler +{ + public override async Task OnHandle(Connection connection, byte[] header, byte[] data) + { + await connection.SendPacket(new PacketMarbleGetDataScRsp()); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Recv/Marble/HandlerMarbleLevelFinishCsReq.cs b/GameServer/Server/Packet/Recv/Marble/HandlerMarbleLevelFinishCsReq.cs new file mode 100644 index 00000000..ec225774 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Marble/HandlerMarbleLevelFinishCsReq.cs @@ -0,0 +1,16 @@ +using EggLink.DanhengServer.GameServer.Server.Packet.Send.Marble; +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Marble; + +[Opcode(CmdIds.MarbleLevelFinishCsReq)] +public class HandlerMarbleLevelFinishCsReq : Handler +{ + public override async Task OnHandle(Connection connection, byte[] header, byte[] data) + { + var req = MarbleLevelFinishCsReq.Parser.ParseFrom(data); + + await connection.SendPacket(new PacketMarbleLevelFinishScRsp(req.MarbleLevelId)); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Recv/Marble/HandlerMarbleUpdateShownSealCsReq.cs b/GameServer/Server/Packet/Recv/Marble/HandlerMarbleUpdateShownSealCsReq.cs new file mode 100644 index 00000000..83886b0e --- /dev/null +++ b/GameServer/Server/Packet/Recv/Marble/HandlerMarbleUpdateShownSealCsReq.cs @@ -0,0 +1,16 @@ +using EggLink.DanhengServer.GameServer.Server.Packet.Send.Marble; +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Marble; + +[Opcode(CmdIds.MarbleUpdateShownSealCsReq)] +public class HandlerMarbleUpdateShownSealCsReq : Handler +{ + public override async Task OnHandle(Connection connection, byte[] header, byte[] data) + { + var req = MarbleUpdateShownSealCsReq.Parser.ParseFrom(data); + + await connection.SendPacket(new PacketMarbleUpdateShownSealScRsp(req.UpdateSealList)); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Recv/Match/HandlerGetCrossInfoCsReq.cs b/GameServer/Server/Packet/Recv/Match/HandlerGetCrossInfoCsReq.cs new file mode 100644 index 00000000..9e1e798f --- /dev/null +++ b/GameServer/Server/Packet/Recv/Match/HandlerGetCrossInfoCsReq.cs @@ -0,0 +1,13 @@ +using EggLink.DanhengServer.GameServer.Server.Packet.Send.Match; +using EggLink.DanhengServer.Kcp; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Match; + +[Opcode(CmdIds.GetCrossInfoCsReq)] +public class HandlerGetCrossInfoCsReq : Handler +{ + public override async Task OnHandle(Connection connection, byte[] header, byte[] data) + { + await connection.SendPacket(new PacketGetCrossInfoScRsp()); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Recv/Multiplayer/HandlerMultiplayerGetFightGateCsReq.cs b/GameServer/Server/Packet/Recv/Multiplayer/HandlerMultiplayerGetFightGateCsReq.cs new file mode 100644 index 00000000..0436cd8f --- /dev/null +++ b/GameServer/Server/Packet/Recv/Multiplayer/HandlerMultiplayerGetFightGateCsReq.cs @@ -0,0 +1,21 @@ +using EggLink.DanhengServer.GameServer.Server.Packet.Send.Multiplayer; +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Multiplayer; + +[Opcode(CmdIds.MultiplayerGetFightGateCsReq)] +public class HandlerMultiplayerGetFightGateCsReq : Handler +{ + public override async Task OnHandle(Connection connection, byte[] header, byte[] data) + { + var room = ServerUtils.MultiPlayerGameServerManager.GetPlayerJoinedRoom(connection.Player!.Uid); + if (room == null) + { + await connection.SendPacket(new PacketMultiplayerGetFightGateScRsp(Retcode.RetFightRoomNotExist)); + return; + } + + await connection.SendPacket(new PacketMultiplayerGetFightGateScRsp(room)); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Recv/Player/HandlerPlayerLogoutCsReq.cs b/GameServer/Server/Packet/Recv/Player/HandlerPlayerLogoutCsReq.cs index 1ffa6812..5c9899ca 100644 --- a/GameServer/Server/Packet/Recv/Player/HandlerPlayerLogoutCsReq.cs +++ b/GameServer/Server/Packet/Recv/Player/HandlerPlayerLogoutCsReq.cs @@ -7,6 +7,12 @@ public class HandlerPlayerLogoutCsReq : Handler { public override async Task OnHandle(Connection connection, byte[] header, byte[] data) { + var room = ServerUtils.LobbyServerManager.GetPlayerJoinedRoom(connection.Player!.Uid); + if (room != null) + { + await room.RemovePlayer(connection.Player.Uid); + } + await connection.SendPacket(CmdIds.PlayerLogoutScRsp); connection.Stop(); } diff --git a/GameServer/Server/Packet/Send/Chat/PacketRevcMsgScNotify.cs b/GameServer/Server/Packet/Send/Chat/PacketRevcMsgScNotify.cs index 9cdd370b..f66c7004 100644 --- a/GameServer/Server/Packet/Send/Chat/PacketRevcMsgScNotify.cs +++ b/GameServer/Server/Packet/Send/Chat/PacketRevcMsgScNotify.cs @@ -32,4 +32,18 @@ public class PacketRevcMsgScNotify : BasePacket SetData(proto); } + + public PacketRevcMsgScNotify(uint toUid, uint fromUid, LobbyInviteInfo info) : base(CmdIds.RevcMsgScNotify) + { + var proto = new RevcMsgScNotify + { + ChatType = ChatType.Private, + SourceUid = fromUid, + TargetUid = toUid, + InviteInfo = info, + MessageType = MsgType.Invite + }; + + SetData(proto); + } } \ No newline at end of file diff --git a/GameServer/Server/Packet/Send/Fight/PacketFightEnterScRsp.cs b/GameServer/Server/Packet/Send/Fight/PacketFightEnterScRsp.cs new file mode 100644 index 00000000..13777ce4 --- /dev/null +++ b/GameServer/Server/Packet/Send/Fight/PacketFightEnterScRsp.cs @@ -0,0 +1,29 @@ +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; +using EggLink.DanhengServer.Util; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.Fight; + +public class PacketFightEnterScRsp : BasePacket +{ + public PacketFightEnterScRsp(Retcode code) : base(CmdIds.FightEnterScRsp) + { + var proto = new FightEnterScRsp + { + Retcode = (uint)code + }; + + SetData(proto); + } + + public PacketFightEnterScRsp(ulong keySeed) : base(CmdIds.FightEnterScRsp) + { + var proto = new FightEnterScRsp + { + SecretKeySeed = keySeed, + ServerTimestampMs = (ulong)Extensions.GetUnixMs() + }; + + SetData(proto); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Send/Fight/PacketFightGeneralScNotify.cs b/GameServer/Server/Packet/Send/Fight/PacketFightGeneralScNotify.cs new file mode 100644 index 00000000..c40a6b57 --- /dev/null +++ b/GameServer/Server/Packet/Send/Fight/PacketFightGeneralScNotify.cs @@ -0,0 +1,46 @@ +using EggLink.DanhengServer.Enums.Fight; +using EggLink.DanhengServer.GameServer.Game.MultiPlayer.MarbleGame; +using EggLink.DanhengServer.GameServer.Game.MultiPlayer.MarbleGame.Sync; +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.Fight; + +public class PacketFightGeneralScNotify : BasePacket +{ + public PacketFightGeneralScNotify(MarbleNetWorkMsgEnum msgType, MarbleNetWorkMsgEnum syncType, MarbleGameRoomInstance game) : base(CmdIds.FightGeneralScNotify) + { + var proto = new FightGeneralScNotify + { + NetworkMsgType = (uint)msgType, + FightGeneralInfo = new FightGeneralServerInfo + { + FightGameInfo = { new FightGameInfo + { + MarbleGameInfo = game.ToProto(), + GameMessageType = (uint)syncType + } } + } + }; + + SetData(proto); + } + + public PacketFightGeneralScNotify(MarbleNetWorkMsgEnum msgType, List sync) : base(CmdIds.FightGeneralScNotify) + { + var proto = new FightGeneralScNotify + { + NetworkMsgType = (uint)msgType, + FightGeneralInfo = new FightGeneralServerInfo + { + FightGameInfo = { sync.Select(x => new FightGameInfo + { + GameMessageType = (uint)x.MessageType, + MarbleGameSyncInfo = x.ToProto() + }) } + } + }; + + SetData(proto); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Send/Fight/PacketFightGeneralScRsp.cs b/GameServer/Server/Packet/Send/Fight/PacketFightGeneralScRsp.cs new file mode 100644 index 00000000..1152136e --- /dev/null +++ b/GameServer/Server/Packet/Send/Fight/PacketFightGeneralScRsp.cs @@ -0,0 +1,27 @@ +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.Fight; + +public class PacketFightGeneralScRsp : BasePacket +{ + public PacketFightGeneralScRsp(uint networkType) : base(CmdIds.FightGeneralScRsp) + { + var proto = new FightGeneralScRsp + { + NetworkMsgType = networkType + }; + + SetData(proto); + } + + public PacketFightGeneralScRsp(Retcode code) : base(CmdIds.FightGeneralScRsp) + { + var proto = new FightGeneralScRsp + { + Retcode = (uint)code + }; + + SetData(proto); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Send/Fight/PacketFightHeartBeatScRsp.cs b/GameServer/Server/Packet/Send/Fight/PacketFightHeartBeatScRsp.cs new file mode 100644 index 00000000..49cc4d73 --- /dev/null +++ b/GameServer/Server/Packet/Send/Fight/PacketFightHeartBeatScRsp.cs @@ -0,0 +1,19 @@ +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; +using EggLink.DanhengServer.Util; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.Fight; + +public class PacketFightHeartBeatScRsp : BasePacket +{ + public PacketFightHeartBeatScRsp(ulong clientTime) : base(CmdIds.FightHeartBeatScRsp) + { + var proto = new FightHeartBeatScRsp + { + ServerTimeMs = (ulong)Extensions.GetUnixMs(), + ClientTimeMs = clientTime + }; + + SetData(proto); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Send/Lobby/PacketLobbyCreateScRsp.cs b/GameServer/Server/Packet/Send/Lobby/PacketLobbyCreateScRsp.cs new file mode 100644 index 00000000..05cdcabc --- /dev/null +++ b/GameServer/Server/Packet/Send/Lobby/PacketLobbyCreateScRsp.cs @@ -0,0 +1,31 @@ +using EggLink.DanhengServer.GameServer.Game.Lobby; +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.Lobby; + +public class PacketLobbyCreateScRsp : BasePacket +{ + public PacketLobbyCreateScRsp(LobbyRoomInstance room) : base(CmdIds.LobbyCreateScRsp) + { + var proto = new LobbyCreateScRsp + { + RoomId = (ulong)room.RoomId, + FightGameMode = room.GameMode, + LobbyBasicInfo = { room.Players.Select(x => x.ToProto()) }, + LobbyMode = (uint)room.LobbyMode + }; + + SetData(proto); + } + + public PacketLobbyCreateScRsp(Retcode retCode) : base(CmdIds.LobbyCreateScRsp) + { + var proto = new LobbyCreateScRsp + { + Retcode = (uint)retCode + }; + + SetData(proto); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Send/Lobby/PacketLobbyJoinScRsp.cs b/GameServer/Server/Packet/Send/Lobby/PacketLobbyJoinScRsp.cs new file mode 100644 index 00000000..78d33e34 --- /dev/null +++ b/GameServer/Server/Packet/Send/Lobby/PacketLobbyJoinScRsp.cs @@ -0,0 +1,31 @@ +using EggLink.DanhengServer.GameServer.Game.Lobby; +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.Lobby; + +public class PacketLobbyJoinScRsp : BasePacket +{ + public PacketLobbyJoinScRsp(Retcode retcode) : base(CmdIds.LobbyJoinScRsp) + { + var proto = new LobbyJoinScRsp + { + Retcode = (uint)retcode + }; + + SetData(proto); + } + + public PacketLobbyJoinScRsp(LobbyRoomInstance room) : base(CmdIds.LobbyJoinScRsp) + { + var proto = new LobbyJoinScRsp + { + RoomId = (ulong)room.RoomId, + FightGameMode = room.GameMode, + LobbyBasicInfo = { room.Players.Select(x => x.ToProto()) }, + LobbyMode = (uint)room.LobbyMode + }; + + SetData(proto); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Send/Lobby/PacketLobbyModifyPlayerInfoScRsp.cs b/GameServer/Server/Packet/Send/Lobby/PacketLobbyModifyPlayerInfoScRsp.cs new file mode 100644 index 00000000..826fd7f3 --- /dev/null +++ b/GameServer/Server/Packet/Send/Lobby/PacketLobbyModifyPlayerInfoScRsp.cs @@ -0,0 +1,17 @@ +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.Lobby; + +public class PacketLobbyModifyPlayerInfoScRsp : BasePacket +{ + public PacketLobbyModifyPlayerInfoScRsp(Retcode code) : base(CmdIds.LobbyModifyPlayerInfoScRsp) + { + var proto = new LobbyModifyPlayerInfoScRsp + { + Retcode = (uint)code + }; + + SetData(proto); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Send/Lobby/PacketLobbyQuitScRsp.cs b/GameServer/Server/Packet/Send/Lobby/PacketLobbyQuitScRsp.cs new file mode 100644 index 00000000..ea7f4877 --- /dev/null +++ b/GameServer/Server/Packet/Send/Lobby/PacketLobbyQuitScRsp.cs @@ -0,0 +1,17 @@ +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.Lobby; + +public class PacketLobbyQuitScRsp : BasePacket +{ + public PacketLobbyQuitScRsp(Retcode code) : base(CmdIds.LobbyQuitScRsp) + { + var proto = new LobbyModifyPlayerInfoScRsp + { + Retcode = (uint)code + }; + + SetData(proto); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Send/Lobby/PacketLobbyStartFightScRsp.cs b/GameServer/Server/Packet/Send/Lobby/PacketLobbyStartFightScRsp.cs new file mode 100644 index 00000000..6f52367b --- /dev/null +++ b/GameServer/Server/Packet/Send/Lobby/PacketLobbyStartFightScRsp.cs @@ -0,0 +1,17 @@ +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.Lobby; + +public class PacketLobbyStartFightScRsp : BasePacket +{ + public PacketLobbyStartFightScRsp(Retcode code) : base(CmdIds.LobbyStartFightScRsp) + { + var proto = new LobbyStartFightScRsp + { + Retcode = (uint)code + }; + + SetData(proto); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Send/Lobby/PacketLobbySyncInfoScNotify.cs b/GameServer/Server/Packet/Send/Lobby/PacketLobbySyncInfoScNotify.cs new file mode 100644 index 00000000..c265ad28 --- /dev/null +++ b/GameServer/Server/Packet/Send/Lobby/PacketLobbySyncInfoScNotify.cs @@ -0,0 +1,21 @@ +using EggLink.DanhengServer.GameServer.Game.Lobby; +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.Lobby; + +public class PacketLobbySyncInfoScNotify : BasePacket +{ + public PacketLobbySyncInfoScNotify(int uid, LobbyRoomInstance room, LobbyModifyType modifyType) : base( + CmdIds.LobbySyncInfoScNotify) + { + var proto = new LobbySyncInfoScNotify + { + LobbyBasicInfo = { room.Players.Select(x => x.ToProto()) }, + Uid = (uint)uid, + Type = modifyType + }; + + SetData(proto); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Send/Marble/PacketMarbleGetDataScRsp.cs b/GameServer/Server/Packet/Send/Marble/PacketMarbleGetDataScRsp.cs new file mode 100644 index 00000000..18b65291 --- /dev/null +++ b/GameServer/Server/Packet/Send/Marble/PacketMarbleGetDataScRsp.cs @@ -0,0 +1,19 @@ +using EggLink.DanhengServer.Data; +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.Marble; + +public class PacketMarbleGetDataScRsp : BasePacket +{ + public PacketMarbleGetDataScRsp() : base(CmdIds.MarbleGetDataScRsp) + { + var proto = new MarbleGetDataScRsp + { + OwnedSealList = { GameData.MarbleSealData.Keys.Select(x => (uint)x) }, + MarbleFinishLevelIdList = { GameData.MarbleMatchInfoData.Keys.Select(x => (uint)x) } + }; + + SetData(proto); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Send/Marble/PacketMarbleLevelFinishScRsp.cs b/GameServer/Server/Packet/Send/Marble/PacketMarbleLevelFinishScRsp.cs new file mode 100644 index 00000000..16f3d78b --- /dev/null +++ b/GameServer/Server/Packet/Send/Marble/PacketMarbleLevelFinishScRsp.cs @@ -0,0 +1,17 @@ +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.Marble; + +public class PacketMarbleLevelFinishScRsp : BasePacket +{ + public PacketMarbleLevelFinishScRsp(uint levelId) : base(CmdIds.MarbleLevelFinishScRsp) + { + var proto = new MarbleLevelFinishScRsp + { + MarbleLevelId = levelId + }; + + SetData(proto); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Send/Marble/PacketMarbleUpdateShownSealScRsp.cs b/GameServer/Server/Packet/Send/Marble/PacketMarbleUpdateShownSealScRsp.cs new file mode 100644 index 00000000..2ef5a2e8 --- /dev/null +++ b/GameServer/Server/Packet/Send/Marble/PacketMarbleUpdateShownSealScRsp.cs @@ -0,0 +1,17 @@ +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.Marble; + +public class PacketMarbleUpdateShownSealScRsp : BasePacket +{ + public PacketMarbleUpdateShownSealScRsp(ICollection sealList) : base(CmdIds.MarbleUpdateShownSealScRsp) + { + var proto = new MarbleUpdateShownSealScRsp + { + UpdateSealList = { sealList } + }; + + SetData(proto); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Send/Match/PacketGetCrossInfoScRsp.cs b/GameServer/Server/Packet/Send/Match/PacketGetCrossInfoScRsp.cs new file mode 100644 index 00000000..273b6259 --- /dev/null +++ b/GameServer/Server/Packet/Send/Match/PacketGetCrossInfoScRsp.cs @@ -0,0 +1,17 @@ +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.Match; + +public class PacketGetCrossInfoScRsp : BasePacket +{ + public PacketGetCrossInfoScRsp() : base(CmdIds.GetCrossInfoScRsp) + { + var proto = new GetCrossInfoScRsp + { + FightGameMode = FightGameMode.Marble + }; + + SetData(proto); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Send/Multiplayer/PacketMultiplayerFightGameStartScNotify.cs b/GameServer/Server/Packet/Send/Multiplayer/PacketMultiplayerFightGameStartScNotify.cs new file mode 100644 index 00000000..8b5b7212 --- /dev/null +++ b/GameServer/Server/Packet/Send/Multiplayer/PacketMultiplayerFightGameStartScNotify.cs @@ -0,0 +1,19 @@ +using EggLink.DanhengServer.GameServer.Game.MultiPlayer; +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.Multiplayer; + +public class PacketMultiplayerFightGameStartScNotify : BasePacket +{ + public PacketMultiplayerFightGameStartScNotify(BaseMultiPlayerGameRoomInstance room) : base(CmdIds.MultiplayerFightGameStartScNotify) + { + var proto = new MultiplayerFightGameStartScNotify + { + SessionInfo = room.ToSessionInfo(), + LobbyBasicInfo = { room.ParentLobby.Players.Select(x => x.ToProto()) } + }; + + SetData(proto); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Send/Multiplayer/PacketMultiplayerGetFightGateScRsp.cs b/GameServer/Server/Packet/Send/Multiplayer/PacketMultiplayerGetFightGateScRsp.cs new file mode 100644 index 00000000..2e0f76ba --- /dev/null +++ b/GameServer/Server/Packet/Send/Multiplayer/PacketMultiplayerGetFightGateScRsp.cs @@ -0,0 +1,31 @@ +using EggLink.DanhengServer.GameServer.Game.MultiPlayer; +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; +using EggLink.DanhengServer.Util; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.Multiplayer; + +public class PacketMultiplayerGetFightGateScRsp : BasePacket +{ + public PacketMultiplayerGetFightGateScRsp(Retcode code) : base(CmdIds.MultiplayerGetFightGateScRsp) + { + var proto = new MultiplayerGetFightGateScRsp + { + Retcode = (uint)code + }; + + SetData(proto); + } + + public PacketMultiplayerGetFightGateScRsp(BaseMultiPlayerGameRoomInstance room) : base(CmdIds.MultiplayerGetFightGateScRsp) + { + var proto = new MultiplayerGetFightGateScRsp + { + GateRoomId = (ulong)room.RoomId, + Ip = ConfigManager.Config.GameServer.PublicAddress, + Port = ConfigManager.Config.GameServer.Port + }; + + SetData(proto); + } +} \ No newline at end of file diff --git a/GameServer/Server/ServerUtils.cs b/GameServer/Server/ServerUtils.cs new file mode 100644 index 00000000..5292b8de --- /dev/null +++ b/GameServer/Server/ServerUtils.cs @@ -0,0 +1,10 @@ +using EggLink.DanhengServer.GameServer.Game.Lobby; +using EggLink.DanhengServer.GameServer.Game.MultiPlayer; + +namespace EggLink.DanhengServer.GameServer.Server; + +public static class ServerUtils +{ + public static LobbyServerManager LobbyServerManager { get; set; } = new(); + public static MultiPlayerGameServerManager MultiPlayerGameServerManager { get; set; } = new(); +} \ No newline at end of file