Merge pull request 'master' (#96) from Alienjack/AxibugEmuOnline:master into master

Reviewed-on: sin365/AxibugEmuOnline#96
This commit is contained in:
sin365 2025-04-22 19:02:09 +08:00
commit ee9d54386b
27 changed files with 781 additions and 134 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -0,0 +1,130 @@
fileFormatVersion: 2
guid: 994c93ed03dadee49974aebf3cb5365d
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
customData:
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -0,0 +1,130 @@
fileFormatVersion: 2
guid: 630c435b903e3dc4e80038c0a531162d
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
customData:
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -0,0 +1,130 @@
fileFormatVersion: 2
guid: c5a9535bb63e1f14f9a1528566864ab2
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
customData:
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -0,0 +1,130 @@
fileFormatVersion: 2
guid: 28db37d78cad20f4397804dfa4572829
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
customData:
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@ -551,13 +551,13 @@ RectTransform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4232056521759880276}
m_LocalRotation: {x: 1, y: 0, z: 0, w: 0}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_LocalScale: {x: 1, y: -1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 4232056520494431727}
m_LocalEulerAnglesHint: {x: 180, y: 0, z: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}

View File

@ -1688,7 +1688,7 @@ RectTransform:
m_GameObject: {fileID: 5162569472849600096}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: -1, z: 1}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 5970282275929291192}

View File

@ -63,6 +63,12 @@ namespace AxibugEmuOnline.Client.ClientCore
{
return s_persistentRoot + "/" + emuPlatform.ToString();
}
public static string UserPersistenDataPath(RomPlatformType emuPlatform)
{
return string.Format("{0}/{1}", PersistentDataPath(emuPlatform), user.userdata.UID);
}
public static string PersistentDataRoot() => s_persistentRoot;
public static RomLib GetRomLib(RomPlatformType platform)

View File

@ -1,14 +1,15 @@
using AxibugEmuOnline.Client.ClientCore;
using AxibugEmuOnline.Client.Tools;
using AxibugProtobuf;
using MAME.Core;
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace AxibugEmuOnline.Client
{
/// <summary> 存档文件管理类 </summary>
public class SaveFile
/// <summary> 存档文件抽象类 </summary>
public partial class SaveFile
{
public SavCloudApi CloudAPI => App.SavMgr.CloudApi;
@ -28,8 +29,8 @@ namespace AxibugEmuOnline.Client
{
get
{
var path = App.PersistentDataPath(EmuPlatform);
path = $"{path}/Slot/{EmuPlatform}/{RomID}";
var path = App.UserPersistenDataPath(EmuPlatform);
path = $"{path}/Slot/{RomID}";
Directory.CreateDirectory(path);
@ -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
}
}
}

View File

@ -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>();
}
}
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 288e73ce3cb773e46b7d37a53a3b43ee

View File

@ -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>();
}
}
}
}

View File

@ -2,8 +2,11 @@
namespace AxibugEmuOnline.Client
{
public class ConflictState : SimpleFSM<SaveFile>.State
public partial class SaveFile
{
public class ConflictState : SimpleFSM<SaveFile>.State
{
}
}
}

View File

@ -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>();
}
}
}

View File

@ -0,0 +1,11 @@
using AxibugEmuOnline.Client.Tools;
namespace AxibugEmuOnline.Client
{
public partial class SaveFile
{
public class IdleState : SimpleFSM<SaveFile>.State
{
}
}
}

View File

@ -2,7 +2,10 @@
namespace AxibugEmuOnline.Client
{
public class SyncedState : SimpleFSM<SaveFile>.State
public partial class SaveFile
{
public class SyncedState : SimpleFSM<SaveFile>.State
{
}
}
}

View File

@ -1,8 +0,0 @@
using AxibugEmuOnline.Client.Tools;
namespace AxibugEmuOnline.Client
{
public class UnkownState : SimpleFSM<SaveFile>.State
{
}
}

View File

@ -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>();
}
}
}
}

View File

@ -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);

View File

@ -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,9 +72,16 @@ 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();
var screenData = tex.ToJPG(m_ingameUI.Core.DrawCanvas.transform.localScale);
m_savFile.Save(m_savFile.Sequecen, stateData, screenData);
}

View File

@ -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) { }

View File

@ -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)

View File

@ -55,29 +55,26 @@ namespace AxibugEmuOnline.Client
}
public static byte[] ToJPG(this Texture texture)
public static byte[] ToJPG(this Texture texture, Vector2 scale)
{
Texture2D outputTex = null;
if (texture is RenderTexture rt)
{
outputTex = ConvertFromRenderTexture(rt);
}
else if (texture is Texture2D)
{
outputTex = texture as Texture2D;
}
Texture2D outputTex = ConvertFromRenderTexture(texture, scale);
return outputTex.EncodeToJPG();
}
private static Texture2D ConvertFromRenderTexture(RenderTexture rt)
private static Texture2D ConvertFromRenderTexture(Texture src, Vector2 scale)
{
float offsetX = (scale.x < 0) ? 1 : 0;
float offsetY = (scale.y < 0) ? 1 : 0;
var offset = new Vector2(offsetX, offsetY);
// 创建临时RenderTexture并拷贝内容
RenderTexture tempRT = RenderTexture.GetTemporary(rt.width, rt.height, 0, RenderTextureFormat.ARGB32);
Graphics.Blit(rt, tempRT);
RenderTexture tempRT = RenderTexture.GetTemporary(src.width, src.height, 0, RenderTextureFormat.ARGB32);
Graphics.Blit(src, tempRT, scale, offset);
// 读取到Texture2D
Texture2D tex = new Texture2D(rt.width, rt.height, TextureFormat.RGBA32, false);
Texture2D tex = new Texture2D(src.width, src.height, TextureFormat.RGBA32, false);
RenderTexture.active = tempRT;
tex.ReadPixels(new Rect(0, 0, tempRT.width, tempRT.height), 0, 0);
tex.Apply();