using System; using System.Threading; using System.Threading.Tasks; using EventPartnerGather; using EventPartnerGatherRequests; using UnityEngine; using EEventBuildState = EventPartnerGatherRequests.EEventBuildState; namespace UI.PartnerGather { /// /// 自动匹配控制器 /// 负责管理自动匹配的生命周期、轮询和状态处理 /// public class AutoMatchController : IDisposable { private const bool EnableTraceLogs = false; /// Set true to log each poll cycle (state / IsInMatching / partner count). English only. private const bool EnablePollCycleLogs = false; private readonly IAutoMatchManagerContext _manager; private CancellationTokenSource _pollCancellation; private bool _isMatching; private const int PollIntervalSeconds = 10; public bool IsMatching => _isMatching; public AutoMatchController(IAutoMatchManagerContext manager) { _manager = manager ?? throw new ArgumentNullException(nameof(manager)); } /// /// Panel re-enabled: if local toggle is still on, sync partners from status then (re)start matching + polling. /// Do not clear IsAutoMatchEnabled when status says IsInMatching=false — user may have only closed the panel /// (polling was cancelled locally) or the API may not reflect queue state; re-call ControlAutoMatch(true) via StartAutoMatch. /// public async Task TryResumeAutoMatch() { try { if (!_manager.IsAutoMatchEnabled) return; if (_manager.ShouldCloseAutoMatch) { _manager.SetAutoMatchEnabled(false); _manager.NotifyAutoMatchStopped(); return; } var response = await _manager.FetchAndProcessAutoMatchStatus(force: true); if (response?.IsInMatching == true) ELog($"AutoMatchStateRestored eventId={_manager.EventId}"); var ok = await StartAutoMatch(); if (!ok) _manager.SetAutoMatchEnabled(false); } catch (Exception e) { LogError($"TryResumeAutoMatchError eventId={_manager.EventId} error={e.Message}"); } } /// /// 开启自动匹配 /// public async Task StartAutoMatch() { var eventId = _manager.EventId; try { Trace($"AutoMatchStartRequest eventId={eventId}"); var response = await _manager.ControlAutoMatch(true); if (response.State != EEventBuildState.Success) { LogError($"AutoMatchStartFailed eventId={eventId} state={response.State} reason={response.Message}"); return false; } _isMatching = true; StartPullMatchResult(eventId); ELog($"AutoMatchStarted eventId={eventId}"); return true; } catch (Exception e) { LogError($"AutoMatchStartException eventId={eventId} error={e.Message}"); return false; } } private void StartPullMatchResult(int eventId) { _pollCancellation = new CancellationTokenSource(); _ = PollMatchResult(eventId, _pollCancellation.Token); } /// /// 停止自动匹配 /// public async Task StopAutoMatch() { Trace($"AutoMatchStopRequest eventId={_manager.EventId}"); if (!_isMatching) return; try { ELog($"AutoMatchStopping eventId={_manager.EventId}"); await _manager.ControlAutoMatch(false); } catch (Exception e) { LogError($"AutoMatchStopException eventId={_manager.EventId} error={e.Message}"); } finally { CancelPollMatchResult(); } } public void CancelPollMatchResult() { Trace($"AutoMatchPollingCancel eventId={_manager.EventId}"); _pollCancellation?.Cancel(); _pollCancellation?.Dispose(); _pollCancellation = null; _isMatching = false; } /// /// 轮询匹配结果 /// private async Task PollMatchResult(int eventId, CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { try { var bRet = await CheckStopAutoMatch(); if (bRet) { break; } var response = await _manager.FetchAndProcessAutoMatchStatus(force: true); var buildState = response?.State; PollCycleLog( $"PollTick eventId={eventId} state={buildState} isInMatching={response?.IsInMatching} partnerCount={response?.Partners?.Count ?? 0}"); switch (buildState) { case EEventBuildState.Success: await OnMatchSuccess(response); break; case EEventBuildState.Failed: OnMatchFailed(response); break; } if (buildState == EEventBuildState.Failed) break; // 等待下次轮询 await Task.Delay(PollIntervalSeconds * 1000, cancellationToken); } catch (OperationCanceledException) { Trace($"AutoMatchPollingCancelled eventId={eventId}"); break; } catch (Exception e) { LogError($"AutoMatchPollingException eventId={eventId} error={e.Message}"); await Task.Delay(PollIntervalSeconds * 1000, cancellationToken); } } _isMatching = false; } private async Task OnMatchSuccess(AutoMatchStatusResponse result) { Trace($"AutoMatchSuccess eventId={_manager.EventId} partnerCount={result?.Partners?.Count ?? 0}"); await CheckStopAutoMatch(); } /// /// 处理匹配失败 /// private void OnMatchFailed(AutoMatchStatusResponse result) { var message = result?.Message ?? string.Empty; ELog($"AutoMatchFailed eventId={_manager.EventId} state={result?.State} reason={message}"); _manager.NotifyAutoMatchFailed(message); _ = StopAutoMatch(); } private async Task CheckStopAutoMatch() { Trace($"AutoMatchCheckStop eventId={_manager.EventId}"); if (_manager.ShouldCloseAutoMatch) { await StopAutoMatch(); _manager.NotifyAutoMatchStopped(); return true; } return false; } /// /// 释放资源 /// public void Dispose() { Trace($"AutoMatchDispose eventId={_manager.EventId}"); CancelPollMatchResult(); } #region 日志 private static void PollCycleLog(string message) { if (!EnablePollCycleLogs) return; #if UNITY_EDITOR Debug.Log($"[AutoMatchController] {message}"); #else Debug.Log($"[AutoMatchController] {message}"); #endif } private static void ELog(string message) { #if UNITY_EDITOR Debug.Log($"[AutoMatchController] {message}"); #endif } [System.Diagnostics.Conditional("UNITY_EDITOR")] private static void Trace(string message) { if (!EnableTraceLogs) return; ELog(message); } private static void LogError(string message) { Debug.LogError($"[AutoMatchController] {message}"); } #endregion } }