diff --git a/AxibugEmuOnline.Client/Assets/Plugins/AxiReplay/NetReplay.cs b/AxibugEmuOnline.Client/Assets/Plugins/AxiReplay/NetReplay.cs index e5c98b0..777a284 100644 --- a/AxibugEmuOnline.Client/Assets/Plugins/AxiReplay/NetReplay.cs +++ b/AxibugEmuOnline.Client/Assets/Plugins/AxiReplay/NetReplay.cs @@ -5,7 +5,7 @@ namespace AxiReplay public class NetReplay { int MaxInFrame = 0; - int mCurrPlayFrame = -1; + public int mCurrPlayFrame = -1; Queue mQueueReplay; ReplayStep mNextReplay; ReplayStep mCurrReplay; diff --git a/AxibugEmuOnline.Client/Assets/Script/Event/EventSystem.cs b/AxibugEmuOnline.Client/Assets/Script/Event/EventSystem.cs index c742461..da07fda 100644 --- a/AxibugEmuOnline.Client/Assets/Script/Event/EventSystem.cs +++ b/AxibugEmuOnline.Client/Assets/Script/Event/EventSystem.cs @@ -29,14 +29,14 @@ namespace AxibugEmuOnline.Client.Event } } - public class EventSystem + public class Eventer { - private static EventSystem instance = new EventSystem(); - public static EventSystem Instance { get { return instance; } } + private static Eventer instance = new Eventer(); + public static Eventer Instance { get { return instance; } } private Dictionary> eventDic = new Dictionary>(128); - private EventSystem() { } + private Eventer() { } #region RegisterEvent diff --git a/AxibugEmuOnline.Client/Assets/Script/Manager/AppChat.cs b/AxibugEmuOnline.Client/Assets/Script/Manager/AppChat.cs index 4ab2e28..c3b2634 100644 --- a/AxibugEmuOnline.Client/Assets/Script/Manager/AppChat.cs +++ b/AxibugEmuOnline.Client/Assets/Script/Manager/AppChat.cs @@ -26,7 +26,7 @@ namespace AxibugEmuOnline.Client.Manager public void RecvChatMsg(byte[] reqData) { Protobuf_ChatMsg_RESP msg = ProtoBufHelper.DeSerizlize(reqData); - EventSystem.Instance.PostEvent(EEvent.OnChatMsg, msg.NickName, msg.ChatMsg); + Eventer.Instance.PostEvent(EEvent.OnChatMsg, msg.NickName, msg.ChatMsg); } } } diff --git a/AxibugEmuOnline.Client/Assets/Script/Manager/AppRoom.cs b/AxibugEmuOnline.Client/Assets/Script/Manager/AppRoom.cs index e737cb6..6146e66 100644 --- a/AxibugEmuOnline.Client/Assets/Script/Manager/AppRoom.cs +++ b/AxibugEmuOnline.Client/Assets/Script/Manager/AppRoom.cs @@ -5,9 +5,9 @@ using AxibugEmuOnline.Client.Network; using AxibugProtobuf; using AxiReplay; using Google.Protobuf; +using System; using System.Collections.Generic; using System.Linq; -using UnityEngine; namespace AxibugEmuOnline.Client.Manager { @@ -182,7 +182,7 @@ namespace AxibugEmuOnline.Client.Manager Protobuf_Room_List_RESP msg = ProtoBufHelper.DeSerizlize(reqData); for (int i = 0; i < msg.RoomMiniInfoList.Count; i++) AddOrUpdateRoomList(msg.RoomMiniInfoList[i]); - EventSystem.Instance.PostEvent(EEvent.OnRoomListAllUpdate); + Eventer.Instance.PostEvent(EEvent.OnRoomListAllUpdate); } /// @@ -194,7 +194,7 @@ namespace AxibugEmuOnline.Client.Manager App.log.Debug("单个房间状态更新"); Protobuf_Room_Update_RESP msg = ProtoBufHelper.DeSerizlize(reqData); AddOrUpdateRoomList(msg.RoomMiniInfo); - EventSystem.Instance.PostEvent(EEvent.OnRoomListSingleUpdate, msg.RoomMiniInfo.GameRomID); + Eventer.Instance.PostEvent(EEvent.OnRoomListSingleUpdate, msg.RoomMiniInfo.GameRomID); } /// @@ -247,7 +247,7 @@ namespace AxibugEmuOnline.Client.Manager Protobuf_Room_Join_RESP msg = ProtoBufHelper.DeSerizlize(reqData); mineRoomMiniInfo = msg.RoomMiniInfo; InitRePlay(); - EventSystem.Instance.PostEvent(EEvent.OnMineJoinRoom); + Eventer.Instance.PostEvent(EEvent.OnMineJoinRoom); } /// @@ -273,7 +273,7 @@ namespace AxibugEmuOnline.Client.Manager Protobuf_Room_Leave_RESP msg = ProtoBufHelper.DeSerizlize(reqData); ReleaseRePlay(); mineRoomMiniInfo = null; - EventSystem.Instance.PostEvent(EEvent.OnMineLeavnRoom); + Eventer.Instance.PostEvent(EEvent.OnMineLeavnRoom); } void RecvRoomMyRoomStateChange(byte[] reqData) @@ -291,12 +291,12 @@ namespace AxibugEmuOnline.Client.Manager //位置之前有人,但是离开了 if (OldPlayer > 0) { - EventSystem.Instance.PostEvent(EEvent.OnOtherPlayerLeavnRoom, i, OldPlayer); + Eventer.Instance.PostEvent(EEvent.OnOtherPlayerLeavnRoom, i, OldPlayer); if (NewPlayer > 0)//而且害换了一个玩家 - EventSystem.Instance.PostEvent(EEvent.OnOtherPlayerJoinRoom, i, NewPlayer); + Eventer.Instance.PostEvent(EEvent.OnOtherPlayerJoinRoom, i, NewPlayer); } else //之前没人 - EventSystem.Instance.PostEvent(EEvent.OnOtherPlayerJoinRoom, i, NewPlayer); + Eventer.Instance.PostEvent(EEvent.OnOtherPlayerJoinRoom, i, NewPlayer); } } @@ -322,7 +322,7 @@ namespace AxibugEmuOnline.Client.Manager if (WaitStep != msg.WaitStep) { WaitStep = msg.WaitStep; - EventSystem.Instance.PostEvent(EEvent.OnRoomWaitStepChange, WaitStep); + Eventer.Instance.PostEvent(EEvent.OnRoomWaitStepChange, WaitStep); if (WaitStep == 1) { byte[] decompressRawData = Helper.DecompressByteArray(msg.LoadStateRaw.ToByteArray()); @@ -378,6 +378,11 @@ namespace AxibugEmuOnline.Client.Manager Protobuf_Screnn_Frame msg = ProtoBufHelper.DeSerizlize(reqData); //解压 byte[] data = Helper.DecompressByteArray(msg.RawBitmap.ToArray()); - } + } + + internal void SendHostRaw(byte[] stateRaw) + { + throw new NotImplementedException(); + } } } \ No newline at end of file diff --git a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/CoreSupporter.cs b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/CoreSupporter.cs index 959e513..e37caf3 100644 --- a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/CoreSupporter.cs +++ b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/CoreSupporter.cs @@ -1,6 +1,8 @@ using AxibugEmuOnline.Client.ClientCore; +using AxiReplay; using System; using System.IO; +using System.Runtime.InteropServices; using UnityEngine; using VirtualNes.Core; @@ -90,10 +92,79 @@ namespace AxibugEmuOnline.Client return db.GetMapperNo(rom.GetPROM_CRC(), out mapperNo); } + private ControllerState m_sampledState; public ControllerState GetControllerState() { - var mapper = NesControllerMapper.Get(); - return mapper.CreateState(); + if (!InGameUI.Instance.IsOnline) + { + return m_sampledState; + } + else + { + //todo : ӷȡ֡ + return default; + } + } + + public void SampleInput() + { + if (InGameUI.Instance.IsOnline) + { + if (App.roomMgr.netReplay.NextFrame(out var replayData, out int _)) + { + m_sampledState = FromNet(replayData); + var localState = NesControllerMapper.Get().CreateState(); + var rawData = ToNet(localState); + App.roomMgr.SendRoomSingelPlayerInput((uint)App.roomMgr.netReplay.mCurrPlayFrame, rawData); + } + else + { + m_sampledState = default; + } + } + else + { + m_sampledState = NesControllerMapper.Get().CreateState(); + } + } + + public ControllerState FromNet(AxiReplay.ReplayStep step) + { + var temp = new ServerInputSnapShot(); + var result = new ControllerState(); + temp.all = step.InPut; + result.raw0 = temp.p1; + result.raw1 = temp.p2; + result.raw2 = temp.p3; + result.raw3 = temp.p4; + result.valid = true; + + return result; + } + + public uint ToNet(ControllerState state) + { + var temp = new ServerInputSnapShot(); + temp.p1 = (byte)state.raw0; + temp.p2 = (byte)state.raw1; + temp.p3 = (byte)state.raw2; + temp.p4 = (byte)state.raw3; + return (uint)temp.all; + } + + [StructLayout(LayoutKind.Explicit, Size = 8)] + struct ServerInputSnapShot + { + [FieldOffset(0)] + public UInt64 all; + [FieldOffset(0)] + public byte p1; + [FieldOffset(1)] + public byte p2; + [FieldOffset(2)] + public byte p3; + [FieldOffset(3)] + public byte p4; } } } diff --git a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesControllerMapper.cs b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesControllerMapper.cs index 867ae99..e6adeee 100644 --- a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesControllerMapper.cs +++ b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesControllerMapper.cs @@ -87,6 +87,8 @@ namespace AxibugEmuOnline.Client public Mapper START = new Mapper(EnumButtonType.START); public Mapper MIC = new Mapper(EnumButtonType.MIC); + + public EnumButtonType GetButtons() { EnumButtonType res = 0; diff --git a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesEmulator.cs b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesEmulator.cs index 67371d2..5d458ab 100644 --- a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesEmulator.cs +++ b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesEmulator.cs @@ -8,14 +8,13 @@ using VirtualNes.Core.Debug; namespace AxibugEmuOnline.Client { - public class NesEmulator : MonoBehaviour - { + { public NES NesCore { get; private set; } public VideoProvider VideoProvider; public AudioProvider AudioProvider; - + public bool m_bPause; private void Start() { @@ -50,9 +49,16 @@ namespace AxibugEmuOnline.Client private void Update() { + if (m_bPause) return; + if (NesCore != null) { + Supporter.SampleInput(); var controlState = Supporter.GetControllerState(); + + //δյInput,֡ƽ + if (!controlState.valid) return; + NesCore.pad.Sync(controlState); NesCore.EmulateFrame(true); @@ -62,6 +68,17 @@ namespace AxibugEmuOnline.Client } } + + public void Pause() + { + m_bPause = true; + } + + public void Resume() + { + m_bPause = false; + } + #if UNITY_EDITOR [ContextMenu("ImportNesDB")] public void ImportNesDB() diff --git a/AxibugEmuOnline.Client/Assets/Script/UI/InGameUI/InGameUI.cs b/AxibugEmuOnline.Client/Assets/Script/UI/InGameUI/InGameUI.cs index 727efe9..5cd0b30 100644 --- a/AxibugEmuOnline.Client/Assets/Script/UI/InGameUI/InGameUI.cs +++ b/AxibugEmuOnline.Client/Assets/Script/UI/InGameUI/InGameUI.cs @@ -1,7 +1,9 @@ using AxibugEmuOnline.Client.ClientCore; +using AxibugEmuOnline.Client.Event; using AxibugEmuOnline.Client.Manager; using System; using System.Collections.Generic; +using VirtualNes.Core; namespace AxibugEmuOnline.Client { @@ -13,22 +15,26 @@ namespace AxibugEmuOnline.Client public override bool Enable => gameObject.activeInHierarchy; /// ָʾϷʵǷģʽ - public bool IsOnline => App.roomMgr.RoomState <= AxibugProtobuf.RoomGameState.OnlyHost; + public bool IsOnline => App.roomMgr.RoomState > AxibugProtobuf.RoomGameState.OnlyHost; private RomFile m_rom; private object m_core; private object m_state; private List menus = new List(); - + private StepPerformer m_stepPerformer; protected override void Awake() { Instance = this; gameObject.SetActiveEx(false); + + m_stepPerformer = new StepPerformer(this); + menus.Add(new InGameUI_SaveState(this)); menus.Add(new InGameUI_LoadState(this)); menus.Add(new InGameUI_QuitGame(this)); + base.Awake(); } @@ -73,15 +79,23 @@ namespace AxibugEmuOnline.Client m_rom = currentRom; m_core = core; + m_stepPerformer.Reset(); if (App.user.IsLoggedIn) { App.roomMgr.SendCreateRoom(m_rom.ID, 0, m_rom.Hash); } + Eventer.Instance.RegisterEvent(EEvent.OnRoomWaitStepChange, OnServerStepUpdate); + gameObject.SetActiveEx(true); } + private void OnServerStepUpdate(int step) + { + m_stepPerformer.Perform(step); + } + public void Hide() { CommandDispatcher.Instance.UnRegistController(this); @@ -96,6 +110,7 @@ namespace AxibugEmuOnline.Client public void QuitGame() { + Eventer.Instance.UnregisterEvent(EEvent.OnRoomWaitStepChange, OnServerStepUpdate); App.roomMgr.SendLeavnRoom(); App.emu.StopGame(); } diff --git a/AxibugEmuOnline.Client/Assets/Script/UI/InGameUI/StepPerformer.cs b/AxibugEmuOnline.Client/Assets/Script/UI/InGameUI/StepPerformer.cs new file mode 100644 index 0000000..90a595f --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/UI/InGameUI/StepPerformer.cs @@ -0,0 +1,75 @@ +using AxibugEmuOnline.Client.ClientCore; +using System; +using VirtualNes.Core; + +namespace AxibugEmuOnline.Client +{ + public class StepPerformer + { + private InGameUI m_inGameUI; + private int m_step = -1; + + public StepPerformer(InGameUI inGameUI) + { + m_inGameUI = inGameUI; + } + + public void Perform(int step) + { + m_step = step; + + + switch (m_step) + { + //等待主机上报快照 + case 0: + PauseCore(); + if (App.roomMgr.IsHost) + { + if (m_inGameUI.RomFile.Platform == EnumPlatform.NES) + { + var stateRaw = m_inGameUI.GetCore().NesCore.GetState().ToBytes(); + App.roomMgr.SendHostRaw(stateRaw); + } + } + break; + //加载存档并发送Ready通知 + case 1: + PauseCore(); + var state = new State(); + state.FromByte(App.roomMgr.RawData); + if (m_inGameUI.RomFile.Platform == EnumPlatform.NES) + { + m_inGameUI.GetCore().NesCore.LoadState(state); + } + App.roomMgr.SendRoomPlayerReady(); + break; + case 2: + m_step = -1; + ResumeCore(); + break; + } + } + + private void PauseCore() + { + if (m_inGameUI.RomFile.Platform == EnumPlatform.NES) + { + m_inGameUI.GetCore().Pause(); + } + } + + private void ResumeCore() + { + if (m_inGameUI.RomFile.Platform == EnumPlatform.NES) + { + m_inGameUI.GetCore().Resume(); + } + } + + internal void Reset() + { + m_step = -1; + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Script/UI/InGameUI/StepPerformer.cs.meta b/AxibugEmuOnline.Client/Assets/Script/UI/InGameUI/StepPerformer.cs.meta new file mode 100644 index 0000000..3ed241c --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/UI/InGameUI/StepPerformer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6e5033cf98f86804bb50f84e3bbc956a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Script/UI/OptionUI/OptionUI.cs b/AxibugEmuOnline.Client/Assets/Script/UI/OptionUI/OptionUI.cs index edb254f..05f4525 100644 --- a/AxibugEmuOnline.Client/Assets/Script/UI/OptionUI/OptionUI.cs +++ b/AxibugEmuOnline.Client/Assets/Script/UI/OptionUI/OptionUI.cs @@ -131,6 +131,7 @@ namespace AxibugEmuOnline.Client if (!m_bPoped) { m_bPoped = true; + DOTween.To( () => MenuRoot.anchoredPosition.x, (value) => @@ -142,6 +143,7 @@ namespace AxibugEmuOnline.Client 0, 0.3f ).SetEase(Ease.OutCubic); + } } @@ -149,10 +151,11 @@ namespace AxibugEmuOnline.Client { if (m_bPoped) { + m_runtimeMenuItems.Clear(); + SelectBorder.gameObject.SetActiveEx(false); CommandDispatcher.Instance.UnRegistController(this); - m_bPoped = false; Canvas.ForceUpdateCanvases(); var width = MenuRoot.rect.width; DOTween.To( @@ -166,6 +169,8 @@ namespace AxibugEmuOnline.Client width, 0.3f ).SetEase(Ease.OutCubic); + + m_bPoped = false; } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/ControllerState.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/ControllerState.cs index 418e35c..642b198 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/ControllerState.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/ControllerState.cs @@ -4,10 +4,12 @@ namespace VirtualNes.Core { public struct ControllerState { - private uint raw0; - private uint raw1; - private uint raw2; - private uint raw3; + public uint raw0; + public uint raw1; + public uint raw2; + public uint raw3; + + public bool valid; public ControllerState( EnumButtonType player0_buttons, @@ -19,6 +21,7 @@ namespace VirtualNes.Core raw1 = (uint)player1_buttons; raw2 = (uint)player2_buttons; raw3 = (uint)player3_buttons; + valid = true; } public bool HasButton(int player, EnumButtonType button) diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/Supporter.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/Supporter.cs index eb5a91f..3ecd65c 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/Supporter.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/Supporter.cs @@ -59,6 +59,11 @@ namespace VirtualNes.Core return s_support.GetControllerState(); } + public static void SampleInput() + { + s_support.SampleInput(); + } + public static EmulatorConfig Config => s_support.Config; } @@ -76,5 +81,6 @@ namespace VirtualNes.Core Stream OpenFile(string directPath, string fileName); bool TryGetMapperNo(ROM rom, out int mapperNo); ControllerState GetControllerState(); + void SampleInput(); } } diff --git a/ProtobufCore/proto/protobuf_AxibugEmuOnline.proto b/ProtobufCore/proto/protobuf_AxibugEmuOnline.proto index 070dec0..4eb8666 100644 --- a/ProtobufCore/proto/protobuf_AxibugEmuOnline.proto +++ b/ProtobufCore/proto/protobuf_AxibugEmuOnline.proto @@ -34,7 +34,7 @@ enum CommandID // 主机玩家 上行 CMD_Room_HostPlayer_UpdateStateRaw消息,上传即时存档 // 主机玩家上传完毕之后,服务器会通知进入Step1 // - // Step1:服务器广播"等待-全员加载即时存档" CMD_Room_WaitStep WaitStep=[1] 附带即时存档 ---> 客户端:全员等待(主机玩家一人上传) + // Step1:服务器广播"等待-全员加载即时存档" CMD_Room_WaitStep WaitStep=[1] 附带即时存档 ---> 客户端:全员等待 // 所有玩家确保加载ROM和即时存档,并保持模拟器暂停,准备完毕后 发送 CMD_Room_Player_Ready // 所有玩家Ready之后,服务器会根据所有玩家延迟提前跑若干Frame,通知进入Step2 //