diff --git a/AxibugEmuOnline.Server/Manager/ClientManager.cs b/AxibugEmuOnline.Server/Manager/ClientManager.cs index 625a404..95cbbde 100644 --- a/AxibugEmuOnline.Server/Manager/ClientManager.cs +++ b/AxibugEmuOnline.Server/Manager/ClientManager.cs @@ -34,20 +34,17 @@ namespace AxibugEmuOnline.Server.Manager public class UserRoomState { public int RoomID { get; private set; } - public int PlayerIdx { get; private set; } public UserRoomState() { ClearRoomData(); } - public void SetRoomData(int roomID, int playerIdx) + public void SetRoomData(int roomID) { RoomID = roomID; - PlayerIdx = playerIdx; } public void ClearRoomData() { RoomID = -1; - PlayerIdx = -1; } } diff --git a/AxibugEmuOnline.Server/Manager/RoomManager.cs b/AxibugEmuOnline.Server/Manager/RoomManager.cs index 69f9467..66fad6c 100644 --- a/AxibugEmuOnline.Server/Manager/RoomManager.cs +++ b/AxibugEmuOnline.Server/Manager/RoomManager.cs @@ -3,6 +3,8 @@ using AxibugEmuOnline.Server.Manager; using AxibugEmuOnline.Server.NetWork; using AxibugProtobuf; using MySql.Data.MySqlClient; +using Org.BouncyCastle.Crypto.Parameters; +using System.Data; using System.Net.Sockets; using System.Runtime.InteropServices; using System.Security.Policy; @@ -10,1023 +12,1176 @@ using System.Security.Policy; namespace AxibugEmuOnline.Server { - public class RoomManager - { - Dictionary mDictRoom = new Dictionary(); - List mKeyRoomList = new List(); - AutoResetEvent roomTickARE; - Thread threadRoomTick; - - int RoomIDSeed = 1; - public RoomManager() - { - NetMsg.Instance.RegNetMsgEvent((int)CommandID.CmdRoomList, OnCmdRoomList); - NetMsg.Instance.RegNetMsgEvent((int)CommandID.CmdRoomGetScreen, CmdRoomGetScreen); - NetMsg.Instance.RegNetMsgEvent((int)CommandID.CmdRoomCreate, OnCmdRoomCreate); - NetMsg.Instance.RegNetMsgEvent((int)CommandID.CmdRoomJoin, OnCmdRoomJoin); - NetMsg.Instance.RegNetMsgEvent((int)CommandID.CmdRoomLeave, OnCmdRoomLeave); - NetMsg.Instance.RegNetMsgEvent((int)CommandID.CmdRoomHostPlayerUpdateStateRaw, OnHostPlayerUpdateStateRaw); - NetMsg.Instance.RegNetMsgEvent((int)CommandID.CmdRoomPlayerReady, OnRoomPlayerReady); - NetMsg.Instance.RegNetMsgEvent((int)CommandID.CmdRoomSingelPlayerInput, OnSingelPlayerInput); - - NetMsg.Instance.RegNetMsgEvent((int)CommandID.CmdScreen, OnCmdScreen); - - roomTickARE = AppSrv.g_Tick.AddNewARE(TickManager.TickType.Interval_16MS); - threadRoomTick = new Thread(UpdateLoopTick); - threadRoomTick.Start(); - - //System.Timers.Timer mTimer16ms = new System.Timers.Timer(16);//实例化Timer类 - //mTimer16ms.Elapsed += new System.Timers.ElapsedEventHandler((source, e) => { UpdateAllRoomLogic(); });//到达时间的时候执行事件; - //mTimer16ms.AutoReset = true;//设置是执行一次(false)还是一直执行(true); - //mTimer16ms.Enabled = true;//是否执行System.Timers.Timer.Elapsed事件; - //mTimer16ms.Start(); - } - - #region 房间管理 - - int GetNewRoomID() - { - return RoomIDSeed++; - } - - void AddRoom(Data_RoomData data) - { - lock (mDictRoom) - { - if (!mDictRoom.ContainsKey(data.RoomID)) - { - mDictRoom.Add(data.RoomID, data); - mKeyRoomList.Add(data.RoomID); - } - } - } - - void RemoveRoom(int RoomID) - { - lock (mDictRoom) - { - if (mDictRoom.ContainsKey(RoomID)) - { - mDictRoom.Remove(RoomID); - mKeyRoomList.Remove(RoomID); - } - } - } - - public Data_RoomData GetRoomData(int RoomID) - { - if (!mDictRoom.TryGetValue(RoomID, out Data_RoomData data)) - return null; - return data; - } - - public List GetRoomList() - { - lock (mDictRoom) - { - List temp = new List(); - foreach (var room in mDictRoom) - { - temp.AddRange(mDictRoom.Values); - } - return temp; - } - } - - #endregion - - #region - - public enum RoomLogType - { - Create = 0, - Join = 1, - Leave = 2 - } - public void RoomLog(long uid, int platform, int RoomID, int RomID, RoomLogType state) - { - MySqlConnection conn = Haoyue_SQLPoolManager.DequeueSQLConn("RoomLog"); - try - { - string query = "INSERT INTO `haoyue_emu`.`room_log` (`uid`, `platform`, `romid`,`roomid`, `state`) VALUES ( ?uid, ?platform, ?romid, ?roomid, ?state);"; - using (var command = new MySqlCommand(query, conn)) - { - // 设置参数值 - command.Parameters.AddWithValue("?uid", uid); - command.Parameters.AddWithValue("?platform", platform); - command.Parameters.AddWithValue("?romid", RomID); - command.Parameters.AddWithValue("?roomid", RoomID); - command.Parameters.AddWithValue("?state", state); - command.ExecuteNonQuery(); - } - - if (state == RoomLogType.Create) - { - query = "update romlist_nes set playcount = playcount + 1 where id = ?romid"; - using (var command = new MySqlCommand(query, conn)) - { - command.Parameters.AddWithValue("?romid", RomID); - command.ExecuteNonQuery(); - } - } - } - catch (Exception e) - { - } - Haoyue_SQLPoolManager.EnqueueSQLConn(conn); - } - #endregion - - private Protobuf_Room_MiniInfo GetProtoDataRoom(Data_RoomData room) - { - Protobuf_Room_MiniInfo result = new Protobuf_Room_MiniInfo() - { - GameRomID = room.GameRomID, - RoomID = room.RoomID, - GameRomHash = room.RomHash, - ScreenProviderUID = room.ScreenProviderUID, - HostPlayerUID = room.HostUID, - GameState = room.GameState, - ObsUserCount = 0,//TODO - Player1UID = room.Player1_UID, - Player2UID = room.Player2_UID, - Player3UID = room.Player3_UID, - Player4UID = room.Player4_UID, - }; - - if (result.Player1UID >= 0 && AppSrv.g_ClientMgr.GetClientByUID(result.Player1UID, out ClientInfo _c1)) - result.Player1NickName = _c1.NickName; - - if (result.Player2UID >= 0 && AppSrv.g_ClientMgr.GetClientByUID(result.Player2UID, out ClientInfo _c2)) - result.Player2NickName = _c2.NickName; - - if (result.Player3UID >= 0 && AppSrv.g_ClientMgr.GetClientByUID(result.Player1UID, out ClientInfo _c3)) - result.Player3NickName = _c3.NickName; - - if (result.Player4UID >= 0 && AppSrv.g_ClientMgr.GetClientByUID(result.Player2UID, out ClientInfo _c4)) - result.Player4NickName = _c4.NickName; - - return result; - } - - public void OnCmdRoomList(Socket sk, byte[] reqData) - { - AppSrv.g_Log.DebugCmd($"OnCmdRoomList"); - ClientInfo _c = AppSrv.g_ClientMgr.GetClientForSocket(sk); - Protobuf_Room_List msg = ProtoBufHelper.DeSerizlize(reqData); - - Protobuf_Room_List_RESP resp = new Protobuf_Room_List_RESP(); - List temp = GetRoomList(); - foreach (var room in temp) - resp.RoomMiniInfoList.Add(GetProtoDataRoom(room)); - AppSrv.g_ClientMgr.ClientSend(_c, (int)CommandID.CmdRoomList, (int)ErrorCode.ErrorOk, ProtoBufHelper.Serizlize(resp)); - } - public void CmdRoomGetScreen(Socket sk, byte[] reqData) - { - AppSrv.g_Log.DebugCmd($"CmdRoomGetScreen"); - ClientInfo _c = AppSrv.g_ClientMgr.GetClientForSocket(sk); - Protobuf_Room_Get_Screen msg = ProtoBufHelper.DeSerizlize(reqData); - - Data_RoomData room = GetRoomData(_c.RoomState.RoomID); - bool bHadRoomStateChange = false; - ErrorCode Errcode = ErrorCode.ErrorOk; - Protobuf_Room_Get_Screen_RESP resp = new Protobuf_Room_Get_Screen_RESP(); - if (room == null) - Errcode = ErrorCode.ErrorRoomNotFound; - else - { - resp.FrameID = (int)room.mCurrServerFrameId; - resp.RoomID = room.RoomID; - resp.RawBitmap = room.ScreenRaw; - } - - AppSrv.g_ClientMgr.ClientSend(_c, (int)CommandID.CmdRoomGetScreen, (int)ErrorCode.ErrorOk, ProtoBufHelper.Serizlize(resp)); - } - - /// - /// - /// - /// - /// //[0] 更新或新增 [1] 删除 - public void SendRoomUpdateToAll(Data_RoomData room, int type) - { - if (room == null) - return; - - Protobuf_Room_Update_RESP resp = new Protobuf_Room_Update_RESP() - { - UpdateType = type, - RoomMiniInfo = GetProtoDataRoom(room) - }; - - AppSrv.g_ClientMgr.ClientSendALL((int)CommandID.CmdRoomListUpdate, (int)ErrorCode.ErrorOk, ProtoBufHelper.Serizlize(resp)); - } - - public void OnCmdRoomCreate(Socket sk, byte[] reqData) - { - AppSrv.g_Log.DebugCmd($"OnCmdRoomCreate"); - ClientInfo _c = AppSrv.g_ClientMgr.GetClientForSocket(sk); - Protobuf_Room_Create msg = ProtoBufHelper.DeSerizlize(reqData); - Protobuf_Room_Create_RESP resp = new Protobuf_Room_Create_RESP(); - - Data_RoomData newRoom = new Data_RoomData(); - newRoom.Init(GetNewRoomID(), msg.GameRomID, msg.GameRomHash, _c.UID); - AddRoom(newRoom); - ErrorCode joinErrcode = ErrorCode.ErrorOk; - //加入 - if (newRoom.Join(msg.JoinPlayerIdx, _c, out joinErrcode, out bool bHadRoomStateChange)) - { - //创建成功下行 - resp.RoomMiniInfo = GetProtoDataRoom(newRoom); - } - AppSrv.g_ClientMgr.ClientSend(_c, (int)CommandID.CmdRoomCreate, (int)joinErrcode, ProtoBufHelper.Serizlize(resp)); - - if (joinErrcode == ErrorCode.ErrorOk && bHadRoomStateChange) - SendRoomStepChange(newRoom); - - SendRoomUpdateToAll(newRoom, 0); - - RoomLog(_c.UID, 1, newRoom.RoomID, newRoom.GameRomID, RoomLogType.Create); - } - - public void OnCmdRoomJoin(Socket sk, byte[] reqData) - { - AppSrv.g_Log.DebugCmd($"OnCmdRoomJoin"); - ClientInfo _c = AppSrv.g_ClientMgr.GetClientForSocket(sk); - Protobuf_Room_Join msg = ProtoBufHelper.DeSerizlize(reqData); - Protobuf_Room_Create_RESP resp = new Protobuf_Room_Create_RESP(); - ErrorCode joinErrcode; - Data_RoomData room = GetRoomData(msg.RoomID); - bool bHadRoomStateChange = false; - if (room == null) - { - joinErrcode = ErrorCode.ErrorRoomNotFound; - - AppSrv.g_ClientMgr.ClientSend(_c, (int)CommandID.CmdRoomJoin, (int)joinErrcode, ProtoBufHelper.Serizlize(resp)); - return; - } - lock (room) - { - //加入 - if (room.Join(msg.PlayerNum, _c, out joinErrcode, out bHadRoomStateChange)) - { - Data_RoomData roomData = GetRoomData(msg.RoomID); - resp.RoomMiniInfo = GetProtoDataRoom(roomData); - } - - AppSrv.g_ClientMgr.ClientSend(_c, (int)CommandID.CmdRoomJoin, (int)joinErrcode, ProtoBufHelper.Serizlize(resp)); - Protobuf_Room_MyRoom_State_Change(msg.RoomID); - - if (joinErrcode == ErrorCode.ErrorOk && bHadRoomStateChange) - SendRoomStepChange(room); - - if (room != null) - { - SendRoomUpdateToAll(room, 0); - } - } - RoomLog(_c.UID, 1, room.RoomID, room.GameRomID, RoomLogType.Join); - } - public void OnCmdRoomLeave(Socket sk, byte[] reqData) - { - AppSrv.g_Log.DebugCmd($"OnCmdRoomLeave"); - ClientInfo _c = AppSrv.g_ClientMgr.GetClientForSocket(sk); - Protobuf_Room_Leave msg = ProtoBufHelper.DeSerizlize(reqData); - LeaveRoom(_c, msg.RoomID); - //Protobuf_Room_Leave_RESP resp = new Protobuf_Room_Leave_RESP(); - //ErrorCode errcode; - //Data_RoomData room = GetRoomData(_c.RoomState.RoomID); - //bool bHadRoomStateChange = false; - //if (room == null) - // errcode = ErrorCode.ErrorRoomNotFound; - //else - //{ - // if (room.Leave(_c, out errcode, out bHadRoomStateChange)) - // { - // resp.RoomID = msg.RoomID; - // } - //} - //AppSrv.g_ClientMgr.ClientSend(_c, (int)CommandID.CmdRoomLeave, (int)errcode, ProtoBufHelper.Serizlize(resp)); - //Protobuf_Room_MyRoom_State_Change(msg.RoomID); - - //if (errcode == ErrorCode.ErrorOk && bHadRoomStateChange) - // SendRoomStepChange(room); - - //SendRoomUpdateToAll(room.RoomID, 1); - //if (room.GetPlayerCount() < 1) - // RemoveRoom(room.RoomID); - } - - public void LeaveRoom(ClientInfo _c, int RoomID) - { - AppSrv.g_Log.Debug($"LeaveRoom"); - if (RoomID < 0) - return; - Protobuf_Room_Leave_RESP resp = new Protobuf_Room_Leave_RESP(); - ErrorCode errcode; - Data_RoomData room = GetRoomData(_c.RoomState.RoomID); - bool bHadRoomStateChange = false; - if (room == null) - { - errcode = ErrorCode.ErrorRoomNotFound; - AppSrv.g_ClientMgr.ClientSend(_c, (int)CommandID.CmdRoomLeave, (int)errcode, ProtoBufHelper.Serizlize(resp)); - return; - } - - if (room.Leave(_c, out errcode, out bHadRoomStateChange)) - { - resp.RoomID = RoomID; - } - AppSrv.g_ClientMgr.ClientSend(_c, (int)CommandID.CmdRoomLeave, (int)errcode, ProtoBufHelper.Serizlize(resp)); - Protobuf_Room_MyRoom_State_Change(RoomID); - - if (errcode == ErrorCode.ErrorOk && bHadRoomStateChange) - SendRoomStepChange(room); - - if (room.GetPlayerCount() < 1) - { - SendRoomUpdateToAll(room, 1); - RemoveRoom(room.RoomID); - } - else - SendRoomUpdateToAll(room, 0); - - RoomLog(_c.UID, 1, room.RoomID, room.GameRomID, RoomLogType.Leave); - } - - public void OnHostPlayerUpdateStateRaw(Socket sk, byte[] reqData) - { - ClientInfo _c = AppSrv.g_ClientMgr.GetClientForSocket(sk); - AppSrv.g_Log.DebugCmd($"OnHostPlayerUpdateStateRaw 上报即时存档 UID->{_c.UID}"); - Protobuf_Room_HostPlayer_UpdateStateRaw msg = ProtoBufHelper.DeSerizlize(reqData); - Protobuf_Room_HostPlayer_UpdateStateRaw_RESP resp = new Protobuf_Room_HostPlayer_UpdateStateRaw_RESP(); - ErrorCode errcode = ErrorCode.ErrorOk; - Data_RoomData room = GetRoomData(_c.RoomState.RoomID); - if (room == null) - errcode = ErrorCode.ErrorRoomNotFound; - else if (room.GameState != RoomGameState.WaitRawUpdate) - errcode = ErrorCode.ErrorRoomCantDoCurrState; - - AppSrv.g_ClientMgr.ClientSend(_c, (int)CommandID.CmdRoomHostPlayerUpdateStateRaw, (int)errcode, ProtoBufHelper.Serizlize(resp)); - - if (errcode == ErrorCode.ErrorOk) - { - room.SetLoadRaw(msg.LoadStateRaw, out bool bHadRoomStateChange); - if (bHadRoomStateChange) - SendRoomStepChange(room); - } - } - - public void OnRoomPlayerReady(Socket sk, byte[] reqData) - { - ClientInfo _c = AppSrv.g_ClientMgr.GetClientForSocket(sk); - AppSrv.g_Log.DebugCmd($"OnRoomPlayerReady _c->{_c.UID}"); - Protobuf_Room_Player_Ready msg = ProtoBufHelper.DeSerizlize(reqData); - ErrorCode errcode = ErrorCode.ErrorOk; - Data_RoomData room = GetRoomData(_c.RoomState.RoomID); - if (room == null) - return; - lock (room) - { - AppSrv.g_Log.Debug($"SetRePlayerReady RoomID->{room.RoomID},UID->{_c.UID}, PlayerIdx->{_c.RoomState.PlayerIdx}"); - room.SetRePlayerReady(_c.RoomState.PlayerIdx, out errcode, out bool bHadRoomStateChange); - if (bHadRoomStateChange) - { - SendRoomStepChange(room); - } - } - } - - public void OnSingelPlayerInput(Socket sk, byte[] reqData) - { - ClientInfo _c = AppSrv.g_ClientMgr.GetClientForSocket(sk); - Protobuf_Room_SinglePlayerInputData msg = ProtoBufHelper.DeSerizlize(reqData); - Data_RoomData room = GetRoomData(_c.RoomState.RoomID); - if (room == null) - return; - - //取玩家操作数据中的第一个 - ServerInputSnapShot temp = new ServerInputSnapShot(); - temp.all = msg.InputData; - //room.SetPlayerInput(_c.RoomState.PlayerIdx, msg.FrameID, temp); - - //是否需要推帧 - if (room.GetNeedForwardTick(msg.FrameID, out long forwaFrame)) - { - for (int i = 0; i < forwaFrame; i++) - { - if (i + 1 == forwaFrame)//最后一帧 - { - //写入操作前、将网络波动堆积,可能造成瞬时多个连续推帧结果(最后一帧除外)立即广播,不等16msTick - //if (forwaFrame > 1) - // room.SynInputData(); - - //推帧过程中,最后一帧才写入操作 - room.SetPlayerInput(_c.RoomState.PlayerIdx, msg.FrameID, temp); - } - //推帧 - room.TakeFrame(); - } - } - else//不需要推帧 - { - //虽然不推帧,但是存入Input - room.SetPlayerInput(_c.RoomState.PlayerIdx, msg.FrameID, temp); - } - - if (room.LastTestRecv != room.mCurrInputData.all) - { - room.LastTestRecv = room.mCurrInputData.all; - //AppSrv.g_Log.Debug($" {DateTime.Now.ToString("hh:mm:ss.fff")} SynTestRecv=> UID->{_c.UID} roomId->{room.mCurrServerFrameId} input->{msg.InputData}"); - } - } - - public void OnCmdScreen(Socket sk, byte[] reqData) - { - AppSrv.g_Log.DebugCmd($"OnCmdScreen lenght:{reqData.Length}"); - ClientInfo _c = AppSrv.g_ClientMgr.GetClientForSocket(sk); - Protobuf_Screnn_Frame msg = ProtoBufHelper.DeSerizlize(reqData); - Data_RoomData room = AppSrv.g_Room.GetRoomData(msg.RoomID); - room.InputScreenData(msg.RawBitmap); - } - - /// - /// 广播房间状态变化 - /// - /// - public void Protobuf_Room_MyRoom_State_Change(int RoomID) - { - Data_RoomData room = GetRoomData(RoomID); - if (room == null) - return; - - Protobuf_Room_MyRoom_State_Change resp = new Protobuf_Room_MyRoom_State_Change() - { - RoomMiniInfo = GetProtoDataRoom(room) - }; - - List userlist = room.GetAllPlayerClientList(); - - foreach (ClientInfo _c in userlist) - { - AppSrv.g_ClientMgr.ClientSend(_c, (int)CommandID.CmdRoomMyRoomStateChanged, (int)ErrorCode.ErrorOk, ProtoBufHelper.Serizlize(resp)); - } - } - - /// - /// 广播联机Step - /// - /// - public void SendRoomStepChange(Data_RoomData room) - { - List roomClient = room.GetAllPlayerClientList(); - switch (room.GameState) - { - case RoomGameState.WaitRawUpdate: - { - Protobuf_Room_WaitStep_RESP resp = new Protobuf_Room_WaitStep_RESP() - { - WaitStep = 0 - }; - AppSrv.g_Log.DebugCmd($"Step:0 WaitRawUpdate 广播等待主机上报即时存档"); - AppSrv.g_ClientMgr.ClientSend(roomClient, (int)CommandID.CmdRoomWaitStep, (int)ErrorCode.ErrorOk, ProtoBufHelper.Serizlize(resp)); - } - break; - case RoomGameState.WaitReady: - { - Protobuf_Room_WaitStep_RESP resp = new Protobuf_Room_WaitStep_RESP() - { - WaitStep = 1, - LoadStateRaw = room.NextStateRaw - }; - AppSrv.g_Log.DebugCmd($"Step:1 WaitReady 广播即时存档"); - AppSrv.g_ClientMgr.ClientSend(roomClient, (int)CommandID.CmdRoomWaitStep, (int)ErrorCode.ErrorOk, ProtoBufHelper.Serizlize(resp)); - } - break; - case RoomGameState.InOnlineGame: - { - Protobuf_Room_WaitStep_RESP resp = new Protobuf_Room_WaitStep_RESP() - { - WaitStep = 2, - }; - AppSrv.g_Log.DebugCmd($"Step:2 InOnlineGame 广播开始游戏"); - AppSrv.g_ClientMgr.ClientSend(roomClient, (int)CommandID.CmdRoomWaitStep, (int)ErrorCode.ErrorOk, ProtoBufHelper.Serizlize(resp)); - } - break; - } - } - - #region 房间帧循环 - void UpdateLoopTick() - { - while (true) - { - roomTickARE.WaitOne(); - UpdateAllRoomLogic(); - } - } - void UpdateAllRoomLogic() - { - if (mKeyRoomList.Count < 1) - return; - for (int i = 0; i < mKeyRoomList.Count; i++) - { - int roomid = mKeyRoomList[i]; - if (!mDictRoom.TryGetValue(roomid, out Data_RoomData room) || room.GameState < RoomGameState.InOnlineGame) - continue; - //更新帧 - //room.TakeFrame(); - //广播 - room.SynInputData(); - } - } - #endregion - } - - public class Data_RoomData - { - public int RoomID { get; private set; } - public int GameRomID { get; private set; } - public string RomHash { get; private set; } - public long HostUID { get; private set; } - public long ScreenProviderUID { get; private set; } - public long Player1_UID { get; private set; } - public long Player2_UID { get; private set; } - public long Player3_UID { get; private set; } - public long Player4_UID { get; private set; } - public Google.Protobuf.ByteString? NextStateRaw { get; private set; } - public Google.Protobuf.ByteString? ScreenRaw { get; private set; } - public bool[] PlayerReadyState { get; private set; } - public List SynUIDs; - //public RoomPlayerState PlayerState => getPlayerState(); - private RoomGameState mGameState; - public uint mCurrServerFrameId = 0; - public ServerInputSnapShot mCurrInputData; - public Queue<(uint, ServerInputSnapShot)> mInputQueue; - object synInputLock = new object(); - //TODO - public Dictionary> mDictPlayerIdx2SendQueue; - public RoomGameState GameState - { - get { return mGameState; } - set - { - if (mGameState != value) - { - mGameState = value; - switch (value) - { - case RoomGameState.WaitRawUpdate: - NextStateRaw = null; - break; - case RoomGameState.WaitReady: - Array.Fill(PlayerReadyState, false); - break; - } - } - } - } - /// - /// 服务器提前帧数 - /// - public uint SrvForwardFrames { get; set; } - bool IsAllReady() - { - bool Ready = true; - if ( - (Player1_UID > 0 && !PlayerReadyState[0]) - || - (Player2_UID > 0 && !PlayerReadyState[1]) - || - (Player3_UID > 0 && !PlayerReadyState[2]) - || - (Player4_UID > 0 && !PlayerReadyState[3]) - ) - { - Ready = false; - } - return Ready; - } - public void Init(int roomID, int gameRomID, string roomHash, long hostUId, bool bloadState = false) - { - RoomID = roomID; - GameRomID = gameRomID; - RomHash = roomHash; - HostUID = hostUId; - ScreenProviderUID = hostUId; - Player1_UID = -1; - Player2_UID = -1; - Player3_UID = -1; - Player4_UID = -1; - PlayerReadyState = new bool[4]; - SynUIDs = new List();//广播角色列表 - GameState = RoomGameState.NoneGameState; - mCurrInputData = new ServerInputSnapShot(); - mInputQueue = new Queue<(uint, ServerInputSnapShot)>(); - mDictPlayerIdx2SendQueue = new Dictionary>(); - } - public void SetPlayerUID(int PlayerIdx, ClientInfo _c) - { - long oldUID = -1; - switch (PlayerIdx) - { - case 0: oldUID = Player1_UID; Player1_UID = _c.UID; break; - case 1: oldUID = Player2_UID; Player2_UID = _c.UID; break; - case 2: oldUID = Player3_UID; Player3_UID = _c.UID; break; - case 3: oldUID = Player4_UID; Player4_UID = _c.UID; break; - } - if (oldUID >= 0) - SynUIDs.Remove(oldUID); - SynUIDs.Add(_c.UID); - AppSrv.g_Log.Debug($"SetPlayerUID RoomID->{RoomID} _c.UID->{_c.UID} PlayerIdx->{PlayerIdx}"); - _c.RoomState.SetRoomData(this.RoomID, PlayerIdx); - } - public void RemovePlayer(ClientInfo _c) - { - int PlayerIdx = GetPlayerIdx(_c); - switch (PlayerIdx) - { - case 0: Player1_UID = -1; SynUIDs.Remove(_c.UID); break; - case 1: Player2_UID = -1; SynUIDs.Remove(_c.UID); break; - case 2: Player3_UID = -1; SynUIDs.Remove(_c.UID); break; - case 3: Player4_UID = -1; SynUIDs.Remove(_c.UID); break; - } - _c.RoomState.ClearRoomData(); - } - int GetPlayerIdx(ClientInfo _c) - { - if (Player1_UID == _c.UID) return 0; - if (Player2_UID == _c.UID) return 1; - if (Player3_UID == _c.UID) return 2; - if (Player4_UID == _c.UID) return 3; - return -1; - } - public bool GetPlayerUIDByIdx(int Idx, out long UID) - { - switch (Idx) - { - case 0: UID = Player1_UID; break; - case 1: UID = Player2_UID; break; - case 2: UID = Player3_UID; break; - case 3: UID = Player4_UID; break; - default: UID = -1; break; - } - return UID > 0; - } - public bool GetPlayerClientByIdx(int Idx, out ClientInfo _c) - { - _c = null; - if (!GetPlayerUIDByIdx(Idx, out long UID)) - return false; - - if (!AppSrv.g_ClientMgr.GetClientByUID(UID, out _c)) - return false; - - return true; - } - public List GetAllPlayerUIDs() - { - List list = new List(); - if (Player1_UID > 0) list.Add(Player1_UID); - if (Player2_UID > 0) list.Add(Player2_UID); - if (Player3_UID > 0) list.Add(Player3_UID); - if (Player4_UID > 0) list.Add(Player4_UID); - return list; - } - public List GetAllPlayerClientList() - { - List list = new List(); - - List Uids = GetAllPlayerUIDs(); - - foreach (long uid in Uids) - { - if (!AppSrv.g_ClientMgr.GetClientByUID(uid, out ClientInfo _c, true)) - continue; - - list.Add(_c); - } - - return list; - } - public void SetPlayerInput(int PlayerIdx, long mFrameID, ServerInputSnapShot allinput) - { - switch (PlayerIdx) - { - case 0: mCurrInputData.p1_byte = allinput.p1_byte; break; - case 1: mCurrInputData.p2_byte = allinput.p1_byte; break; - case 2: mCurrInputData.p3_byte = allinput.p1_byte; break; - case 3: mCurrInputData.p4_byte = allinput.p1_byte; break; - } - } - public void ClearPlayerInput(ClientInfo _c) - { - int PlayerIdx = GetPlayerIdx(_c); - switch (PlayerIdx) - { - case 0: mCurrInputData.p1_byte = 0; break; - case 1: mCurrInputData.p2_byte = 0; break; - case 2: mCurrInputData.p3_byte = 0; break; - case 3: mCurrInputData.p4_byte = 0; break; - } - } - public int GetPlayerCount() - { - int count = 0; - if (Player1_UID > 0) count++; - if (Player2_UID > 0) count++; - if (Player3_UID > 0) count++; - if (Player4_UID > 0) count++; - return count; - } - void StartNewTick() - { - mInputQueue.Clear(); - mDictPlayerIdx2SendQueue.Clear(); - - mCurrServerFrameId = 0; - mCurrInputData.all = 1; - - UpdateRoomForwardNum(); - - uint StartForwardFrames = (SrvForwardFrames * 2) + 5; - StartForwardFrames = Math.Min(10, StartForwardFrames); - //服务器提前跑帧数 - for (int i = 0; i < SrvForwardFrames; i++) - TakeFrame(); - AppSrv.g_Log.Info($"房间初始提前量=>{StartForwardFrames},当前延迟提前量=>{SrvForwardFrames}"); - } - - public void UpdateRoomForwardNum() - { - List playerlist = GetAllPlayerClientList(); - double maxNetDelay = 0; - for (int i = 0; i < playerlist.Count; i++) - { - ClientInfo player = playerlist[i]; - maxNetDelay = Math.Max(maxNetDelay, player.AveNetDelay); - } - float MustTaskFrame = 1; - SrvForwardFrames = (uint)((maxNetDelay / 0.016f) + MustTaskFrame); - if (SrvForwardFrames < 2) - SrvForwardFrames = 2; - //AppSrv.g_Log.Debug($"服务器提前跑帧数:Max(2,({maxNetDelay} / {0.016f}) + {MustTaskFrame}) = {SrvForwardFrames}"); - } - - public void TakeFrame() - { - lock (synInputLock) - { - mInputQueue.Enqueue((mCurrServerFrameId, mCurrInputData)); - mCurrServerFrameId++; - if (mCurrServerFrameId % 60 == 0) - { - UpdateRoomForwardNum(); - } - } - } - - ulong LastTestSend = 0; - internal ulong LastTestRecv; - - public List send2time = new List(); - const int SynLimitOnSec = 63; - - /// - /// 广播数据 - /// - public void SynInputData() - { - List<(uint frameId, ServerInputSnapShot inputdata)> temp = null; - bool flagInitList = false; - lock (synInputLock) - { - double timeNow = AppSrv.g_Tick.timeNow; - while (mInputQueue.Count > 0) - { - if (send2time.Count >= SynLimitOnSec) - { - //AppSrv.g_Log.Info($"{timeNow} - {send2time[0]} =>{timeNow - send2time[0]}"); - if (timeNow - send2time[0] < 1f) //最早的历史发送还在一秒之内 - break; - else - send2time.RemoveAt(0); - } - - if (!flagInitList) - { - flagInitList = true; - temp = new List<(uint frameId, ServerInputSnapShot inputdata)>(); - } - temp.Add(mInputQueue.Dequeue()); - send2time.Add(timeNow); - } - //while (mInputQueue.Count > 0) - //{ - // temp.Add(mInputQueue.Dequeue()); - //} - } - - if (!flagInitList) - return; - - for (int i = 0; i < temp.Count; i++) - { - (uint frameId, ServerInputSnapShot inputdata) data = temp[i]; - - Protobuf_Room_Syn_RoomFrameAllInputData resp = new Protobuf_Room_Syn_RoomFrameAllInputData() - { - FrameID = data.frameId, - InputData = data.inputdata.all, - ServerFrameID = mCurrServerFrameId, - ServerForwardCount = this.SrvForwardFrames - }; - AppSrv.g_ClientMgr.ClientSend(SynUIDs, (int)CommandID.CmdRoomSynPlayerInput, (int)ErrorCode.ErrorOk, ProtoBufHelper.Serizlize(resp)); - //if (LastTestSend != data.inputdata.all) - //{ - // LastTestSend = data.inputdata.all; - // AppSrv.g_Log.Debug($" {DateTime.Now.ToString("hh:mm:ss.fff")} SynInput=> RoomID->{RoomID} ServerFrameID->{mCurrServerFrameId} SynUIDs=>{string.Join(",", SynUIDs)} "); - //} - } - } - - #region 房间进出 - /// - /// 进入房间 - /// - /// - /// - /// - /// - /// - public bool Join(int PlayerNum, ClientInfo _c, out ErrorCode errcode, out bool bHadRoomStateChange) - { - bHadRoomStateChange = false; - int oldPlayerCount = GetPlayerCount(); - if (GetPlayerUIDByIdx(PlayerNum, out long hadUID)) - { - errcode = ErrorCode.ErrorRoomSlotAlreadlyHadPlayer; - return false; - } - AppSrv.g_Log.Debug($"Join _c.UID->{_c.UID} RoomID->{RoomID}"); - SetPlayerUID(PlayerNum, _c); - int newPlayerCount = GetPlayerCount(); - errcode = ErrorCode.ErrorOk; - - bHadRoomStateChange = CheckRoomStateChange(oldPlayerCount, newPlayerCount); - return true; - } - - /// - /// 离开房间 - /// - /// - /// - /// - /// - public bool Leave(ClientInfo _c, out ErrorCode errcode, out bool bHadRoomStateChange) - { - int oldPlayerCount = GetPlayerCount(); - RemovePlayer(_c); - ClearPlayerInput(_c); - int newPlayerCount = GetPlayerCount(); - errcode = ErrorCode.ErrorOk; - bHadRoomStateChange = CheckRoomStateChange(oldPlayerCount, newPlayerCount); - return true; - } - #endregion - - public bool SetRePlayerReady(int PlayerIdx, out ErrorCode errcode, out bool bHadRoomStateChange) - { - int oldPlayerCount = GetPlayerCount(); - PlayerReadyState[PlayerIdx] = true; - int newPlayerCount = GetPlayerCount(); - errcode = ErrorCode.ErrorOk; - bHadRoomStateChange = CheckRoomStateChange(oldPlayerCount, newPlayerCount); - return true; - } - - bool CheckRoomStateChange(int oldPlayerCount, int newPlayerCount) - { - bool bChanged = false; - bool bNewToOnlyHost = (oldPlayerCount != 1 && newPlayerCount == 1); - bool bMorePlayer = (oldPlayerCount < 2 && newPlayerCount >= 2) || (newPlayerCount > oldPlayerCount); - switch (this.GameState) - { - case RoomGameState.NoneGameState: - if (bNewToOnlyHost) - { - this.GameState = RoomGameState.OnlyHost; - bChanged = true; - } - break; - case RoomGameState.OnlyHost: - if (bMorePlayer)//加入更多玩家 - { - this.GameState = RoomGameState.WaitRawUpdate; - bChanged = true; - break; - } - break; - case RoomGameState.WaitRawUpdate: - if (bMorePlayer)//加入更多玩家 - { - this.GameState = RoomGameState.WaitRawUpdate; - bChanged = true; - break; - } - if (NextStateRaw != null)//已经上传即时存档 - { - this.GameState = RoomGameState.WaitReady; - bChanged = true; - break; - } - break; - case RoomGameState.WaitReady: - if (bMorePlayer)//加入更多玩家 - { - this.GameState = RoomGameState.WaitRawUpdate; - bChanged = true; - break; - } - //没有未准备的 - bool bAllReady = IsAllReady(); - if (bAllReady) - { - this.GameState = RoomGameState.InOnlineGame; - //新开Tick - StartNewTick(); - bChanged = true; - break; - } - break; - case RoomGameState.Pause: - if (bMorePlayer)//加入更多玩家 - { - this.GameState = RoomGameState.WaitRawUpdate; - bChanged = true; - break; - } - break; - case RoomGameState.InOnlineGame: - if (bMorePlayer)//加入更多玩家 - { - this.GameState = RoomGameState.WaitRawUpdate; - bChanged = true; - break; - } - break; - } - return bChanged; - } - - public void SetLoadRaw(Google.Protobuf.ByteString NextStateRaw, out bool bHadRoomStateChange) - { - int oldPlayerCount = GetPlayerCount(); - AppSrv.g_Log.Debug($"SetLoadRaw proto Lenght->{NextStateRaw.Length}"); - this.NextStateRaw = NextStateRaw; - int newPlayerCount = GetPlayerCount(); - bHadRoomStateChange = CheckRoomStateChange(oldPlayerCount, newPlayerCount); - } - - public void InputScreenData(Google.Protobuf.ByteString screenRaw) - { - this.ScreenRaw = NextStateRaw; - } - - public bool GetNeedForwardTick(uint clientFrame, out long forwaFrame) - { - forwaFrame = 0; - //目标帧,客户端+服务器提前量 - long targetFrame = clientFrame + SrvForwardFrames; - if (targetFrame > mCurrServerFrameId)//更靠前 - forwaFrame = targetFrame - mCurrServerFrameId; - return forwaFrame > 0; - } - } - - [StructLayout(LayoutKind.Explicit, Size = 8)] - public struct ServerInputSnapShot - { - [FieldOffset(0)] - public UInt64 all; - - [FieldOffset(0)] - public byte p1_byte; - [FieldOffset(1)] - public byte p2_byte; - [FieldOffset(2)] - public byte p3_byte; - [FieldOffset(3)] - public byte p4_byte; - - [FieldOffset(0)] - public ushort p1_ushort; - [FieldOffset(2)] - public ushort p2_ushort; - [FieldOffset(4)] - public ushort p3_ushort; - [FieldOffset(6)] - public ushort p4_ushort; - } + public class RoomManager + { + Dictionary mDictRoom = new Dictionary(); + List mKeyRoomList = new List(); + AutoResetEvent roomTickARE; + Thread threadRoomTick; + + int RoomIDSeed = 1; + public RoomManager() + { + NetMsg.Instance.RegNetMsgEvent((int)CommandID.CmdRoomList, OnCmdRoomList); + NetMsg.Instance.RegNetMsgEvent((int)CommandID.CmdRoomGetScreen, CmdRoomGetScreen); + NetMsg.Instance.RegNetMsgEvent((int)CommandID.CmdRoomCreate, OnCmdRoomCreate); + NetMsg.Instance.RegNetMsgEvent((int)CommandID.CmdRoomJoin, OnCmdRoomJoin); + NetMsg.Instance.RegNetMsgEvent((int)CommandID.CmdRoomLeave, OnCmdRoomLeave); + NetMsg.Instance.RegNetMsgEvent((int)CommandID.CmdRoomChangePlayerWithJoy, OnCmdRoomChangePlayerWithJoy); + NetMsg.Instance.RegNetMsgEvent((int)CommandID.CmdRoomHostPlayerUpdateStateRaw, OnHostPlayerUpdateStateRaw); + NetMsg.Instance.RegNetMsgEvent((int)CommandID.CmdRoomPlayerReady, OnRoomPlayerReady); + NetMsg.Instance.RegNetMsgEvent((int)CommandID.CmdRoomSingelPlayerInput, OnSingelPlayerInput); + + NetMsg.Instance.RegNetMsgEvent((int)CommandID.CmdScreen, OnCmdScreen); + + roomTickARE = AppSrv.g_Tick.AddNewARE(TickManager.TickType.Interval_16MS); + threadRoomTick = new Thread(UpdateLoopTick); + threadRoomTick.Start(); + + //System.Timers.Timer mTimer16ms = new System.Timers.Timer(16);//实例化Timer类 + //mTimer16ms.Elapsed += new System.Timers.ElapsedEventHandler((source, e) => { UpdateAllRoomLogic(); });//到达时间的时候执行事件; + //mTimer16ms.AutoReset = true;//设置是执行一次(false)还是一直执行(true); + //mTimer16ms.Enabled = true;//是否执行System.Timers.Timer.Elapsed事件; + //mTimer16ms.Start(); + } + + #region 房间管理 + + int GetNewRoomID() + { + return RoomIDSeed++; + } + + void AddRoom(Data_RoomData data) + { + lock (mDictRoom) + { + if (!mDictRoom.ContainsKey(data.RoomID)) + { + mDictRoom.Add(data.RoomID, data); + mKeyRoomList.Add(data.RoomID); + } + } + } + + void RemoveRoom(int RoomID) + { + lock (mDictRoom) + { + if (mDictRoom.ContainsKey(RoomID)) + { + mDictRoom.Remove(RoomID); + mKeyRoomList.Remove(RoomID); + } + } + } + + public Data_RoomData GetRoomData(int RoomID) + { + if (!mDictRoom.TryGetValue(RoomID, out Data_RoomData data)) + return null; + return data; + } + + public List GetRoomList() + { + lock (mDictRoom) + { + List temp = new List(); + foreach (var room in mDictRoom) + { + temp.AddRange(mDictRoom.Values); + } + return temp; + } + } + + #endregion + + #region + + public enum RoomLogType + { + Create = 0, + Join = 1, + Leave = 2 + } + public void RoomLog(long uid, int platform, int RoomID, int RomID, RoomLogType state) + { + MySqlConnection conn = Haoyue_SQLPoolManager.DequeueSQLConn("RoomLog"); + try + { + string query = "INSERT INTO `haoyue_emu`.`room_log` (`uid`, `platform`, `romid`,`roomid`, `state`) VALUES ( ?uid, ?platform, ?romid, ?roomid, ?state);"; + using (var command = new MySqlCommand(query, conn)) + { + // 设置参数值 + command.Parameters.AddWithValue("?uid", uid); + command.Parameters.AddWithValue("?platform", platform); + command.Parameters.AddWithValue("?romid", RomID); + command.Parameters.AddWithValue("?roomid", RoomID); + command.Parameters.AddWithValue("?state", state); + command.ExecuteNonQuery(); + } + + if (state == RoomLogType.Create) + { + query = "update romlist_nes set playcount = playcount + 1 where id = ?romid"; + using (var command = new MySqlCommand(query, conn)) + { + command.Parameters.AddWithValue("?romid", RomID); + command.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + } + Haoyue_SQLPoolManager.EnqueueSQLConn(conn); + } + #endregion + + private Protobuf_Room_MiniInfo GetProtoDataRoom(Data_RoomData room) + { + Protobuf_Room_MiniInfo result = new Protobuf_Room_MiniInfo() + { + GameRomID = room.GameRomID, + RoomID = room.RoomID, + GameRomHash = room.RomHash, + ScreenProviderUID = room.ScreenProviderUID, + HostPlayerUID = room.HostUID, + GameState = room.GameState, + ObsUserCount = 0,//TODO + }; + + for (byte i = 0; i < room.PlayerSlot.Count(); i++) + { + Protobuf_Room_GamePlaySlot pbSlot = new Protobuf_Room_GamePlaySlot(); + Data_RoomSlot slot = room.PlayerSlot[i]; + if (slot.UID > 0) + { + pbSlot.PlayerUID = slot.UID; + pbSlot.PlayerLocalJoyIdx = (int)slot.LocalJoyIdx; + if (AppSrv.g_ClientMgr.GetClientByUID(pbSlot.PlayerUID, out ClientInfo _client)) + pbSlot.PlayerNickName = _client.NickName; + } + result.GamePlaySlotList.Add(pbSlot); + } + + return result; + } + + public void OnCmdRoomList(Socket sk, byte[] reqData) + { + AppSrv.g_Log.DebugCmd($"OnCmdRoomList"); + ClientInfo _c = AppSrv.g_ClientMgr.GetClientForSocket(sk); + Protobuf_Room_List msg = ProtoBufHelper.DeSerizlize(reqData); + + Protobuf_Room_List_RESP resp = new Protobuf_Room_List_RESP(); + List temp = GetRoomList(); + foreach (var room in temp) + resp.RoomMiniInfoList.Add(GetProtoDataRoom(room)); + AppSrv.g_ClientMgr.ClientSend(_c, (int)CommandID.CmdRoomList, (int)ErrorCode.ErrorOk, ProtoBufHelper.Serizlize(resp)); + } + public void CmdRoomGetScreen(Socket sk, byte[] reqData) + { + AppSrv.g_Log.DebugCmd($"CmdRoomGetScreen"); + ClientInfo _c = AppSrv.g_ClientMgr.GetClientForSocket(sk); + Protobuf_Room_Get_Screen msg = ProtoBufHelper.DeSerizlize(reqData); + + Data_RoomData room = GetRoomData(_c.RoomState.RoomID); + bool bHadRoomStateChange = false; + ErrorCode Errcode = ErrorCode.ErrorOk; + Protobuf_Room_Get_Screen_RESP resp = new Protobuf_Room_Get_Screen_RESP(); + if (room == null) + Errcode = ErrorCode.ErrorRoomNotFound; + else + { + resp.FrameID = (int)room.mCurrServerFrameId; + resp.RoomID = room.RoomID; + resp.RawBitmap = room.ScreenRaw; + } + + AppSrv.g_ClientMgr.ClientSend(_c, (int)CommandID.CmdRoomGetScreen, (int)ErrorCode.ErrorOk, ProtoBufHelper.Serizlize(resp)); + } + + /// + /// + /// + /// + /// //[0] 更新或新增 [1] 删除 + public void SendRoomUpdateToAll(Data_RoomData room, int type) + { + if (room == null) + return; + + Protobuf_Room_Update_RESP resp = new Protobuf_Room_Update_RESP() + { + UpdateType = type, + RoomMiniInfo = GetProtoDataRoom(room) + }; + + AppSrv.g_ClientMgr.ClientSendALL((int)CommandID.CmdRoomListUpdate, (int)ErrorCode.ErrorOk, ProtoBufHelper.Serizlize(resp)); + } + + public void OnCmdRoomCreate(Socket sk, byte[] reqData) + { + AppSrv.g_Log.DebugCmd($"OnCmdRoomCreate"); + ClientInfo _c = AppSrv.g_ClientMgr.GetClientForSocket(sk); + Protobuf_Room_Create msg = ProtoBufHelper.DeSerizlize(reqData); + Protobuf_Room_Create_RESP resp = new Protobuf_Room_Create_RESP(); + + Data_RoomData newRoom = new Data_RoomData(); + newRoom.Init(GetNewRoomID(), msg.GameRomID, msg.GameRomHash, _c.UID); + AddRoom(newRoom); + ErrorCode joinErrcode = ErrorCode.ErrorOk; + //加入 + if (newRoom.Join((uint)msg.PlayerSlotIdx, (uint)msg.PlayerLocalJoyIdx, _c, out joinErrcode, out bool bHadRoomStateChange)) + { + //创建成功下行 + resp.RoomMiniInfo = GetProtoDataRoom(newRoom); + } + AppSrv.g_ClientMgr.ClientSend(_c, (int)CommandID.CmdRoomCreate, (int)joinErrcode, ProtoBufHelper.Serizlize(resp)); + + if (joinErrcode == ErrorCode.ErrorOk && bHadRoomStateChange) + SendRoomStepChange(newRoom); + + SendRoomUpdateToAll(newRoom, 0); + + RoomLog(_c.UID, 1, newRoom.RoomID, newRoom.GameRomID, RoomLogType.Create); + } + + public void OnCmdRoomJoin(Socket sk, byte[] reqData) + { + AppSrv.g_Log.DebugCmd($"OnCmdRoomJoin"); + ClientInfo _c = AppSrv.g_ClientMgr.GetClientForSocket(sk); + Protobuf_Room_Join msg = ProtoBufHelper.DeSerizlize(reqData); + Protobuf_Room_Join_RESP resp = new Protobuf_Room_Join_RESP(); + ErrorCode joinErrcode; + Data_RoomData room = GetRoomData(msg.RoomID); + bool bHadRoomStateChange = false; + if (room == null) + { + joinErrcode = ErrorCode.ErrorRoomNotFound; + + AppSrv.g_ClientMgr.ClientSend(_c, (int)CommandID.CmdRoomJoin, (int)joinErrcode, ProtoBufHelper.Serizlize(resp)); + return; + } + lock (room) + { + //加入 + if (room.Join((uint)msg.PlayerSlotIdx, (uint)msg.PlayerLocalJoyIdx, _c, out joinErrcode, out bHadRoomStateChange)) + { + Data_RoomData roomData = GetRoomData(msg.RoomID); + resp.RoomMiniInfo = GetProtoDataRoom(roomData); + } + + AppSrv.g_ClientMgr.ClientSend(_c, (int)CommandID.CmdRoomJoin, (int)joinErrcode, ProtoBufHelper.Serizlize(resp)); + Protobuf_Room_MyRoom_State_Change(msg.RoomID); + + if (joinErrcode == ErrorCode.ErrorOk && bHadRoomStateChange) + SendRoomStepChange(room); + + if (room != null) + { + SendRoomUpdateToAll(room, 0); + } + } + RoomLog(_c.UID, 1, room.RoomID, room.GameRomID, RoomLogType.Join); + } + public void OnCmdRoomLeave(Socket sk, byte[] reqData) + { + AppSrv.g_Log.DebugCmd($"OnCmdRoomLeave"); + ClientInfo _c = AppSrv.g_ClientMgr.GetClientForSocket(sk); + Protobuf_Room_Leave msg = ProtoBufHelper.DeSerizlize(reqData); + LeaveRoom(_c, msg.RoomID); + //Protobuf_Room_Leave_RESP resp = new Protobuf_Room_Leave_RESP(); + //ErrorCode errcode; + //Data_RoomData room = GetRoomData(_c.RoomState.RoomID); + //bool bHadRoomStateChange = false; + //if (room == null) + // errcode = ErrorCode.ErrorRoomNotFound; + //else + //{ + // if (room.Leave(_c, out errcode, out bHadRoomStateChange)) + // { + // resp.RoomID = msg.RoomID; + // } + //} + //AppSrv.g_ClientMgr.ClientSend(_c, (int)CommandID.CmdRoomLeave, (int)errcode, ProtoBufHelper.Serizlize(resp)); + //Protobuf_Room_MyRoom_State_Change(msg.RoomID); + + //if (errcode == ErrorCode.ErrorOk && bHadRoomStateChange) + // SendRoomStepChange(room); + + //SendRoomUpdateToAll(room.RoomID, 1); + //if (room.GetPlayerCount() < 1) + // RemoveRoom(room.RoomID); + } + public void LeaveRoom(ClientInfo _c, int RoomID) + { + AppSrv.g_Log.Debug($"LeaveRoom"); + if (RoomID < 0) + return; + Protobuf_Room_Leave_RESP resp = new Protobuf_Room_Leave_RESP(); + ErrorCode errcode; + Data_RoomData room = GetRoomData(_c.RoomState.RoomID); + bool bHadRoomStateChange = false; + if (room == null) + { + errcode = ErrorCode.ErrorRoomNotFound; + AppSrv.g_ClientMgr.ClientSend(_c, (int)CommandID.CmdRoomLeave, (int)errcode, ProtoBufHelper.Serizlize(resp)); + return; + } + + if (room.Leave(_c, out errcode, out bHadRoomStateChange)) + { + resp.RoomID = RoomID; + } + AppSrv.g_ClientMgr.ClientSend(_c, (int)CommandID.CmdRoomLeave, (int)errcode, ProtoBufHelper.Serizlize(resp)); + Protobuf_Room_MyRoom_State_Change(RoomID); + + if (errcode == ErrorCode.ErrorOk && bHadRoomStateChange) + SendRoomStepChange(room); + + if (room.GetPlayerCount() < 1) + { + SendRoomUpdateToAll(room, 1); + RemoveRoom(room.RoomID); + } + else + SendRoomUpdateToAll(room, 0); + + RoomLog(_c.UID, 1, room.RoomID, room.GameRomID, RoomLogType.Leave); + } + + + public void OnCmdRoomChangePlayerWithJoy(Socket sk, byte[] reqData) + { + AppSrv.g_Log.DebugCmd($"OnCmdRoomChangePlayerjoySlot"); + ClientInfo _c = AppSrv.g_ClientMgr.GetClientForSocket(sk); + Protobuf_Room_Change_PlaySlotWithJoy msg = ProtoBufHelper.DeSerizlize(reqData); + Protobuf_Room_Change_PlaySlotWithJoy_RESP resp = new Protobuf_Room_Change_PlaySlotWithJoy_RESP(); + ErrorCode errcode = ErrorCode.ErrorOk; + Data_RoomData room = GetRoomData(_c.RoomState.RoomID); + if (room == null) + errcode = ErrorCode.ErrorRoomNotFound; + + if (errcode == ErrorCode.ErrorOk) + { + Dictionary newSlotIdx2JoyIdx = new Dictionary(); + foreach (var slotinfo in msg.SlotWithJoy) + { + //如果有任意一个槽位有人 + if (room.GetPlayerUIDByIdx((uint)slotinfo.PlayerSlotIdx, out long UID)) + { + //且人不是自己,则不允许换位 + if(UID != _c.UID) + errcode = ErrorCode.ErrorRoomSlotAlreadlyHadPlayer; break; + } + + newSlotIdx2JoyIdx[(uint)slotinfo.PlayerSlotIdx] = (uint)slotinfo.PlayerLocalJoyIdx; + } + + room.SetPlayerSlotData(_c,ref newSlotIdx2JoyIdx); + } + AppSrv.g_ClientMgr.ClientSend(_c, (int)CommandID.CmdRoomMyRoomStateChanged, (int)errcode, ProtoBufHelper.Serizlize(resp)); + } + + + public void OnHostPlayerUpdateStateRaw(Socket sk, byte[] reqData) + { + ClientInfo _c = AppSrv.g_ClientMgr.GetClientForSocket(sk); + AppSrv.g_Log.DebugCmd($"OnHostPlayerUpdateStateRaw 上报即时存档 UID->{_c.UID}"); + Protobuf_Room_HostPlayer_UpdateStateRaw msg = ProtoBufHelper.DeSerizlize(reqData); + Protobuf_Room_HostPlayer_UpdateStateRaw_RESP resp = new Protobuf_Room_HostPlayer_UpdateStateRaw_RESP(); + ErrorCode errcode = ErrorCode.ErrorOk; + Data_RoomData room = GetRoomData(_c.RoomState.RoomID); + if (room == null) + errcode = ErrorCode.ErrorRoomNotFound; + else if (room.GameState != RoomGameState.WaitRawUpdate) + errcode = ErrorCode.ErrorRoomCantDoCurrState; + + AppSrv.g_ClientMgr.ClientSend(_c, (int)CommandID.CmdRoomHostPlayerUpdateStateRaw, (int)errcode, ProtoBufHelper.Serizlize(resp)); + + if (errcode == ErrorCode.ErrorOk) + { + room.SetLoadRaw(msg.LoadStateRaw, out bool bHadRoomStateChange); + if (bHadRoomStateChange) + SendRoomStepChange(room); + } + } + + public void OnRoomPlayerReady(Socket sk, byte[] reqData) + { + ClientInfo _c = AppSrv.g_ClientMgr.GetClientForSocket(sk); + AppSrv.g_Log.DebugCmd($"OnRoomPlayerReady _c->{_c.UID}"); + Protobuf_Room_Player_Ready msg = ProtoBufHelper.DeSerizlize(reqData); + ErrorCode errcode = ErrorCode.ErrorOk; + Data_RoomData room = GetRoomData(_c.RoomState.RoomID); + if (room == null) + return; + lock (room) + { + AppSrv.g_Log.Debug($"SetRePlayerReady RoomID->{room.RoomID},UID->{_c.UID}"); + room.SetRePlayerReady(_c.UID, out errcode, out bool bHadRoomStateChange); + if (bHadRoomStateChange) + { + SendRoomStepChange(room); + } + } + } + + public void OnSingelPlayerInput(Socket sk, byte[] reqData) + { + ClientInfo _c = AppSrv.g_ClientMgr.GetClientForSocket(sk); + Protobuf_Room_SinglePlayerInputData msg = ProtoBufHelper.DeSerizlize(reqData); + Data_RoomData room = GetRoomData(_c.RoomState.RoomID); + if (room == null) + return; + + //取玩家操作数据中的第一个 + ServerInputSnapShot temp = new ServerInputSnapShot(); + temp.all = msg.InputData; + //room.SetPlayerInput(_c.RoomState.PlayerIdx, msg.FrameID, temp); + + //是否需要推帧 + if (room.GetNeedForwardTick(msg.FrameID, out long forwaFrame)) + { + for (int i = 0; i < forwaFrame; i++) + { + if (i + 1 == forwaFrame)//最后一帧 + { + //写入操作前、将网络波动堆积,可能造成瞬时多个连续推帧结果(最后一帧除外)立即广播,不等16msTick + //if (forwaFrame > 1) + // room.SynInputData(); + + //推帧过程中,最后一帧才写入操作 + room.SetPlayerInput(_c.UID, msg.FrameID, temp); + } + //推帧 + room.TakeFrame(); + } + } + else//不需要推帧 + { + //虽然不推帧,但是存入Input + room.SetPlayerInput(_c.UID, msg.FrameID, temp); + } + + if (room.LastTestRecv != room.mCurrInputData.all) + { + room.LastTestRecv = room.mCurrInputData.all; + //AppSrv.g_Log.Debug($" {DateTime.Now.ToString("hh:mm:ss.fff")} SynTestRecv=> UID->{_c.UID} roomId->{room.mCurrServerFrameId} input->{msg.InputData}"); + } + } + + public void OnCmdScreen(Socket sk, byte[] reqData) + { + AppSrv.g_Log.DebugCmd($"OnCmdScreen lenght:{reqData.Length}"); + ClientInfo _c = AppSrv.g_ClientMgr.GetClientForSocket(sk); + Protobuf_Screnn_Frame msg = ProtoBufHelper.DeSerizlize(reqData); + Data_RoomData room = AppSrv.g_Room.GetRoomData(msg.RoomID); + room.InputScreenData(msg.RawBitmap); + } + + /// + /// 广播房间状态变化 + /// + /// + public void Protobuf_Room_MyRoom_State_Change(int RoomID) + { + Data_RoomData room = GetRoomData(RoomID); + if (room == null) + return; + + Protobuf_Room_MyRoom_State_Change resp = new Protobuf_Room_MyRoom_State_Change() + { + RoomMiniInfo = GetProtoDataRoom(room) + }; + + List userlist = room.GetAllPlayerClientList(); + + foreach (ClientInfo _c in userlist) + { + AppSrv.g_ClientMgr.ClientSend(_c, (int)CommandID.CmdRoomMyRoomStateChanged, (int)ErrorCode.ErrorOk, ProtoBufHelper.Serizlize(resp)); + } + } + + /// + /// 广播联机Step + /// + /// + public void SendRoomStepChange(Data_RoomData room) + { + List roomClient = room.GetAllPlayerClientList(); + switch (room.GameState) + { + case RoomGameState.WaitRawUpdate: + { + Protobuf_Room_WaitStep_RESP resp = new Protobuf_Room_WaitStep_RESP() + { + WaitStep = 0 + }; + AppSrv.g_Log.DebugCmd($"Step:0 WaitRawUpdate 广播等待主机上报即时存档"); + AppSrv.g_ClientMgr.ClientSend(roomClient, (int)CommandID.CmdRoomWaitStep, (int)ErrorCode.ErrorOk, ProtoBufHelper.Serizlize(resp)); + } + break; + case RoomGameState.WaitReady: + { + Protobuf_Room_WaitStep_RESP resp = new Protobuf_Room_WaitStep_RESP() + { + WaitStep = 1, + LoadStateRaw = room.NextStateRaw + }; + AppSrv.g_Log.DebugCmd($"Step:1 WaitReady 广播即时存档"); + AppSrv.g_ClientMgr.ClientSend(roomClient, (int)CommandID.CmdRoomWaitStep, (int)ErrorCode.ErrorOk, ProtoBufHelper.Serizlize(resp)); + } + break; + case RoomGameState.InOnlineGame: + { + Protobuf_Room_WaitStep_RESP resp = new Protobuf_Room_WaitStep_RESP() + { + WaitStep = 2, + }; + AppSrv.g_Log.DebugCmd($"Step:2 InOnlineGame 广播开始游戏"); + AppSrv.g_ClientMgr.ClientSend(roomClient, (int)CommandID.CmdRoomWaitStep, (int)ErrorCode.ErrorOk, ProtoBufHelper.Serizlize(resp)); + } + break; + } + } + + #region 房间帧循环 + void UpdateLoopTick() + { + while (true) + { + roomTickARE.WaitOne(); + UpdateAllRoomLogic(); + } + } + void UpdateAllRoomLogic() + { + if (mKeyRoomList.Count < 1) + return; + for (int i = 0; i < mKeyRoomList.Count; i++) + { + int roomid = mKeyRoomList[i]; + if (!mDictRoom.TryGetValue(roomid, out Data_RoomData room) || room.GameState < RoomGameState.InOnlineGame) + continue; + //更新帧 + //room.TakeFrame(); + //广播 + room.SynInputData(); + } + } + #endregion + } + + public class Data_RoomData + { + public int RoomID { get; private set; } + public int GameRomID { get; private set; } + public string RomHash { get; private set; } + public long HostUID { get; private set; } + public long ScreenProviderUID { get; private set; } + public Data_RoomSlot[] PlayerSlot; + public long Player1_UID => PlayerSlot[0].UID; + public long Player2_UID => PlayerSlot[1].UID; + public long Player3_UID => PlayerSlot[2].UID; + public long Player4_UID => PlayerSlot[3].UID; + public Google.Protobuf.ByteString? NextStateRaw { get; private set; } + public Google.Protobuf.ByteString? ScreenRaw { get; private set; } + //public bool[] PlayerReadyState { get; private set; } + public List SynUIDs; + //public RoomPlayerState PlayerState => getPlayerState(); + private RoomGameState mGameState; + public uint mCurrServerFrameId = 0; + public ServerInputSnapShot mCurrInputData; + public Queue<(uint, ServerInputSnapShot)> mInputQueue; + object synInputLock = new object(); + //TODO + public Dictionary> mDictPlayerIdx2SendQueue; + public RoomGameState GameState + { + get { return mGameState; } + set + { + if (mGameState != value) + { + mGameState = value; + switch (value) + { + case RoomGameState.WaitRawUpdate: + NextStateRaw = null; + break; + case RoomGameState.WaitReady: + ClearAllSlotReadyState();//清理玩家所有准备状态 + break; + } + } + } + } + /// + /// 服务器提前帧数 + /// + public uint SrvForwardFrames { get; set; } + public void Init(int roomID, int gameRomID, string roomHash, long hostUId, bool bloadState = false) + { + RoomID = roomID; + GameRomID = gameRomID; + RomHash = roomHash; + HostUID = hostUId; + ScreenProviderUID = hostUId; + + if (PlayerSlot == null) + PlayerSlot = new Data_RoomSlot[4]; + + for (uint i = 0; i < PlayerSlot.Length; i++) + PlayerSlot[i].Init(i); + + //PlayerReadyState = new bool[4]; + SynUIDs = new List();//广播角色列表 + GameState = RoomGameState.NoneGameState; + mCurrInputData = new ServerInputSnapShot(); + mInputQueue = new Queue<(uint, ServerInputSnapShot)>(); + mDictPlayerIdx2SendQueue = new Dictionary>(); + } + public Dictionary GetSlotDataByUID(long uid) + { + Dictionary slotIdx2JoyIdx = new Dictionary(); + var dataarr = PlayerSlot.Where(w => w.UID == uid).ToArray(); + foreach (var slot in dataarr) + slotIdx2JoyIdx[slot.SlotIdx] = slot.LocalJoyIdx; + return slotIdx2JoyIdx; + } + /// + /// 按照SlotIdx设置Input + /// + /// + void SetInputDataBySlotIdx(uint slotIdx, byte val) + { + switch (slotIdx) + { + case 0: mCurrInputData.p1_byte = val; break; + case 1: mCurrInputData.p2_byte = val; break; + case 2: mCurrInputData.p3_byte = val; break; + case 4: mCurrInputData.p3_byte = val; break; + } + } + /// + /// 按照UID清理SlotData + /// + /// + void ClearSlotDataByUid(long uid) + { + var dataarr = PlayerSlot.Where(w => w.UID == uid).ToArray(); + foreach (var slot in dataarr) + { + dataarr[slot.SlotIdx].Init(slot.SlotIdx); + ClearInputDataBySlotIdx(slot.SlotIdx); + } + } + /// + /// 按照SlotIdx清理SlotData + /// + /// + void ClearSlotDataBySlotIdx(uint slotIdx) + { + PlayerSlot[slotIdx].Init(slotIdx); + ClearInputDataBySlotIdx(slotIdx); + } + /// + /// 按照SlotIdx清理Input + /// + /// + void ClearInputDataBySlotIdx(uint slotIdx) + { + switch (slotIdx) + { + case 0: mCurrInputData.p1_byte = 0; break; + case 1: mCurrInputData.p2_byte = 0; break; + case 2: mCurrInputData.p3_byte = 0; break; + case 4: mCurrInputData.p3_byte = 0; break; + } + } + /// + /// 更新同步名单 + /// + void UpdateSynUIDs() + { + for (int i = SynUIDs.Count - 1; i >= 0; i--) + { + long uid = SynUIDs[i]; + + bool bHad = false; + if (Player1_UID == uid) bHad = true; + else if (Player2_UID == uid) bHad = true; + else if (Player3_UID == uid) bHad = true; + else if (Player4_UID == uid) bHad = true; + if (bHad) + SynUIDs.RemoveAt(i); + } + if (!SynUIDs.Contains(Player1_UID)) SynUIDs.Add(Player1_UID); + if (!SynUIDs.Contains(Player2_UID)) SynUIDs.Add(Player2_UID); + if (!SynUIDs.Contains(Player3_UID)) SynUIDs.Add(Player3_UID); + if (!SynUIDs.Contains(Player4_UID)) SynUIDs.Add(Player4_UID); + } + + #region 准备状态管理 + bool IsAllReady() + { + bool Ready = true; + if ( + (Player1_UID > 0 && !PlayerSlot[0].Ready) + || + (Player2_UID > 0 && !PlayerSlot[1].Ready) + || + (Player3_UID > 0 && !PlayerSlot[2].Ready) + || + (Player4_UID > 0 && !PlayerSlot[3].Ready) + ) + { + Ready = false; + } + return Ready; + } + /// + /// 清除所有槽位准备状态 + /// + void ClearAllSlotReadyState() + { + for (var i = 0; i < PlayerSlot.Length; i++) + { + PlayerSlot[i].Ready = false; + } + } + /// + /// 按照UID设置Ready信息 + /// + /// + void SetReadyByUid(long uid) + { + for (var i = 0; i < PlayerSlot.Length; i++) + { + if (PlayerSlot[i].UID == uid) + PlayerSlot[i].Ready = true; + } + } + #endregion + + public void SetPlayerSlotData(ClientInfo _c, ref readonly Dictionary newSlotIdx2JoyIdx) + { + Dictionary oldSlotIdx2JoyIdx = GetSlotDataByUID(_c.UID); + HashSet diffSlotIdxs = new HashSet(); + foreach (var old in oldSlotIdx2JoyIdx) + { + uint old_slotIdx = old.Key; + //如果旧位置已经不存在于新位置,则需要算作diff + if (!newSlotIdx2JoyIdx.ContainsKey(old_slotIdx)) + { + diffSlotIdxs.Add(old_slotIdx); continue; + } + uint old_slotjoyIdx = old.Value; + //如果旧位置不变,但客户端本地JoyIdx变化则算作diff + if (old_slotjoyIdx != newSlotIdx2JoyIdx[old_slotIdx]) + { + diffSlotIdxs.Add(old_slotIdx); continue; + } + } + //如果是在旧数据中不存在的位置,则算作diff + foreach (var newdata in newSlotIdx2JoyIdx) + { + uint new_slotIdx = newdata.Key; + if (!oldSlotIdx2JoyIdx.ContainsKey(new_slotIdx)) + { + diffSlotIdxs.Add(new_slotIdx); continue; + } + } + //必要的diff slot 清理键值数据 + foreach (var diffSlotIdx in diffSlotIdxs) + { + ClearSlotDataBySlotIdx(diffSlotIdx); + } + //设置新的槽位 + foreach (var slotdata in newSlotIdx2JoyIdx) + { + PlayerSlot[slotdata.Key].LocalJoyIdx = slotdata.Value; + AppSrv.g_Log.Debug($"SetPlayerUID RoomID->{RoomID} _c.UID->{_c.UID} PlayerSlotIdx->{slotdata.Key} LocalJoyIdx->{slotdata.Value}"); + } + //更新需要同步的UID + UpdateSynUIDs(); + _c.RoomState.SetRoomData(this.RoomID); + } + public void RemovePlayer(ClientInfo _c) + { + ClearSlotDataByUid(_c.UID); + UpdateSynUIDs(); + _c.RoomState.ClearRoomData(); + } + public bool GetPlayerUIDByIdx(uint Idx, out long UID) + { + switch (Idx) + { + case 0: UID = Player1_UID; break; + case 1: UID = Player2_UID; break; + case 2: UID = Player3_UID; break; + case 3: UID = Player4_UID; break; + default: UID = -1; break; + } + return UID > 0; + } + public bool GetPlayerClientByIdx(uint Idx, out ClientInfo _c) + { + _c = null; + if (!GetPlayerUIDByIdx(Idx, out long UID)) + return false; + + if (!AppSrv.g_ClientMgr.GetClientByUID(UID, out _c)) + return false; + + return true; + } + public List GetAllPlayerClientList() + { + List list = new List(); + List Uids = SynUIDs; + foreach (long uid in Uids) + { + if (!AppSrv.g_ClientMgr.GetClientByUID(uid, out ClientInfo _c, true)) + continue; + list.Add(_c); + } + return list; + } + + void SetInputBySlotIdxJoyIdx(uint SlotIdx, uint LocalJoyIdx, ServerInputSnapShot clieninput) + { + switch (LocalJoyIdx) + { + case 0: SetInputDataBySlotIdx(SlotIdx, clieninput.p1_byte); break; + case 1: SetInputDataBySlotIdx(SlotIdx, clieninput.p2_byte); break; + case 2: SetInputDataBySlotIdx(SlotIdx, clieninput.p3_byte); break; + case 3: SetInputDataBySlotIdx(SlotIdx, clieninput.p4_byte); break; + } + } + public int GetPlayerCount() + { + return SynUIDs.Count; + } + public void UpdateRoomForwardNum() + { + List playerlist = GetAllPlayerClientList(); + double maxNetDelay = 0; + for (int i = 0; i < playerlist.Count; i++) + { + ClientInfo player = playerlist[i]; + maxNetDelay = Math.Max(maxNetDelay, player.AveNetDelay); + } + float MustTaskFrame = 1; + SrvForwardFrames = (uint)((maxNetDelay / 0.016f) + MustTaskFrame); + if (SrvForwardFrames < 2) + SrvForwardFrames = 2; + //AppSrv.g_Log.Debug($"服务器提前跑帧数:Max(2,({maxNetDelay} / {0.016f}) + {MustTaskFrame}) = {SrvForwardFrames}"); + } + + #region 帧相关 + void StartNewTick() + { + mInputQueue.Clear(); + mDictPlayerIdx2SendQueue.Clear(); + + mCurrServerFrameId = 0; + mCurrInputData.all = 1; + + UpdateRoomForwardNum(); + + uint StartForwardFrames = (SrvForwardFrames * 2) + 5; + StartForwardFrames = Math.Min(10, StartForwardFrames); + //服务器提前跑帧数 + for (int i = 0; i < SrvForwardFrames; i++) + TakeFrame(); + AppSrv.g_Log.Info($"房间初始提前量=>{StartForwardFrames},当前延迟提前量=>{SrvForwardFrames}"); + } + public void TakeFrame() + { + lock (synInputLock) + { + mInputQueue.Enqueue((mCurrServerFrameId, mCurrInputData)); + mCurrServerFrameId++; + if (mCurrServerFrameId % 60 == 0) + { + UpdateRoomForwardNum(); + } + } + } + #endregion + + ulong LastTestSend = 0; + internal ulong LastTestRecv; + + public List send2time = new List(); + const int SynLimitOnSec = 63; + + /// + /// 广播数据 + /// + public void SynInputData() + { + List<(uint frameId, ServerInputSnapShot inputdata)> temp = null; + bool flagInitList = false; + lock (synInputLock) + { + double timeNow = AppSrv.g_Tick.timeNow; + while (mInputQueue.Count > 0) + { + if (send2time.Count >= SynLimitOnSec) + { + //AppSrv.g_Log.Info($"{timeNow} - {send2time[0]} =>{timeNow - send2time[0]}"); + if (timeNow - send2time[0] < 1f) //最早的历史发送还在一秒之内 + break; + else + send2time.RemoveAt(0); + } + + if (!flagInitList) + { + flagInitList = true; + temp = new List<(uint frameId, ServerInputSnapShot inputdata)>(); + } + temp.Add(mInputQueue.Dequeue()); + send2time.Add(timeNow); + } + //while (mInputQueue.Count > 0) + //{ + // temp.Add(mInputQueue.Dequeue()); + //} + } + + if (!flagInitList) + return; + + for (int i = 0; i < temp.Count; i++) + { + (uint frameId, ServerInputSnapShot inputdata) data = temp[i]; + + Protobuf_Room_Syn_RoomFrameAllInputData resp = new Protobuf_Room_Syn_RoomFrameAllInputData() + { + FrameID = data.frameId, + InputData = data.inputdata.all, + ServerFrameID = mCurrServerFrameId, + ServerForwardCount = this.SrvForwardFrames + }; + AppSrv.g_ClientMgr.ClientSend(SynUIDs, (int)CommandID.CmdRoomSynPlayerInput, (int)ErrorCode.ErrorOk, ProtoBufHelper.Serizlize(resp)); + //if (LastTestSend != data.inputdata.all) + //{ + // LastTestSend = data.inputdata.all; + // AppSrv.g_Log.Debug($" {DateTime.Now.ToString("hh:mm:ss.fff")} SynInput=> RoomID->{RoomID} ServerFrameID->{mCurrServerFrameId} SynUIDs=>{string.Join(",", SynUIDs)} "); + //} + } + } + + bool CheckRoomStateChange(int oldPlayerCount, int newPlayerCount) + { + bool bChanged = false; + bool bNewToOnlyHost = (oldPlayerCount != 1 && newPlayerCount == 1); + bool bMorePlayer = (oldPlayerCount < 2 && newPlayerCount >= 2) || (newPlayerCount > oldPlayerCount); + switch (this.GameState) + { + case RoomGameState.NoneGameState: + if (bNewToOnlyHost) + { + this.GameState = RoomGameState.OnlyHost; + bChanged = true; + } + break; + case RoomGameState.OnlyHost: + if (bMorePlayer)//加入更多玩家 + { + this.GameState = RoomGameState.WaitRawUpdate; + bChanged = true; + break; + } + break; + case RoomGameState.WaitRawUpdate: + if (bMorePlayer)//加入更多玩家 + { + this.GameState = RoomGameState.WaitRawUpdate; + bChanged = true; + break; + } + if (NextStateRaw != null)//已经上传即时存档 + { + this.GameState = RoomGameState.WaitReady; + bChanged = true; + break; + } + break; + case RoomGameState.WaitReady: + if (bMorePlayer)//加入更多玩家 + { + this.GameState = RoomGameState.WaitRawUpdate; + bChanged = true; + break; + } + //没有未准备的 + bool bAllReady = IsAllReady(); + if (bAllReady) + { + this.GameState = RoomGameState.InOnlineGame; + //新开Tick + StartNewTick(); + bChanged = true; + break; + } + break; + case RoomGameState.Pause: + if (bMorePlayer)//加入更多玩家 + { + this.GameState = RoomGameState.WaitRawUpdate; + bChanged = true; + break; + } + break; + case RoomGameState.InOnlineGame: + if (bMorePlayer)//加入更多玩家 + { + this.GameState = RoomGameState.WaitRawUpdate; + bChanged = true; + break; + } + break; + } + return bChanged; + } + + + #region 对外开放函数 + + #region 房间进出 + /// + /// 进入房间 + /// + /// + /// + /// + /// + /// + public bool Join(uint slotIdx, uint joyIdx, ClientInfo _c, out ErrorCode errcode, out bool bHadRoomStateChange) + { + bHadRoomStateChange = false; + int oldPlayerCount = GetPlayerCount(); + if (GetPlayerUIDByIdx(slotIdx, out long hadUID)) + { + errcode = ErrorCode.ErrorRoomSlotAlreadlyHadPlayer; + return false; + } + AppSrv.g_Log.Debug($"Join _c.UID->{_c.UID} RoomID->{RoomID}"); + Dictionary slotInfo = new Dictionary(); + slotInfo[slotIdx] = joyIdx; + SetPlayerSlotData(_c, ref slotInfo); + int newPlayerCount = GetPlayerCount(); + errcode = ErrorCode.ErrorOk; + bHadRoomStateChange = CheckRoomStateChange(oldPlayerCount, newPlayerCount); + return true; + } + + /// + /// 离开房间 + /// + /// + /// + /// + /// + public bool Leave(ClientInfo _c, out ErrorCode errcode, out bool bHadRoomStateChange) + { + int oldPlayerCount = GetPlayerCount(); + RemovePlayer(_c); + int newPlayerCount = GetPlayerCount(); + errcode = ErrorCode.ErrorOk; + bHadRoomStateChange = CheckRoomStateChange(oldPlayerCount, newPlayerCount); + return true; + } + #endregion + public void SetPlayerInput(long UID, uint LocalJoyIdx, ServerInputSnapShot clieninput) + { + for (uint i = 0; i < PlayerSlot.Count(); i++) + { + Data_RoomSlot slotData = PlayerSlot[i]; + if (slotData.UID != UID) + continue; + SetInputBySlotIdxJoyIdx(slotData.SlotIdx, slotData.LocalJoyIdx, clieninput); + } + } + public bool SetRePlayerReady(long UID, out ErrorCode errcode, out bool bHadRoomStateChange) + { + int oldPlayerCount = GetPlayerCount(); + SetReadyByUid(UID); + int newPlayerCount = GetPlayerCount(); + errcode = ErrorCode.ErrorOk; + bHadRoomStateChange = CheckRoomStateChange(oldPlayerCount, newPlayerCount); + return true; + } + public void SetLoadRaw(Google.Protobuf.ByteString NextStateRaw, out bool bHadRoomStateChange) + { + int oldPlayerCount = GetPlayerCount(); + AppSrv.g_Log.Debug($"SetLoadRaw proto Lenght->{NextStateRaw.Length}"); + this.NextStateRaw = NextStateRaw; + int newPlayerCount = GetPlayerCount(); + bHadRoomStateChange = CheckRoomStateChange(oldPlayerCount, newPlayerCount); + } + public void InputScreenData(Google.Protobuf.ByteString screenRaw) + { + this.ScreenRaw = NextStateRaw; + } + public bool GetNeedForwardTick(uint clientFrame, out long forwaFrame) + { + forwaFrame = 0; + //目标帧,客户端+服务器提前量 + long targetFrame = clientFrame + SrvForwardFrames; + if (targetFrame > mCurrServerFrameId)//更靠前 + forwaFrame = targetFrame - mCurrServerFrameId; + return forwaFrame > 0; + } + #endregion + } + + public class Data_RoomSlot + { + public uint SlotIdx { get; set; } + public long UID { get; set; } + public uint LocalJoyIdx { get; set; } + public bool Ready = false; + public void Init(uint SlotIdx) + { + this.SlotIdx = SlotIdx; + UID = -1; + LocalJoyIdx = 0; + Ready = false; + } + } + + [StructLayout(LayoutKind.Explicit, Size = 8)] + public struct ServerInputSnapShot + { + [FieldOffset(0)] + public UInt64 all; + + [FieldOffset(0)] + public byte p1_byte; + [FieldOffset(1)] + public byte p2_byte; + [FieldOffset(2)] + public byte p3_byte; + [FieldOffset(3)] + public byte p4_byte; + + [FieldOffset(0)] + public ushort p1_ushort; + [FieldOffset(2)] + public ushort p2_ushort; + [FieldOffset(4)] + public ushort p3_ushort; + [FieldOffset(6)] + public ushort p4_ushort; + } } \ No newline at end of file diff --git a/AxibugEmuOnline.Server/Program.cs b/AxibugEmuOnline.Server/Program.cs index 2f97104..adbad23 100644 --- a/AxibugEmuOnline.Server/Program.cs +++ b/AxibugEmuOnline.Server/Program.cs @@ -38,15 +38,17 @@ namespace AxibugEmuOnline.Server AppSrv.g_Log.Info($"input p3:{room.mCurrInputData.p3_byte}"); AppSrv.g_Log.Info($"input p4:{room.mCurrInputData.p4_byte}"); AppSrv.g_Log.Info($"GetPlayerCount:{room.GetPlayerCount()}"); - for (int i = 0; i < 4; i++) + for (int i = 0; i < room.PlayerSlot.Length; i++) { AppSrv.g_Log.Info($" P{i}:"); - if (room.GetPlayerClientByIdx(i, out ClientInfo _c)) + + if (AppSrv.g_ClientMgr.GetClientByUID(room.PlayerSlot[i].UID, out ClientInfo _c)) { - AppSrv.g_Log.Info($" UID->{_c.UID}"); + AppSrv.g_Log.Info($" UID->{room.PlayerSlot[i].UID}"); AppSrv.g_Log.Info($" NickName->{_c.NickName}"); AppSrv.g_Log.Info($" AveNetDelay->{_c.AveNetDelay}"); - } + AppSrv.g_Log.Info($" LocalJoyIdx->{room.PlayerSlot[i].LocalJoyIdx}"); + } else { AppSrv.g_Log.Info($" None"); diff --git a/ProtobufCore/proto/protobuf_AxibugEmuOnline.proto b/ProtobufCore/proto/protobuf_AxibugEmuOnline.proto index d8f66c2..1220aad 100644 --- a/ProtobufCore/proto/protobuf_AxibugEmuOnline.proto +++ b/ProtobufCore/proto/protobuf_AxibugEmuOnline.proto @@ -33,6 +33,7 @@ enum CommandID CMD_Room_Join = 5105; //房间加入 对应 Protobuf_Room_Join | Protobuf_Room_Join_RESP //建议Join之前按照房间信息,提前下载并读取本地Rom CMD_Room_Leave = 5106; //房间离开 对应 Protobuf_Room_Leave | Protobuf_Room_Leave_RESP CMD_Room_MyRoom_State_Changed = 5110; //我所在的房间内状态发生变化 对应 Protobuf_Room_MyRoom_State_Change + CMD_Room_ChangePlayerWithJoy = 5130; //更换操作槽位 对应 Protobuf_Room_Change_PlaySlotWithJoy | Protobuf_Room_Change_PlaySlotWithJoy_RESP //准备和开始流程(5201 ~ 5204 ~ 5208) // @@ -257,15 +258,15 @@ message Protobuf_Room_MiniInfo int64 HostPlayerUID = 4;//主机玩家ID RoomGameState GameState = 5;//游戏状态 int32 ObsUserCount = 6;//观战用户数量 - int64 Player1_UID = 7;//玩家1 UID - string Player1_NickName = 8;//玩家1 昵称 - int64 Player2_UID = 9;//玩家2 UID - string Player2_NickName = 10;//玩家2 昵称 - int64 Player3_UID = 11;//玩家3 UID - string Player3_NickName = 12;//玩家3 昵称 - int64 Player4_UID = 13;//玩家4 UID - string Player4_NickName = 14;//玩家4 昵称 - int64 ScreenProviderUID = 15;//屏幕数据供应者 + int64 ScreenProviderUID = 7;//屏幕数据供应者 + repeated Protobuf_Room_GamePlaySlot GamePlaySlotList = 8;//游玩槽位信息P1~P4 固定4个 +} + +message Protobuf_Room_GamePlaySlot +{ + int64 Player_UID = 1;//玩家 UID + string Player_NickName = 2;//玩家 昵称 + int32 PlayerLocalJoyIdx = 3;//客户端JoyIdx } message Protobuf_Room_Update_RESP @@ -299,7 +300,8 @@ message Protobuf_Room_Create { int32 GameRomID = 1; string GameRomHash = 2; - int32 JoinPlayerIdx = 3;//P1~P4[0~3] 以几号位玩家创建房间 + int32 PlayerSlotIdx = 3;//创建时的P1~P4编号 + int32 PlayerLocalJoyIdx = 4;//创建时的客户端Joy编号 } message Protobuf_Room_Create_RESP @@ -310,7 +312,8 @@ message Protobuf_Room_Create_RESP message Protobuf_Room_Join { int32 RoomID = 1;//房间ID - int32 PlayerNum = 2;//玩家编号 [0]1号玩家 [1]2号玩家 + int32 PlayerSlotIdx = 2;//P1~P4编号 + int32 PlayerLocalJoyIdx = 3;//客户端Joy编号 } message Protobuf_Room_Join_RESP @@ -333,6 +336,21 @@ message Protobuf_Room_MyRoom_State_Change Protobuf_Room_MiniInfo RoomMiniInfo = 1;//更新房间信息 } +message Protobuf_Room_Change_PlaySlotWithJoy +{ + repeated Protobuf_PlaySlotIdxWithJoyIdx SlotWithJoy = 1;//P1~P4编号 +} + +message Protobuf_PlaySlotIdxWithJoyIdx +{ + int32 PlayerSlotIdx = 1;//P1~P4编号 + int32 PlayerLocalJoyIdx = 2;//客户端Joy编号 +} + +message Protobuf_Room_Change_PlaySlotWithJoy_RESP +{ +} + message Protobuf_Room_WaitStep_RESP { int32 WaitStep = 1;//状态 [0]等待主机上报即时存档 [1]要求客户端准备 [2]开始(收到本状态时,立即开始跑模拟器核心) @@ -352,6 +370,10 @@ message Protobuf_Room_HostPlayer_UpdateStateRaw_RESP message Protobuf_Room_Player_Ready { + float PushFrameNeedTimeUs = 1;//push帧所需平均时间(微秒) + float LoadStateNeedTimeUs = 2;//加载即时存档所需平均时间(微秒) + float VideoFrameShowNeedTimeUs = 3;//视频一帧所需时间(微秒) + float AudioFramePlayNeedTimeUs = 4;//音频处理一帧所需时间(微秒) } message Protobuf_Room_Get_Screen