44#include <stdio.h>
55#include <stdlib.h>
66#include <string.h>
7+ #include <sys/time.h> // gettimeofday를 위해 추가
78#include <unistd.h>
89
910#include "config.h"
1011#include "logger.h"
1112#include "network.h"
1213#include "utils.h" // 체스판 초기화를 위해 추가
1314
15+ // 타이머 체크 스레드 관련 변수
16+ bool timer_thread_running = false;
17+ pthread_t timer_thread_id = 0 ;
18+
1419// 매칭 매니저 전역 인스턴스
1520MatchManager g_match_manager ;
1621
22+ // 밀리초 단위 현재 시간 가져오기
23+ int64_t get_current_time_ms (void ) {
24+ struct timeval tv ;
25+ gettimeofday (& tv , NULL );
26+ return (int64_t )tv .tv_sec * 1000 + tv .tv_usec / 1000 ;
27+ }
28+
1729// 매칭 매니저 초기화
1830int init_match_manager (void ) {
1931 memset (& g_match_manager , 0 , sizeof (MatchManager ));
@@ -26,12 +38,22 @@ int init_match_manager(void) {
2638 g_match_manager .waiting_count = 0 ;
2739 g_match_manager .active_game_count = 0 ;
2840
41+ // 타이머 체크 스레드 시작
42+ if (start_timer_thread () != 0 ) {
43+ LOG_ERROR ("Failed to start timer thread" );
44+ pthread_mutex_destroy (& g_match_manager .mutex );
45+ return -1 ;
46+ }
47+
2948 LOG_INFO ("Match manager initialized" );
3049 return 0 ;
3150}
3251
3352// 매칭 매니저 정리
3453void cleanup_match_manager (void ) {
54+ // 타이머 스레드 먼저 종료
55+ stop_timer_thread ();
56+
3557 pthread_mutex_lock (& g_match_manager .mutex );
3658
3759 // 대기 중인 플레이어들 정리
@@ -48,6 +70,150 @@ void cleanup_match_manager(void) {
4870 LOG_INFO ("Match manager cleaned up" );
4971}
5072
73+ // 타이머 체크 스레드 시작
74+ int start_timer_thread (void ) {
75+ timer_thread_running = true;
76+
77+ if (pthread_create (& timer_thread_id , NULL , timer_check_thread , NULL ) != 0 ) {
78+ LOG_ERROR ("Failed to create timer thread" );
79+ timer_thread_running = false;
80+ return -1 ;
81+ }
82+
83+ LOG_INFO ("Timer check thread started" );
84+ return 0 ;
85+ }
86+
87+ // 타이머 체크 스레드 종료
88+ void stop_timer_thread (void ) {
89+ if (timer_thread_running ) {
90+ timer_thread_running = false;
91+
92+ if (timer_thread_id != 0 ) {
93+ pthread_join (timer_thread_id , NULL );
94+ timer_thread_id = 0 ;
95+ LOG_INFO ("Timer check thread stopped" );
96+ }
97+ }
98+ }
99+
100+ // 타이머 체크 스레드 메인 함수 (100ms 간격으로 정밀하게 체크)
101+ void * timer_check_thread (void * arg ) {
102+ LOG_INFO ("Timer check thread started" );
103+
104+ while (timer_thread_running ) {
105+ check_game_timeouts ();
106+
107+ // 100ms마다 체크 (더 정밀한 타이머)
108+ usleep (100000 ); // 100ms = 100,000 microseconds
109+ }
110+
111+ LOG_INFO ("Timer check thread terminated" );
112+ return NULL ;
113+ }
114+
115+ // 게임 타이머 체크 및 시간 초과 처리 (밀리초 단위로 정밀도 향상)
116+ void check_game_timeouts (void ) {
117+ int64_t current_time_ms = get_current_time_ms ();
118+
119+ pthread_mutex_lock (& g_match_manager .mutex );
120+
121+ for (int i = 0 ; i < MAX_ACTIVE_GAMES ; i ++ ) {
122+ ActiveGame * game = & g_match_manager .active_games [i ];
123+
124+ if (!game -> is_active ) {
125+ continue ;
126+ }
127+
128+ // 마지막 타이머 체크 이후 경과 시간만 차감 (중복 차감 방지)
129+ int64_t time_since_last_check_ms = current_time_ms - game -> last_timer_check_ms ;
130+
131+ // 100ms 미만이면 건너뜀 (너무 빈번한 업데이트 방지)
132+ if (time_since_last_check_ms < 100 ) {
133+ continue ;
134+ }
135+
136+ team_t current_player = game -> game_state .side_to_move ;
137+
138+ bool timeout_occurred = false;
139+ Team timeout_winner = TEAM__TEAM_UNSPECIFIED ;
140+ const char * timeout_player_id = NULL ;
141+
142+ // 현재 턴인 플레이어의 시간만 차감 (밀리초 단위)
143+ if (current_player == TEAM_WHITE ) {
144+ // 백 팀 턴인 경우
145+ game -> white_time_remaining -= (int32_t )time_since_last_check_ms ;
146+ if (game -> white_time_remaining <= 0 ) {
147+ LOG_INFO ("White player timeout in game %s (elapsed_ms: %ld, remaining was: %d ms)" ,
148+ game -> game_id , time_since_last_check_ms , game -> white_time_remaining + (int32_t )time_since_last_check_ms );
149+ timeout_occurred = true;
150+ timeout_winner = TEAM__TEAM_BLACK ;
151+ timeout_player_id = game -> white_player_id ;
152+ game -> white_time_remaining = 0 ;
153+ }
154+ } else {
155+ // 흑 팀 턴인 경우
156+ game -> black_time_remaining -= (int32_t )time_since_last_check_ms ;
157+ if (game -> black_time_remaining <= 0 ) {
158+ LOG_INFO ("Black player timeout in game %s (elapsed_ms: %ld, remaining was: %d ms)" ,
159+ game -> game_id , time_since_last_check_ms , game -> black_time_remaining + (int32_t )time_since_last_check_ms );
160+ timeout_occurred = true;
161+ timeout_winner = TEAM__TEAM_WHITE ;
162+ timeout_player_id = game -> black_player_id ;
163+ game -> black_time_remaining = 0 ;
164+ }
165+ }
166+
167+ // 마지막 체크 시간 업데이트
168+ game -> last_timer_check_ms = current_time_ms ;
169+
170+ if (timeout_occurred ) {
171+ LOG_INFO ("Game %s ended by timeout - winner: %s" ,
172+ game -> game_id ,
173+ (timeout_winner == TEAM__TEAM_WHITE ) ? "WHITE" : "BLACK" );
174+
175+ // 게임 종료 브로드캐스트 전송
176+ send_timeout_game_end_broadcast (game , timeout_player_id , timeout_winner );
177+
178+ // 게임 제거
179+ game -> is_active = false;
180+ g_match_manager .active_game_count -- ;
181+ }
182+ }
183+
184+ pthread_mutex_unlock (& g_match_manager .mutex );
185+ }
186+
187+ // 시간 초과로 인한 게임 종료 브로드캐스트 전송
188+ int send_timeout_game_end_broadcast (ActiveGame * game , const char * timeout_player_id , Team winner_team ) {
189+ ServerMessage game_end_msg = SERVER_MESSAGE__INIT ;
190+ GameEndBroadcast game_end_broadcast = GAME_END_BROADCAST__INIT ;
191+
192+ game_end_broadcast .game_id = game -> game_id ;
193+ game_end_broadcast .player_id = (char * )timeout_player_id ;
194+ game_end_broadcast .winner_team = winner_team ;
195+ game_end_broadcast .end_type = GAME_END_TYPE__GAME_END_TIMEOUT ;
196+
197+ game_end_msg .msg_case = SERVER_MESSAGE__MSG_GAME_END ;
198+ game_end_msg .game_end = & game_end_broadcast ;
199+
200+ // 양쪽 플레이어 모두에게 게임 종료 브로드캐스트 전송
201+ int result = 0 ;
202+ if (send_server_message (game -> white_player_fd , & game_end_msg ) < 0 ) {
203+ LOG_ERROR ("Failed to send timeout game end broadcast to white player fd=%d" , game -> white_player_fd );
204+ result = -1 ;
205+ }
206+ if (send_server_message (game -> black_player_fd , & game_end_msg ) < 0 ) {
207+ LOG_ERROR ("Failed to send timeout game end broadcast to black player fd=%d" , game -> black_player_fd );
208+ result = -1 ;
209+ }
210+
211+ LOG_INFO ("Sent timeout game end broadcast for game %s (timeout_player=%s, winner_team=%d)" ,
212+ game -> game_id , timeout_player_id , winner_team );
213+
214+ return result ;
215+ }
216+
51217// 게임 ID 생성
52218char * generate_game_id (void ) {
53219 static char game_id [GAME_ID_LENGTH + 1 ];
@@ -95,11 +261,12 @@ MatchResult add_player_to_matching(int fd, const char *player_id) {
95261 // 체스판 초기화 (표준 시작 위치)
96262 init_startpos (& game -> game_state );
97263
98- // 타이머 설정
99- game -> time_limit_per_player = DEFAULT_GAME_TIME_LIMIT ;
100- game -> white_time_remaining = DEFAULT_GAME_TIME_LIMIT ;
101- game -> black_time_remaining = DEFAULT_GAME_TIME_LIMIT ;
102- game -> last_move_time = game -> game_start_time ;
264+ // 타이머 설정 (밀리초 단위)
265+ game -> time_limit_per_player = DEFAULT_GAME_TIME_LIMIT * 1000 ; // 초를 밀리초로 변환
266+ game -> white_time_remaining = DEFAULT_GAME_TIME_LIMIT * 1000 ; // 초를 밀리초로 변환
267+ game -> black_time_remaining = DEFAULT_GAME_TIME_LIMIT * 1000 ; // 초를 밀리초로 변환
268+ game -> last_move_time_ms = get_current_time_ms ();
269+ game -> last_timer_check_ms = get_current_time_ms ();
103270
104271 if (current_is_white ) {
105272 game -> white_player_fd = fd ;
0 commit comments