diff --git a/Assets/Script/Common/Constants.cs b/Assets/Script/Common/Constants.cs index 83d9046..eb717cc 100644 --- a/Assets/Script/Common/Constants.cs +++ b/Assets/Script/Common/Constants.cs @@ -31,6 +31,7 @@ RevengeConfirmed, // 재대결 수락 전송 확인 RevengeRejected, // 재대결 거절 수신 RevengeRejectionConfirmed, // 재대결 거절 전송 확인 - ReceiveTimeout // 상대방이 타임 아웃일 때 + ReceiveTimeout, // 상대방이 타임 아웃일 때 + OpponentDisconnected }; } \ No newline at end of file diff --git a/Assets/Script/Game/GameLogic.cs b/Assets/Script/Game/GameLogic.cs index 957d3fb..1397fd2 100644 --- a/Assets/Script/Game/GameLogic.cs +++ b/Assets/Script/Game/GameLogic.cs @@ -34,6 +34,7 @@ public partial class GameLogic : IDisposable public int SelectedRow { get; private set; } public int SelectedCol { get; private set; } public FioTimer FioTimer { get; private set; } + public bool GameInProgress { get; private set; } // 게임 진행 중인지 확인 #endregion @@ -113,12 +114,18 @@ public partial class GameLogic : IDisposable StartGameOnMainThread(); break; case Constants.MultiplayManagerState.ExitRoom: - Debug.Log("## Exit Room"); + Debug.Log("## Exit Room"); // TODO: Exit Room 처리 break; case Constants.MultiplayManagerState.EndGame: Debug.Log("## End Game"); - // TODO: End Room 처리 + ExecuteOnMainThread(() => + { + GameManager.Instance.panelManager.OpenConfirmPanel("상대방이 방을 나갔습니다.", () => + { + // TODO: 무승부/항복 등 버튼 동작 안하도록 처리 + }); + }); break; case Constants.MultiplayManagerState.DoSurrender: Debug.Log("상대방의 항복 요청 들어옴"); @@ -213,6 +220,7 @@ public partial class GameLogic : IDisposable break; case Constants.MultiplayManagerState.ReceiveRevengeRequest: Debug.Log("상대방의 재대결 요청이 들어옴"); + ChangeGameInProgress(true); ExecuteOnMainThread(() => { GameManager.Instance.panelManager.OpenDrawConfirmPanel("상대방의 재대결 요청을\n승낙하시겠습니까?", () => @@ -288,7 +296,18 @@ public partial class GameLogic : IDisposable }); }); break; - } + case Constants.MultiplayManagerState.OpponentDisconnected: + Debug.Log("상대방 강제 종료"); + ExecuteOnMainThread(() => + { + GameManager.Instance.panelManager.OpenConfirmPanel("연결이 끊어졌습니다.", () => + { + GameManager.Instance.panelManager.OpenEffectPanel(Enums.GameResult.Win); + EndGame(Enums.GameResult.Win); + }); + }); + break; + } ReplayManager.Instance.InitReplayData(UserManager.Instance.Nickname,"nicknameB"); }); @@ -349,6 +368,9 @@ public partial class GameLogic : IDisposable // 메인스레드에서 게임 시작 private void StartGameOnMainThread() { + ChangeGameInProgress(true); + Debug.Log("GameInProgress 변경 true"); + ExecuteOnMainThread(() => { // 로딩 패널 열려있으면 닫기 @@ -497,6 +519,8 @@ public partial class GameLogic : IDisposable // 기존 멀티플레이 상태 초기화 MultiPlayManager = null; _roomId = null; + + GameType = Enums.GameType.SinglePlay; // 싱글 플레이 상태로 변경 InitializeSinglePlayMode(); @@ -560,10 +584,24 @@ public partial class GameLogic : IDisposable } #endregion + + public void ChangeGameInProgress(bool inProgress) + { + if (GameInProgress == inProgress) + return; + + GameInProgress = inProgress; + } public void Dispose() { MultiPlayManager?.LeaveRoom(_roomId); MultiPlayManager?.Dispose(); } + + public void ForceQuit() + { + MultiPlayManager?.ForceQuit(_roomId); + MultiPlayManager?.Dispose(); + } } diff --git a/Assets/Script/Game/GameManager.cs b/Assets/Script/Game/GameManager.cs index f34e129..10bc6fc 100644 --- a/Assets/Script/Game/GameManager.cs +++ b/Assets/Script/Game/GameManager.cs @@ -152,4 +152,15 @@ public class GameManager : Singleton _gameLogic.EndGame(Enums.GameResult.Draw); } + + private void OnApplicationQuit() + { + Debug.Log("앱 종료 감지: 소켓 연결 정리 중..."); + + if(_gameLogic.GameInProgress) + _gameLogic?.ForceQuit(); + else + _gameLogic?.Dispose(); + + } } \ No newline at end of file diff --git a/Assets/Script/Game/GameUtility/GameRoutine.cs b/Assets/Script/Game/GameUtility/GameRoutine.cs index b3148c3..fd79021 100644 --- a/Assets/Script/Game/GameUtility/GameRoutine.cs +++ b/Assets/Script/Game/GameUtility/GameRoutine.cs @@ -1,4 +1,6 @@ -public partial class GameLogic +using UnityEngine; + +public partial class GameLogic { // 돌 카운터 증가 함수 public void CountStoneCounter() => _totalStoneCounter++; @@ -85,6 +87,8 @@ SetState(null); ReplayManager.Instance.SaveReplayDataResult(result); //TODO: 게임 종료 후 행동 구현 + ChangeGameInProgress(false); + Debug.Log("GameInProgress 변경 false"); } public void SetLastPositioned(int row, int col) diff --git a/Assets/Script/Game/MultiplayManager.cs b/Assets/Script/Game/MultiplayManager.cs index 62a1a29..1e8c232 100644 --- a/Assets/Script/Game/MultiplayManager.cs +++ b/Assets/Script/Game/MultiplayManager.cs @@ -113,7 +113,9 @@ public class MultiplayManager : IDisposable _socket.On("revengeConfirmed", RevengeConfirmed); _socket.On("revengeRejected", RevengeRejected); _socket.On("revengeRejectionConfirmed", RevengeRejectionConfirmed); - + // 강제 종료 처리 + _socket.On("opponentDisconnected", OpponentDisconnected); + _socket.Connect(); } catch (Exception e) @@ -216,6 +218,18 @@ public class MultiplayManager : IDisposable _roomId = null; // 방 나가면 roomId 초기화 } + public void ForceQuit(string roomId) + { + if (string.IsNullOrEmpty(_roomId)) + { + Debug.LogError("Disconnect 호출 실패: _roomId가 설정되지 않음"); + return; + } + + _socket.Emit("disconnect", new { roomId = _roomId }); + _roomId = null; // 방 나가면 roomId 초기화 + } + public void RequestSurrender() { if (string.IsNullOrEmpty(_roomId)) @@ -337,6 +351,13 @@ public class MultiplayManager : IDisposable _onMultiplayStateChanged?.Invoke(Constants.MultiplayManagerState.DrawRejectionConfirmed, data.message); } + + private void DerawRejectionConfirmed(SocketIOResponse response) + { + var data = response.GetValue(); + + _onMultiplayStateChanged?.Invoke(Constants.MultiplayManagerState.DrawRejectionConfirmed, data.message); + } #endregion @@ -414,6 +435,13 @@ public class MultiplayManager : IDisposable _onMultiplayStateChanged?.Invoke(Constants.MultiplayManagerState.RevengeRejectionConfirmed, data.message); } + private void OpponentDisconnected(SocketIOResponse response) + { + var data = response.GetValue(); + + _onMultiplayStateChanged?.Invoke(Constants.MultiplayManagerState.OpponentDisconnected, data.message); + } + #endregion public void Dispose()