@@ -67,17 +67,72 @@ local function action_rejoinedLobby(code, type, token)
6767 -- Update reconnect token
6868 reconnectToken = token
6969 lastLobbyCode = code
70+ MP .self_reconnect_countdown = nil
7071 MP .ACTIONS .sync_client ()
7172 MP .ACTIONS .lobby_info ()
7273 MP .UI .update_connection_status ()
74+ sendWarnMessage (" Reconnected to lobby!" , " MULTIPLAYER" )
75+ G .FUNCS .exit_overlay_menu ()
76+ MP .UI .UTILS .overlay_message (" Reconnected to lobby!" )
7377end
7478
75- local function action_enemyDisconnected ()
79+ -- Countdown state for disconnect overlays
80+ MP .enemy_disconnect_countdown = nil
81+ MP .self_reconnect_countdown = nil
82+
83+ -- Shared timeout handler for both countdowns
84+ local function handle_reconnect_timeout (message )
85+ G .FUNCS .exit_overlay_menu ()
86+ MP .LOBBY .connected = false
87+ if MP .LOBBY .code then MP .LOBBY .code = nil end
88+ reconnectToken = nil
89+ lastLobbyCode = nil
90+ MP .UI .update_connection_status ()
91+ if G .STAGE ~= G .STAGES .MAIN_MENU then
92+ MP .reset_game_states ()
93+ G .FUNCS .go_to_menu ()
94+ end
95+ MP .UI .UTILS .overlay_message (message )
96+ end
97+
98+ -- Hook into Game.update to tick countdown displays
99+ local _disconnect_gupdate = Game .update
100+ function Game :update (dt )
101+ if MP .enemy_disconnect_countdown then
102+ local remaining = math.max (0 , math.ceil (MP .enemy_disconnect_countdown .end_time - love .timer .getTime ()))
103+ MP .enemy_disconnect_countdown .display = remaining .. " s remaining"
104+ -- No client-side timeout needed: the server sends stopGame
105+ -- when the grace period expires, which handles the cleanup
106+ end
107+ if MP .self_reconnect_countdown then
108+ local remaining = math.max (0 , math.ceil (MP .self_reconnect_countdown .end_time - love .timer .getTime ()))
109+ MP .self_reconnect_countdown .display = remaining .. " s remaining"
110+ if remaining <= 0 then
111+ MP .self_reconnect_countdown = nil
112+ handle_reconnect_timeout (" Reconnection failed.\n Returning to main menu." )
113+ end
114+ end
115+ return _disconnect_gupdate (self , dt )
116+ end
117+
118+ local function action_enemyDisconnected (timeout )
119+ timeout = timeout or 60
76120 sendWarnMessage (" Opponent disconnected, waiting for reconnection..." , " MULTIPLAYER" )
77- MP .UI .UTILS .overlay_message (" Opponent disconnected,\n waiting for reconnection..." , true )
121+
122+ MP .enemy_disconnect_countdown = {
123+ end_time = love .timer .getTime () + timeout ,
124+ display = timeout .. " s remaining" ,
125+ }
126+
127+ MP .UI .UTILS .overlay_message_countdown (
128+ " Opponent disconnected,\n waiting for reconnection..." ,
129+ MP .enemy_disconnect_countdown ,
130+ true
131+ )
78132end
79133
80134local function action_enemyReconnected ()
135+ MP .enemy_disconnect_countdown = nil
81136 sendWarnMessage (" Opponent reconnected!" , " MULTIPLAYER" )
82137 G .FUNCS .exit_overlay_menu ()
83138 MP .UI .UTILS .overlay_message (" Opponent reconnected!" )
@@ -142,13 +197,34 @@ end
142197
143198local function action_disconnected ()
144199 MP .LOBBY .connected = false
200+ MP .self_reconnect_countdown = nil
145201 if MP .LOBBY .code then MP .LOBBY .code = nil end
146202 -- Clear reconnect state since all reconnection attempts failed
147203 reconnectToken = nil
148204 lastLobbyCode = nil
149205 MP .UI .update_connection_status ()
150206end
151207
208+ local function action_reconnecting ()
209+ -- Only show if we were in a lobby and don't already have a countdown running
210+ if reconnectToken and lastLobbyCode and not MP .self_reconnect_countdown then
211+ MP .LOBBY .connected = false
212+ MP .UI .update_connection_status ()
213+ sendWarnMessage (" Connection lost, attempting to reconnect..." , " MULTIPLAYER" )
214+
215+ MP .self_reconnect_countdown = {
216+ end_time = love .timer .getTime () + 60 ,
217+ display = " 60s remaining" ,
218+ }
219+
220+ MP .UI .UTILS .overlay_message_countdown (
221+ " Connection lost,\n attempting to reconnect..." ,
222+ MP .self_reconnect_countdown ,
223+ true
224+ )
225+ end
226+ end
227+
152228--- @param seed string
153229--- @param stake_str string
154230local function action_start_game (seed , stake_str )
@@ -251,6 +327,7 @@ local function action_enemy_info(score_str, hands_left_str, skips_str, lives_str
251327end
252328
253329local function action_stop_game ()
330+ MP .enemy_disconnect_countdown = nil
254331 if G .STAGE ~= G .STAGES .MAIN_MENU then
255332 G .FUNCS .go_to_menu ()
256333 MP .UI .update_connection_status ()
@@ -1128,12 +1205,14 @@ function Game:update(dt)
11281205 action_version ()
11291206 elseif parsedAction .action == " disconnected" then
11301207 action_disconnected ()
1208+ elseif parsedAction .action == " reconnecting" then
1209+ action_reconnecting ()
11311210 elseif parsedAction .action == " joinedLobby" then
11321211 action_joinedLobby (parsedAction .code , parsedAction .type , parsedAction .reconnectToken )
11331212 elseif parsedAction .action == " rejoinedLobby" then
11341213 action_rejoinedLobby (parsedAction .code , parsedAction .type , parsedAction .reconnectToken )
11351214 elseif parsedAction .action == " enemyDisconnected" then
1136- action_enemyDisconnected ()
1215+ action_enemyDisconnected (parsedAction . timeout )
11371216 elseif parsedAction .action == " enemyReconnected" then
11381217 action_enemyReconnected ()
11391218 elseif parsedAction .action == " lobbyInfo" then
0 commit comments