feat(upserver): 接入认证与KV存档并完成限定钓鱼闭环
This commit is contained in:
@@ -6,6 +6,8 @@ using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using game.Account;
|
||||
using game.PlayerDataStorage;
|
||||
|
||||
public interface ICustomServerMgr
|
||||
{
|
||||
@@ -21,6 +23,11 @@ public interface ICustomServerMgr
|
||||
Task<T> EventPartnerRequest<T>(string url, object data = null);
|
||||
Task<string> EventPartnerRequest(string url, object data);
|
||||
CookieContainer Cookie { get; }
|
||||
Uri ApiBaseUri { get; }
|
||||
int SessionVersion { get; }
|
||||
event Action SessionChanged;
|
||||
void UseAccountSession(S101AccountSession session);
|
||||
void ClearAccountSession();
|
||||
}
|
||||
|
||||
public class CustomServerMgr : ICustomServerMgr
|
||||
@@ -31,87 +38,122 @@ public class CustomServerMgr : ICustomServerMgr
|
||||
HttpClient client;
|
||||
|
||||
private CookieContainer _cookie;
|
||||
private readonly Func<CookieContainer, HttpMessageHandler> handlerFactory;
|
||||
public int SessionVersion { get; private set; }
|
||||
public event Action SessionChanged;
|
||||
PlayerKVAccountLease kvLease;
|
||||
bool kvOnly;
|
||||
int pfRequests;
|
||||
internal event Action<int> KVUnauthorized;
|
||||
internal bool IsFreshForKVLoad => pfRequests==0 && kvLease==null;
|
||||
internal void BindKVForValidation(PlayerKVAccountLease lease)
|
||||
{
|
||||
if(lease==null || !lease.IsCurrent || lease.ApiBaseUri!=ApiBaseUri || !IsFreshForKVLoad)
|
||||
throw new InvalidOperationException("Fresh matching KV lease required.");
|
||||
kvOnly=true; SessionVersion++; kvLease=lease; _cookie=null;
|
||||
var previous=client; client=null; previous?.Dispose(); SessionChanged?.Invoke();
|
||||
}
|
||||
internal Task ClearKVForValidation()
|
||||
{
|
||||
SessionVersion++; var previous=kvLease; kvLease=null;
|
||||
var closing=previous?.CloseAsync() ?? Task.CompletedTask;
|
||||
SessionChanged?.Invoke(); return closing;
|
||||
}
|
||||
internal async Task<PlayerKVAccountResponse> SendKVForValidationAsync(string route)
|
||||
{
|
||||
var lease=kvLease; int version=SessionVersion;
|
||||
if(!kvOnly || lease==null) return new PlayerKVAccountResponse(false);
|
||||
var response=await lease.SendAsync(HttpMethod.Get,route);
|
||||
if(version!=SessionVersion || !ReferenceEquals(lease,kvLease) || !response.IsCurrent) return new PlayerKVAccountResponse(false);
|
||||
if(response.StatusCode==401 || response.StatusCode==403)
|
||||
{
|
||||
KVUnauthorized?.Invoke(version);
|
||||
return new PlayerKVAccountResponse(false,response.StatusCode);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
public CookieContainer Cookie => _cookie ?? throw new System.NullReferenceException();
|
||||
public Uri ApiBaseUri { get; }
|
||||
|
||||
public CustomServerMgr(IConfig config)
|
||||
public CustomServerMgr(IConfig config) : this(config, null) { }
|
||||
|
||||
public CustomServerMgr(IConfig config, Func<CookieContainer, HttpMessageHandler> handlerFactory)
|
||||
{
|
||||
this.handlerFactory = handlerFactory ?? (cookies => new HttpClientHandler
|
||||
{
|
||||
CookieContainer = cookies, UseCookies = true, AllowAutoRedirect = false
|
||||
});
|
||||
this.config = config;
|
||||
|
||||
var httpClientHandler = new HttpClientHandler();
|
||||
_cookie = new CookieContainer();
|
||||
httpClientHandler.CookieContainer = _cookie;
|
||||
client = new HttpClient(httpClientHandler);
|
||||
|
||||
client.BaseAddress =
|
||||
new System.Uri(config.Get<string>(GConstant.K_Event_API_URL,
|
||||
"http://192.168.9.101:5292/"));
|
||||
client.Timeout = TimeSpan.FromSeconds(15);
|
||||
var url = config.Get<string>(GConstant.K_Event_API_URL, S101AccountSession.DefaultApiUrl);
|
||||
if (string.IsNullOrWhiteSpace(url)) url = S101AccountSession.DefaultApiUrl;
|
||||
ApiBaseUri = new Uri(url.TrimEnd('/') + "/");
|
||||
ClearAccountSession();
|
||||
}
|
||||
|
||||
public async Task<string> CustomServerPost(string url, string bodyString,
|
||||
string contentType = "application/json")
|
||||
public void UseAccountSession(S101AccountSession session)
|
||||
{
|
||||
if(kvOnly) throw new InvalidOperationException("Controlled container cannot switch to PlayFab.");
|
||||
if (session == null || !session.IsAuthenticated || session.ApiBaseUri != ApiBaseUri)
|
||||
throw new ArgumentException("An authenticated session for this API is required.", nameof(session));
|
||||
ReplaceTransport(session.Cookies);
|
||||
}
|
||||
|
||||
public void ClearAccountSession()
|
||||
{
|
||||
if(kvOnly) { PlayerKVShutdownRegistry.Process.Register(ClearKVForValidation()); return; }
|
||||
ReplaceTransport(new CookieContainer());
|
||||
}
|
||||
|
||||
private void ReplaceTransport(CookieContainer cookies)
|
||||
{
|
||||
SessionVersion++;
|
||||
var previous = client;
|
||||
_cookie = cookies;
|
||||
client = new HttpClient(handlerFactory(cookies)) { BaseAddress = ApiBaseUri, Timeout = TimeSpan.FromSeconds(15) };
|
||||
previous?.Dispose();
|
||||
SessionChanged?.Invoke();
|
||||
}
|
||||
|
||||
public Task<string> CustomServerPost(string url, string bodyString,
|
||||
string contentType = "application/json") => SendAsync(HttpMethod.Post, url, bodyString, contentType);
|
||||
|
||||
public Task<string> GetStringAsync(string url) => SendAsync(HttpMethod.Get, url, null, null);
|
||||
|
||||
// Unity callers use the main thread. Capture a fixed transport before the first await.
|
||||
private async Task<string> SendAsync(HttpMethod method, string url, string body, string contentType)
|
||||
{
|
||||
if(kvOnly) throw new InvalidOperationException("Only controlled validation routes are enabled.");
|
||||
pfRequests++;
|
||||
var transport = client;
|
||||
var version = SessionVersion;
|
||||
try
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log("client.BaseAddress.AbsoluteUri:" + client.BaseAddress.AbsoluteUri);
|
||||
Debug.Log("CustomServerPost url:" + url);
|
||||
Debug.Log("CustomServerPost bodyString:" + bodyString);
|
||||
#endif
|
||||
var content = new StringContent(bodyString, Encoding.UTF8, "application/json");
|
||||
var response = await client.PostAsync(url, content);
|
||||
string result = await response.Content.ReadAsStringAsync();
|
||||
if (response.StatusCode == System.Net.HttpStatusCode.OK)
|
||||
using (var request = new HttpRequestMessage(method, url))
|
||||
{
|
||||
//GameDebug.Log("CustomServerPost result:" + result.ToString());
|
||||
return result;
|
||||
}
|
||||
else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized || response.StatusCode == System.Net.HttpStatusCode.Forbidden)
|
||||
{
|
||||
GContext.Publish(new game.EvtAPISvrUnauthorized());
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError(response.StatusCode.ToString() + "+++CustomServerPost error:" +
|
||||
result.ToString() + "+++CustomServerPost url:" + url + ":" +
|
||||
bodyString);
|
||||
return null;
|
||||
if (body != null) request.Content = new StringContent(body, Encoding.UTF8, contentType);
|
||||
using (var response = await transport.SendAsync(request))
|
||||
{
|
||||
var result = await response.Content.ReadAsStringAsync();
|
||||
if (version != SessionVersion) return null;
|
||||
if (response.StatusCode == HttpStatusCode.OK) return result;
|
||||
if (response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden)
|
||||
GContext.Publish(new game.EvtAPISvrUnauthorized { SessionVersion = version });
|
||||
else
|
||||
Debug.LogError($"API request failed: HTTP {(int)response.StatusCode}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError("CustomServerPost error:" + e.Message + "---CustomServerPost url:" +
|
||||
url + ":" + bodyString + "\n" + e.StackTrace);
|
||||
if (version == SessionVersion) Debug.LogError($"API request failed: {e.GetType().Name}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<string> GetStringAsync(string url)
|
||||
{
|
||||
var response = await client.GetAsync(url);
|
||||
|
||||
if (response.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
return await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
string result = await response.Content.ReadAsStringAsync();
|
||||
Debug.LogError(response.StatusCode.ToString() + "+++GetStringAsync error:" +
|
||||
result.ToString() + "+++GetStringAsync url:" + url);
|
||||
}
|
||||
|
||||
if (response.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
GContext.Publish(new game.EvtAPISvrUnauthorized());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<string> AccountRequest(string url, object data)
|
||||
{
|
||||
return await CustomServerPost($"account/{url}",
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using game.PlayerDataStorage;
|
||||
|
||||
public class PlayFabMgr : MonoBehaviour
|
||||
{
|
||||
@@ -25,6 +26,9 @@ public class PlayFabMgr : MonoBehaviour
|
||||
private CancellationTokenSource tokenSource;
|
||||
|
||||
static PlayFabMgr _instance = null;
|
||||
// An inactive, never-awakened component proves that no timer or PF send has ever been started.
|
||||
internal bool IsUnstartedForKVLoad => tokenSource==null && dataBuffer==null && swapData==null && localData==null && lastUpdateTime==null && _instance!=this;
|
||||
internal static bool HasLiveInstanceForKVLoad => _instance!=null;
|
||||
|
||||
public static PlayFabMgr Instance
|
||||
{
|
||||
@@ -41,6 +45,10 @@ public class PlayFabMgr : MonoBehaviour
|
||||
|
||||
public Dictionary<string, string> CompareDatas(Dictionary<string, UserDataRecord> remoteDatas, string userId)
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerKVGameplayLoop.Enabled) throw PlayerKVGameplayLoop.Stop("PF archive comparison is disabled in KV loop.");
|
||||
#endif
|
||||
PlayerKVGameLoadGuard.RequireLegacyAllowed(PlayerKVGuardPoint.Compare);
|
||||
Last_Update_Time_Key = $"{Last_Update_Time_Key_Prefix}_{userId}";
|
||||
Local_Data_Key = $"{Local_Data_Key_prefix}_{userId}";
|
||||
var lastUpdateTimeStr = PlayerPrefs.GetString(Last_Update_Time_Key, string.Empty);
|
||||
@@ -114,12 +122,26 @@ public class PlayFabMgr : MonoBehaviour
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if(PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Lifecycle)) return;
|
||||
_instance = this;
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerKVGameplayLoop.Enabled) return;
|
||||
#endif
|
||||
InitBuffer();
|
||||
tokenSource = new CancellationTokenSource();
|
||||
UpdateUserDataValue(tokenSource.Token);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
internal void StartKVGameplayLoopSync()
|
||||
{
|
||||
if(!PlayerKVGameplayLoop.Ready || tokenSource!=null) return;
|
||||
tokenSource=new CancellationTokenSource();
|
||||
UpdateUserDataValue(tokenSource.Token);
|
||||
}
|
||||
internal void StopKVGameplayLoopSync()
|
||||
{ tokenSource?.Cancel(); tokenSource?.Dispose(); tokenSource=null; }
|
||||
#endif
|
||||
void InitBuffer()
|
||||
{
|
||||
dataBuffer = new LinkedList<Dictionary<string, string>>();
|
||||
@@ -132,6 +154,15 @@ public class PlayFabMgr : MonoBehaviour
|
||||
|
||||
void OnApplicationPause(bool is_pause)
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerKVGameplayLoop.Enabled)
|
||||
{
|
||||
if(is_pause) { StopKVGameplayLoopSync(); _=PlayerKVGameplayLoop.FlushObservedAsync(); }
|
||||
else StartKVGameplayLoopSync();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if(PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Lifecycle)) return;
|
||||
if (is_pause)
|
||||
{
|
||||
tokenSource.Cancel();
|
||||
@@ -147,6 +178,10 @@ public class PlayFabMgr : MonoBehaviour
|
||||
|
||||
void OnApplicationQuit()
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerKVGameplayLoop.Enabled) { StopKVGameplayLoopSync(); _=PlayerKVGameplayLoop.CloseAsync(); return; }
|
||||
#endif
|
||||
if(PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Lifecycle)) return;
|
||||
tokenSource.Cancel();
|
||||
tokenSource = null;
|
||||
SyncData();
|
||||
@@ -154,13 +189,19 @@ public class PlayFabMgr : MonoBehaviour
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerKVGameplayLoop.Enabled) { StopKVGameplayLoopSync(); _=PlayerKVGameplayLoop.CloseAsync(); return; }
|
||||
#endif
|
||||
if(PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Lifecycle)) return;
|
||||
tokenSource?.Cancel();
|
||||
tokenSource = null;
|
||||
if(!PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.PrefsSave)) PlayerPrefs.Save();
|
||||
SyncData();
|
||||
}
|
||||
|
||||
private async void UpdateUserDataValue(CancellationToken token)
|
||||
{
|
||||
if(PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Lifecycle)) return;
|
||||
try
|
||||
{
|
||||
while (!token.IsCancellationRequested)
|
||||
@@ -174,6 +215,10 @@ public class PlayFabMgr : MonoBehaviour
|
||||
|
||||
public void UpdateUserDataValue(string key, string data, bool remoteStore = true)
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerKVGameplayLoop.Enabled) { PlayerKVGameplayLoop.Write(key,data,remoteStore); return; }
|
||||
#endif
|
||||
PlayerKVGameLoadGuard.RequireLegacyAllowed(PlayerKVGuardPoint.Update);
|
||||
if (remoteStore)
|
||||
{
|
||||
var cbuff = dataBuffer.First;
|
||||
@@ -203,6 +248,10 @@ public class PlayFabMgr : MonoBehaviour
|
||||
/// <returns></returns>
|
||||
public string GetLocalData(string key)
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerKVGameplayLoop.Enabled) return PlayerKVGameplayLoop.Read(key);
|
||||
#endif
|
||||
PlayerKVGameLoadGuard.RequireLegacyAllowed(PlayerKVGuardPoint.ReadLocal);
|
||||
if (localData.ContainsKey(key))
|
||||
{
|
||||
return localData[key];
|
||||
@@ -215,6 +264,12 @@ public class PlayFabMgr : MonoBehaviour
|
||||
|
||||
private void SyncData()
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerKVGameplayLoop.Enabled) { _=PlayerKVGameplayLoop.FlushObservedAsync(); return; }
|
||||
#endif
|
||||
if(PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Sync)) return;
|
||||
// Logout clears credentials before RootCtx is destroyed. Keep pending data local.
|
||||
if (!PlayFabClientAPI.IsClientLoggedIn()) return;
|
||||
var buf = dataBuffer.First;
|
||||
if (buf.Value.Count == 0)
|
||||
return;
|
||||
@@ -267,6 +322,10 @@ public class PlayFabMgr : MonoBehaviour
|
||||
|
||||
public async Task<bool> SyncDataImmidiate(string key, string data)
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerKVGameplayLoop.Enabled) throw PlayerKVGameplayLoop.Stop("Immediate/consuming saves are outside the equipment loop.");
|
||||
#endif
|
||||
if(PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Immediate)) return false;
|
||||
foreach (var buff in dataBuffer)
|
||||
{
|
||||
if (buff.ContainsKey(key))
|
||||
@@ -293,4 +352,52 @@ public class PlayFabMgr : MonoBehaviour
|
||||
);
|
||||
return await taskSource.Task;
|
||||
}
|
||||
|
||||
// BEGIN KV explicit fixed-handle access
|
||||
internal static PlayerKVKeyReadResult ReadKV(PlayerKVGameSessionHandle handle, string key)
|
||||
{
|
||||
if (PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.ReadLocal))
|
||||
return new PlayerKVKeyReadResult(PlayerKVKeyReadError.LoadGuardActive, handle);
|
||||
if (handle == null || !KVSnapshot.ValidKey(key))
|
||||
return new PlayerKVKeyReadResult(PlayerKVKeyReadError.InvalidInput, handle);
|
||||
var read = handle.ReadLocalSnapshot();
|
||||
if (!read.Success)
|
||||
return new PlayerKVKeyReadResult(read.Error == PlayerKVLocalReadError.StaleOrClosing
|
||||
? PlayerKVKeyReadError.StaleOrClosing : PlayerKVKeyReadError.NotReady, handle);
|
||||
var found = read.Data.TryGetValue(key, out var value);
|
||||
return new PlayerKVKeyReadResult(PlayerKVKeyReadError.None, handle, found, value);
|
||||
}
|
||||
|
||||
internal static PlayerKVEditResult WriteKV(PlayerKVGameSessionHandle handle,
|
||||
string key, string value, bool remoteStore = true)
|
||||
{
|
||||
if (PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Update))
|
||||
return new PlayerKVEditResult(new PlayerKVStateResult(PlayerKVChangeError.Blocked));
|
||||
if (handle == null)
|
||||
return new PlayerKVEditResult(new PlayerKVStateResult(PlayerKVChangeError.Validation));
|
||||
return handle.Update(key, value, remoteStore);
|
||||
}
|
||||
|
||||
internal static Task<PlayerKVFlushResult> FlushKVAsync(PlayerKVGameSessionHandle handle,
|
||||
CancellationToken waitToken = default)
|
||||
{
|
||||
if (PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Sync))
|
||||
return Task.FromResult(new PlayerKVFlushResult(0, 0, 1, 0, PlayerKVError.Cancelled,
|
||||
handle == null ? PlayerKVSyncStatus.NotLoaded : handle.Status));
|
||||
if (handle == null)
|
||||
return Task.FromResult(new PlayerKVFlushResult(0, 0, 1, 0, PlayerKVError.Validation, PlayerKVSyncStatus.NotLoaded));
|
||||
return handle.FlushAsync(waitToken);
|
||||
}
|
||||
|
||||
internal static Task<PlayerKVTicketResult> WaitForKVTicketAsync(PlayerKVGameSessionHandle handle,
|
||||
PlayerKVSyncTicket ticket, CancellationToken waitToken = default)
|
||||
{
|
||||
if (PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Sync))
|
||||
return Task.FromResult(new PlayerKVTicketResult(PlayerKVTicketStatus.Blocked, PlayerKVError.Cancelled,
|
||||
handle == null ? PlayerKVSyncStatus.NotLoaded : handle.Status));
|
||||
if (handle == null)
|
||||
return Task.FromResult(new PlayerKVTicketResult(PlayerKVTicketStatus.Blocked, PlayerKVError.Validation, PlayerKVSyncStatus.NotLoaded));
|
||||
return handle.WaitForTicketAsync(ticket, waitToken);
|
||||
}
|
||||
// END KV explicit fixed-handle access
|
||||
}
|
||||
|
||||
@@ -198,54 +198,69 @@ namespace Game
|
||||
}
|
||||
|
||||
protected CompositeDisposable disposables;
|
||||
private int initializationVersion;
|
||||
public async Task Init(System.Action<float> progress)
|
||||
{
|
||||
//playerData = GContext.container.Resolve<PlayerData>();
|
||||
_tables = GContext.container.Resolve<cfg.Tables>();
|
||||
//var service = (SoundService)soundService;
|
||||
await soundService.InitAsync();
|
||||
|
||||
if (residentClip != null || disposables != null) Release();
|
||||
var version = ++initializationVersion;
|
||||
var container = GContext.container;
|
||||
_tables = container.Resolve<cfg.Tables>();
|
||||
var opMap = new Dictionary<string, AsyncOperationHandle<AudioClip>>();
|
||||
|
||||
foreach (var addr in residentFish)
|
||||
CompositeDisposable pendingSubscriptions = null;
|
||||
bool transferred = false;
|
||||
try
|
||||
{
|
||||
opMap[addr] = Addressables.LoadAssetAsync<AudioClip>(addr);
|
||||
}
|
||||
|
||||
var tasks = opMap.Values.Select(_ => _.Task);
|
||||
var total = (float)tasks.Count();
|
||||
|
||||
while (true)
|
||||
{
|
||||
var completed = tasks.Count(_ => _.IsCompleted);
|
||||
if (completed == total)
|
||||
await soundService.InitAsync();
|
||||
if (version != initializationVersion || !ReferenceEquals(container, GContext.container)) return;
|
||||
foreach (var addr in residentFish)
|
||||
{
|
||||
progress.Invoke(1f);
|
||||
break;
|
||||
if (!opMap.ContainsKey(addr)) opMap.Add(addr, Addressables.LoadAssetAsync<AudioClip>(addr));
|
||||
}
|
||||
else
|
||||
var tasks = opMap.Values.Select(_ => _.Task).ToArray();
|
||||
var total = (float)tasks.Length;
|
||||
while (true)
|
||||
{
|
||||
var completed = tasks.Count(_ => _.IsCompleted);
|
||||
if (completed == total)
|
||||
{
|
||||
progress.Invoke(1f);
|
||||
break;
|
||||
}
|
||||
progress.Invoke(completed / total);
|
||||
await Awaiters.NextFrame;
|
||||
if (version != initializationVersion || !ReferenceEquals(container, GContext.container)) return;
|
||||
}
|
||||
if (version != initializationVersion || !ReferenceEquals(container, GContext.container)) return;
|
||||
var loadedClips = opMap.ToDictionary(_ => _.Key, _ => _.Value.Result);
|
||||
pendingSubscriptions = new CompositeDisposable();
|
||||
GContext.OnEvent<EventUISound>().Subscribe(PlaySound).AddTo(pendingSubscriptions);
|
||||
GContext.OnEvent<EventRewardSound>().Subscribe(PlayRewardSound).AddTo(pendingSubscriptions);
|
||||
GContext.OnEvent<EventFishingSound>().Subscribe(PlayFishingSound).AddTo(pendingSubscriptions);
|
||||
GContext.OnEvent<EventBGMSound>().Subscribe(PlayMusic).AddTo(pendingSubscriptions);
|
||||
GContext.OnEvent<FadeOutEvent>().Subscribe(FadeOutEvent).AddTo(pendingSubscriptions);
|
||||
GContext.OnEvent<AssetReferenceAudioClip>().Subscribe(PlaySoundByReference).AddTo(pendingSubscriptions);
|
||||
GContext.OnEvent<AssetReferenceAudioVoiceClip>().Subscribe(PlayVoiceSoundByReference).AddTo(pendingSubscriptions);
|
||||
|
||||
GContext.OnEvent<ChangeRodDataEvent>().Subscribe(OnChangeRodData).AddTo(pendingSubscriptions);
|
||||
GContext.OnEvent<SoundMuteEvent>().Subscribe(OnSoundMute).AddTo(pendingSubscriptions);
|
||||
GContext.OnEvent<OnSoundStateEvent>().Subscribe(OnSoundStateEvent).AddTo(pendingSubscriptions);
|
||||
soundService.BGMVol = container.Resolve<ISettingService>().Music ? 80 : 0;
|
||||
soundService.SFXVol = container.Resolve<ISettingService>().Sound ? 80 : 0;
|
||||
soundService.VoiceVol = container.Resolve<ISettingService>().Sound ? 80 : 0;
|
||||
residentClip = loadedClips;
|
||||
disposables = pendingSubscriptions;
|
||||
transferred = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!transferred)
|
||||
{
|
||||
pendingSubscriptions?.Dispose();
|
||||
if (version == initializationVersion) Release();
|
||||
foreach (var handle in opMap.Values)
|
||||
if (handle.IsValid()) Addressables.Release(handle);
|
||||
}
|
||||
await Awaiters.NextFrame;
|
||||
}
|
||||
|
||||
residentClip = opMap.ToDictionary(_ => _.Key, _ => _.Value.Result);
|
||||
disposables = new CompositeDisposable();
|
||||
GContext.OnEvent<EventUISound>().Subscribe(PlaySound).AddTo(disposables);
|
||||
GContext.OnEvent<EventRewardSound>().Subscribe(PlayRewardSound).AddTo(disposables);
|
||||
GContext.OnEvent<EventFishingSound>().Subscribe(PlayFishingSound).AddTo(disposables);
|
||||
GContext.OnEvent<EventBGMSound>().Subscribe(PlayMusic).AddTo(disposables);
|
||||
GContext.OnEvent<FadeOutEvent>().Subscribe(FadeOutEvent).AddTo(disposables);
|
||||
GContext.OnEvent<AssetReferenceAudioClip>().Subscribe(PlaySoundByReference).AddTo(disposables);
|
||||
GContext.OnEvent<AssetReferenceAudioVoiceClip>().Subscribe(PlayVoiceSoundByReference).AddTo(disposables);
|
||||
|
||||
GContext.OnEvent<ChangeRodDataEvent>().Subscribe(OnChangeRodData).AddTo(disposables);
|
||||
GContext.OnEvent<SoundMuteEvent>().Subscribe(OnSoundMute).AddTo(disposables);
|
||||
GContext.OnEvent<OnSoundStateEvent>().Subscribe(OnSoundStateEvent).AddTo(disposables);
|
||||
soundService.BGMVol = GContext.container.Resolve<ISettingService>().Music ? 80 : 0;
|
||||
soundService.SFXVol = GContext.container.Resolve<ISettingService>().Sound ? 80 : 0;
|
||||
soundService.VoiceVol = GContext.container.Resolve<ISettingService>().Sound ? 80 : 0;
|
||||
}
|
||||
void OnSoundStateEvent(OnSoundStateEvent onSoundStateEvent)
|
||||
{
|
||||
@@ -757,6 +772,7 @@ namespace Game
|
||||
}
|
||||
public void Release()
|
||||
{
|
||||
initializationVersion++;
|
||||
if (disposables != null)
|
||||
{
|
||||
disposables.Dispose();
|
||||
|
||||
Reference in New Issue
Block a user