forked from sin365/AxibugEmuOnline
存档支持云同步
This commit is contained in:
parent
13cddd8a3b
commit
eb2f5d268c
AxibugEmuOnline.Client/Assets/Script/AppMain
@ -1,6 +1,7 @@
|
||||
using AxibugEmuOnline.Client.ClientCore;
|
||||
using AxibugEmuOnline.Client.Tools;
|
||||
using AxibugProtobuf;
|
||||
using MAME.Core;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
@ -8,7 +9,7 @@ using System.Runtime.InteropServices;
|
||||
namespace AxibugEmuOnline.Client
|
||||
{
|
||||
/// <summary> 存档文件抽象类 </summary>
|
||||
public class SaveFile
|
||||
public partial class SaveFile
|
||||
{
|
||||
public SavCloudApi CloudAPI => App.SavMgr.CloudApi;
|
||||
|
||||
@ -38,7 +39,24 @@ namespace AxibugEmuOnline.Client
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsBusy
|
||||
{
|
||||
get
|
||||
{
|
||||
if (FSM.CurrentState is DownloadingState) return true;
|
||||
else if (FSM.CurrentState is UploadingState) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public SimpleFSM<SaveFile>.State GetState()
|
||||
{
|
||||
return FSM.CurrentState;
|
||||
}
|
||||
|
||||
public event Action OnSavSuccessed;
|
||||
public event Action OnStateChanged;
|
||||
|
||||
/// <summary> 存档顺序号,用于判断云存档和本地存档的同步状态,是否存在冲突 </summary>
|
||||
public uint Sequecen { get; private set; }
|
||||
@ -55,11 +73,12 @@ namespace AxibugEmuOnline.Client
|
||||
EmuPlatform = platform;
|
||||
SlotIndex = slotIndex;
|
||||
FSM = new SimpleFSM<SaveFile>(this);
|
||||
FSM.AddState<UnkownState>();
|
||||
FSM.AddState<IdleState>();
|
||||
FSM.AddState<CheckingState>();
|
||||
FSM.AddState<DownloadingState>();
|
||||
FSM.AddState<UploadingState>();
|
||||
FSM.AddState<SyncedState>();
|
||||
FSM.OnStateChanged += FSM_OnStateChanged;
|
||||
|
||||
IsEmpty = !File.Exists(FilePath);
|
||||
|
||||
@ -82,7 +101,12 @@ namespace AxibugEmuOnline.Client
|
||||
streaming.Dispose();
|
||||
}
|
||||
|
||||
FSM.ChangeState<UnkownState>();
|
||||
FSM.ChangeState<IdleState>();
|
||||
}
|
||||
|
||||
private void FSM_OnStateChanged()
|
||||
{
|
||||
OnStateChanged?.Invoke();
|
||||
}
|
||||
|
||||
public void Update()
|
||||
@ -145,6 +169,8 @@ namespace AxibugEmuOnline.Client
|
||||
|
||||
public unsafe void Save(uint sequence, byte[] savData, byte[] screenShotData)
|
||||
{
|
||||
if (IsBusy) return;
|
||||
|
||||
var filePath = FilePath;
|
||||
|
||||
var header = new Header
|
||||
@ -190,9 +216,9 @@ namespace AxibugEmuOnline.Client
|
||||
/// </summary>
|
||||
public void TrySync()
|
||||
{
|
||||
if (FSM.CurrentState is not UnkownState && FSM.CurrentState is not SyncedState) return;
|
||||
if (FSM.CurrentState is not IdleState && FSM.CurrentState is not SyncedState) return;
|
||||
|
||||
FSM.ChangeState<CheckingState>();
|
||||
FSM.ChangeState<CheckingNetworkState>();
|
||||
}
|
||||
|
||||
|
||||
@ -210,14 +236,5 @@ namespace AxibugEmuOnline.Client
|
||||
[FieldOffset(20)]
|
||||
public uint ScreenShotLength;
|
||||
}
|
||||
|
||||
public enum EnumState
|
||||
{
|
||||
Unkown,
|
||||
Checking,
|
||||
Downloading,
|
||||
Uploading,
|
||||
Synced
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
using AxibugEmuOnline.Client.ClientCore;
|
||||
using AxibugEmuOnline.Client.Tools;
|
||||
using System;
|
||||
|
||||
namespace AxibugEmuOnline.Client
|
||||
{
|
||||
public partial class SaveFile
|
||||
{
|
||||
|
||||
public class CheckingNetworkState : SimpleFSM<SaveFile>.State
|
||||
{
|
||||
public override void OnUpdate()
|
||||
{
|
||||
if (App.network.isConnected)
|
||||
{
|
||||
FSM.ChangeState<CheckingState>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 288e73ce3cb773e46b7d37a53a3b43ee
|
@ -4,45 +4,48 @@ using UnityEngine;
|
||||
|
||||
namespace AxibugEmuOnline.Client
|
||||
{
|
||||
public class CheckingState : SimpleFSM<SaveFile>.State
|
||||
public partial class SaveFile
|
||||
{
|
||||
private float m_timeOut;
|
||||
|
||||
public Protobuf_Mine_GameSavInfo NetData { get; private set; }
|
||||
|
||||
public override void OnEnter(SimpleFSM<SaveFile>.State preState)
|
||||
public class CheckingState : SimpleFSM<SaveFile>.State
|
||||
{
|
||||
m_timeOut = 5f;
|
||||
Host.CloudAPI.OnFetchGameSavList += CloudAPI_OnFetchGameSavList;
|
||||
Host.CloudAPI.SendGetGameSavList(Host.RomID);
|
||||
}
|
||||
private float m_timeOut;
|
||||
|
||||
public override void OnExit(SimpleFSM<SaveFile>.State nextState)
|
||||
{
|
||||
Host.CloudAPI.OnFetchGameSavList -= CloudAPI_OnFetchGameSavList;
|
||||
}
|
||||
public Protobuf_Mine_GameSavInfo NetData { get; private set; }
|
||||
|
||||
public override void OnUpdate()
|
||||
{
|
||||
m_timeOut -= Time.deltaTime;
|
||||
if (m_timeOut < 0) //已超时
|
||||
public override void OnEnter(SimpleFSM<SaveFile>.State preState)
|
||||
{
|
||||
FSM.ChangeState<UnkownState>();
|
||||
m_timeOut = 5f;
|
||||
Host.CloudAPI.OnFetchGameSavList += CloudAPI_OnFetchGameSavList;
|
||||
Host.CloudAPI.SendGetGameSavList(Host.RomID);
|
||||
}
|
||||
}
|
||||
|
||||
private void CloudAPI_OnFetchGameSavList(int romID, Protobuf_Mine_GameSavInfo[] savSlotData)
|
||||
{
|
||||
if (romID != Host.RomID) return;
|
||||
NetData = savSlotData[Host.SlotIndex];
|
||||
|
||||
if (NetData == null) //云存档不存在,上传本地存档
|
||||
public override void OnExit(SimpleFSM<SaveFile>.State nextState)
|
||||
{
|
||||
FSM.ChangeState<UploadingState>();
|
||||
Host.CloudAPI.OnFetchGameSavList -= CloudAPI_OnFetchGameSavList;
|
||||
}
|
||||
else
|
||||
|
||||
public override void OnUpdate()
|
||||
{
|
||||
FSM.ChangeState<DownloadingState>();
|
||||
m_timeOut -= Time.deltaTime;
|
||||
if (m_timeOut < 0) //已超时
|
||||
{
|
||||
FSM.ChangeState<IdleState>();
|
||||
}
|
||||
}
|
||||
|
||||
private void CloudAPI_OnFetchGameSavList(int romID, Protobuf_Mine_GameSavInfo[] savSlotData)
|
||||
{
|
||||
if (romID != Host.RomID) return;
|
||||
NetData = savSlotData[Host.SlotIndex];
|
||||
|
||||
if (NetData == null) //云存档不存在,上传本地存档
|
||||
{
|
||||
FSM.ChangeState<UploadingState>();
|
||||
}
|
||||
else
|
||||
{
|
||||
FSM.ChangeState<DownloadingState>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,11 @@
|
||||
|
||||
namespace AxibugEmuOnline.Client
|
||||
{
|
||||
public class ConflictState : SimpleFSM<SaveFile>.State
|
||||
public partial class SaveFile
|
||||
{
|
||||
public class ConflictState : SimpleFSM<SaveFile>.State
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -3,51 +3,54 @@ using AxibugEmuOnline.Client.Tools;
|
||||
|
||||
namespace AxibugEmuOnline.Client
|
||||
{
|
||||
public class DownloadingState : SimpleFSM<SaveFile>.State
|
||||
public partial class SaveFile
|
||||
{
|
||||
uint m_sequece;
|
||||
private AxiHttpProxy.SendDownLoadProxy m_downloadTask;
|
||||
private AxiHttpProxy.SendDownLoadProxy m_downloadTaskImg;
|
||||
|
||||
public override void OnEnter(SimpleFSM<SaveFile>.State preState)
|
||||
public class DownloadingState : SimpleFSM<SaveFile>.State
|
||||
{
|
||||
var checkState = preState as CheckingState;
|
||||
uint m_sequece;
|
||||
private AxiHttpProxy.SendDownLoadProxy m_downloadTask;
|
||||
private AxiHttpProxy.SendDownLoadProxy m_downloadTaskImg;
|
||||
|
||||
var netData = checkState.NetData;
|
||||
|
||||
if (Host.Sequecen >= (uint)netData.Sequence)
|
||||
public override void OnEnter(SimpleFSM<SaveFile>.State preState)
|
||||
{
|
||||
FSM.ChangeState<ConflictState>();
|
||||
return;
|
||||
var checkState = preState as CheckingState;
|
||||
|
||||
var netData = checkState.NetData;
|
||||
|
||||
if (Host.Sequecen >= (uint)netData.Sequence)
|
||||
{
|
||||
FSM.ChangeState<ConflictState>();
|
||||
return;
|
||||
}
|
||||
|
||||
m_sequece = (uint)netData.Sequence;
|
||||
m_downloadTask = AxiHttpProxy.GetDownLoad($"{App.httpAPI.WebHost}/{netData.SavUrl}");
|
||||
m_downloadTaskImg = AxiHttpProxy.GetDownLoad($"{App.httpAPI.WebHost}/{netData.SavImgUrl}");
|
||||
}
|
||||
|
||||
m_sequece = (uint)netData.Sequence;
|
||||
m_downloadTask = AxiHttpProxy.GetDownLoad($"{App.httpAPI.WebHost}/{netData.SavUrl}");
|
||||
m_downloadTaskImg = AxiHttpProxy.GetDownLoad($"{App.httpAPI.WebHost}/{netData.SavImgUrl}");
|
||||
}
|
||||
|
||||
public override void OnUpdate()
|
||||
{
|
||||
if (!m_downloadTask.downloadHandler.isDone) return;
|
||||
|
||||
if (m_downloadTask.downloadHandler.bHadErr) //下载失败
|
||||
public override void OnUpdate()
|
||||
{
|
||||
FSM.ChangeState<UnkownState>();
|
||||
return;
|
||||
if (!m_downloadTask.downloadHandler.isDone) return;
|
||||
|
||||
if (m_downloadTask.downloadHandler.bHadErr) //下载失败
|
||||
{
|
||||
FSM.ChangeState<IdleState>();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_downloadTaskImg.downloadHandler.isDone) return;
|
||||
|
||||
if (m_downloadTaskImg.downloadHandler.bHadErr) //下载失败
|
||||
{
|
||||
FSM.ChangeState<IdleState>();
|
||||
return;
|
||||
}
|
||||
|
||||
var savData = Host.CloudAPI.UnGzipData(m_downloadTask.downloadHandler.data);
|
||||
var imgData = Host.CloudAPI.UnGzipData(m_downloadTaskImg.downloadHandler.data);
|
||||
Host.Save(m_sequece, savData, imgData);
|
||||
FSM.ChangeState<SyncedState>();
|
||||
}
|
||||
|
||||
if (!m_downloadTaskImg.downloadHandler.isDone) return;
|
||||
|
||||
if (m_downloadTaskImg.downloadHandler.bHadErr) //下载失败
|
||||
{
|
||||
FSM.ChangeState<UnkownState>();
|
||||
return;
|
||||
}
|
||||
|
||||
var savData = Host.CloudAPI.UnGzipData(m_downloadTask.downloadHandler.data);
|
||||
var imgData = Host.CloudAPI.UnGzipData(m_downloadTaskImg.downloadHandler.data);
|
||||
Host.Save(m_sequece, savData, imgData);
|
||||
FSM.ChangeState<SyncedState>();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
using AxibugEmuOnline.Client.Tools;
|
||||
|
||||
namespace AxibugEmuOnline.Client
|
||||
{
|
||||
public partial class SaveFile
|
||||
{
|
||||
public class IdleState : SimpleFSM<SaveFile>.State
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,10 @@
|
||||
|
||||
namespace AxibugEmuOnline.Client
|
||||
{
|
||||
public class SyncedState : SimpleFSM<SaveFile>.State
|
||||
public partial class SaveFile
|
||||
{
|
||||
public class SyncedState : SimpleFSM<SaveFile>.State
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
using AxibugEmuOnline.Client.Tools;
|
||||
|
||||
namespace AxibugEmuOnline.Client
|
||||
{
|
||||
public class UnkownState : SimpleFSM<SaveFile>.State
|
||||
{
|
||||
}
|
||||
}
|
@ -2,27 +2,30 @@
|
||||
|
||||
namespace AxibugEmuOnline.Client
|
||||
{
|
||||
public class UploadingState : SimpleFSM<SaveFile>.State
|
||||
public partial class SaveFile
|
||||
{
|
||||
public override void OnEnter(SimpleFSM<SaveFile>.State preState)
|
||||
public class UploadingState : SimpleFSM<SaveFile>.State
|
||||
{
|
||||
Host.CloudAPI.OnUploadedSavData += Api_OnUploadedSavData;
|
||||
public override void OnEnter(SimpleFSM<SaveFile>.State preState)
|
||||
{
|
||||
Host.CloudAPI.OnUploadedSavData += Api_OnUploadedSavData;
|
||||
|
||||
Host.GetSavData(out byte[] savData, out byte[] screenData);
|
||||
Host.CloudAPI.SendUpLoadGameSav(Host.RomID, Host.SlotIndex, Host.Sequecen, savData, screenData);
|
||||
}
|
||||
Host.GetSavData(out byte[] savData, out byte[] screenData);
|
||||
Host.CloudAPI.SendUpLoadGameSav(Host.RomID, Host.SlotIndex, Host.Sequecen, savData, screenData);
|
||||
}
|
||||
|
||||
public override void OnExit(SimpleFSM<SaveFile>.State nextState)
|
||||
{
|
||||
Host.CloudAPI.OnUploadedSavData -= Api_OnUploadedSavData;
|
||||
}
|
||||
public override void OnExit(SimpleFSM<SaveFile>.State nextState)
|
||||
{
|
||||
Host.CloudAPI.OnUploadedSavData -= Api_OnUploadedSavData;
|
||||
}
|
||||
|
||||
private void Api_OnUploadedSavData(int romID, int slotIndex, AxibugProtobuf.Protobuf_Mine_GameSavInfo savInfo)
|
||||
{
|
||||
if (Host.RomID != romID) return;
|
||||
if (Host.SlotIndex != slotIndex) return;
|
||||
private void Api_OnUploadedSavData(int romID, int slotIndex, AxibugProtobuf.Protobuf_Mine_GameSavInfo savInfo)
|
||||
{
|
||||
if (Host.RomID != romID) return;
|
||||
if (Host.SlotIndex != slotIndex) return;
|
||||
|
||||
FSM.ChangeState<SyncedState>();
|
||||
FSM.ChangeState<SyncedState>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,10 +4,10 @@ using System.Linq;
|
||||
|
||||
namespace AxibugEmuOnline.Client.Tools
|
||||
{
|
||||
public class SimpleFSM<HOST>
|
||||
public partial class SimpleFSM<HOST>
|
||||
{
|
||||
public event Action OnStateChanged;
|
||||
private Dictionary<Type, State> m_states = new Dictionary<Type, State>();
|
||||
private State m_current;
|
||||
|
||||
public HOST Host { get; private set; }
|
||||
|
||||
@ -16,7 +16,18 @@ namespace AxibugEmuOnline.Client.Tools
|
||||
Host = host;
|
||||
}
|
||||
|
||||
public State CurrentState => m_current;
|
||||
private State m_current;
|
||||
public State CurrentState
|
||||
{
|
||||
get => m_current;
|
||||
set
|
||||
{
|
||||
if (m_current == value) return;
|
||||
|
||||
m_current = value;
|
||||
OnStateChanged?.Invoke();
|
||||
}
|
||||
}
|
||||
public T AddState<T>() where T : State, new()
|
||||
{
|
||||
var stateType = typeof(T);
|
||||
|
@ -46,6 +46,13 @@ namespace AxibugEmuOnline.Client
|
||||
m_subOptions.Add(new LoadMenuItem(inGameui, savFile));
|
||||
}
|
||||
|
||||
public override void OnShow(OptionUI_MenuItem ui)
|
||||
{
|
||||
base.OnShow(ui);
|
||||
|
||||
SavFile.TrySync();
|
||||
}
|
||||
|
||||
protected override List<InternalOptionMenu> GetOptionMenus()
|
||||
{
|
||||
return m_subOptions;
|
||||
@ -65,6 +72,13 @@ namespace AxibugEmuOnline.Client
|
||||
|
||||
public override void OnExcute(OptionUI optionUI, ref bool cancelHide)
|
||||
{
|
||||
if (m_savFile.IsBusy)
|
||||
{
|
||||
OverlayManager.PopTip("存档正在同步中");
|
||||
cancelHide = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var stateData = m_ingameUI.Core.GetStateBytes();
|
||||
var tex = m_ingameUI.Core.OutputPixel;
|
||||
var screenData = tex.ToJPG(m_ingameUI.Core.DrawCanvas.transform.localScale);
|
||||
|
@ -439,7 +439,6 @@ namespace AxibugEmuOnline.Client
|
||||
public abstract string Name { get; }
|
||||
public virtual Sprite Icon { get; }
|
||||
public virtual bool Visible => true;
|
||||
public virtual bool Enable => true;
|
||||
|
||||
public virtual void OnFocus() { }
|
||||
public virtual void OnShow(OptionUI_MenuItem ui) { }
|
||||
|
@ -1,4 +1,7 @@
|
||||
using UnityEngine;
|
||||
using AxibugEmuOnline.Client.Tools;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using static AxibugEmuOnline.Client.InGameUI_SaveStateMenu;
|
||||
|
||||
@ -10,15 +13,38 @@ namespace AxibugEmuOnline.Client
|
||||
public Image UI_Empty;
|
||||
public Text UI_SavTime;
|
||||
|
||||
public GameObject UI_Disconnect;
|
||||
public GameObject UI_Syncing;
|
||||
public GameObject UI_Conflict;
|
||||
public GameObject UI_Synced;
|
||||
|
||||
Texture2D m_screenTex;
|
||||
|
||||
Dictionary<Type, GameObject> m_stateNodes = new Dictionary<Type, GameObject>();
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
m_stateNodes[typeof(SaveFile.CheckingState)] = UI_Syncing;
|
||||
m_stateNodes[typeof(SaveFile.ConflictState)] = UI_Conflict;
|
||||
m_stateNodes[typeof(SaveFile.DownloadingState)] = UI_Syncing;
|
||||
m_stateNodes[typeof(SaveFile.SyncedState)] = UI_Synced;
|
||||
m_stateNodes[typeof(SaveFile.UploadingState)] = UI_Syncing;
|
||||
m_stateNodes[typeof(SaveFile.CheckingNetworkState)] = UI_Disconnect;
|
||||
}
|
||||
|
||||
protected override void OnSetData(InternalOptionMenu menuData)
|
||||
{
|
||||
base.OnSetData(menuData);
|
||||
|
||||
RefreshUI();
|
||||
MenuData.SavFile.OnSavSuccessed += SavFile_OnSavSuccessed;
|
||||
MenuData.SavFile.OnStateChanged += UpdateStateNode;
|
||||
}
|
||||
|
||||
MenuData.SavFile.OnSavSuccessed += RefreshUI;
|
||||
private void SavFile_OnSavSuccessed()
|
||||
{
|
||||
MenuData.SavFile.TrySync();
|
||||
RefreshUI();
|
||||
}
|
||||
|
||||
private void RefreshUI()
|
||||
@ -48,6 +74,21 @@ namespace AxibugEmuOnline.Client
|
||||
m_screenTex.LoadImage(screenShotData);
|
||||
UI_ScreenShot.texture = m_screenTex;
|
||||
}
|
||||
|
||||
UpdateStateNode();
|
||||
}
|
||||
|
||||
private void UpdateStateNode()
|
||||
{
|
||||
var stateType = MenuData.SavFile.GetState().GetType();
|
||||
|
||||
foreach (var item in m_stateNodes)
|
||||
{
|
||||
var type = item.Key;
|
||||
var nodeGo = item.Value;
|
||||
|
||||
nodeGo.SetActiveEx(type == stateType);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnHide()
|
||||
@ -60,7 +101,8 @@ namespace AxibugEmuOnline.Client
|
||||
m_screenTex = null;
|
||||
}
|
||||
|
||||
MenuData.SavFile.OnSavSuccessed -= RefreshUI;
|
||||
MenuData.SavFile.OnSavSuccessed -= SavFile_OnSavSuccessed;
|
||||
MenuData.SavFile.OnStateChanged -= UpdateStateNode;
|
||||
}
|
||||
|
||||
public override void OnExecute(OptionUI optionUI, ref bool cancelHide)
|
||||
|
Loading…
Reference in New Issue
Block a user