From 2ecc4c4a07ca99cc301d0815e31f86ce1f307452 Mon Sep 17 00:00:00 2001 From: Chris Taylor Date: Mon, 9 Jan 2017 19:09:34 +0800 Subject: [PATCH 1/3] Remove call to window_close(Game_wind) when stopping demo playback Replace call to window_close(Game_wind) with returning window_event_result::close to game_handler. Applies to whenever newdemo_stop_playback() is called. Closing a window within its handler is problematic - it can result in an unstable state. --- common/main/newdemo.h | 7 ++-- similar/main/game.cpp | 9 +++-- similar/main/gamecntl.cpp | 16 ++++---- similar/main/newdemo.cpp | 83 +++++++++++++++++++++++++-------------- 4 files changed, 72 insertions(+), 43 deletions(-) diff --git a/common/main/newdemo.h b/common/main/newdemo.h index 1aa5f5552..4db162fd3 100644 --- a/common/main/newdemo.h +++ b/common/main/newdemo.h @@ -28,6 +28,7 @@ COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. #ifdef __cplusplus #include "physfsx.h" #include "fwd-object.h" +#include "fwd-window.h" class object_signature_t; @@ -140,13 +141,13 @@ extern void newdemo_record_secret_exit_blown(int truth); // Functions called during playback process... extern void newdemo_object_move_all(); -extern void newdemo_playback_one_frame(); +extern window_event_result newdemo_playback_one_frame(); #ifdef dsx namespace dsx { -extern void newdemo_goto_end(int to_rewrite); +extern window_event_result newdemo_goto_end(int to_rewrite); } #endif -extern void newdemo_goto_beginning(); +extern window_event_result newdemo_goto_beginning(); // Interactive functions to control playback/record; #ifdef dsx diff --git a/similar/main/game.cpp b/similar/main/game.cpp index f0a09a780..a8c38f897 100644 --- a/similar/main/game.cpp +++ b/similar/main/game.cpp @@ -1485,9 +1485,12 @@ window_event_result GameProcessFrame() flash_frame(); - if ( Newdemo_state == ND_STATE_PLAYBACK ) { - newdemo_playback_one_frame(); - if ( Newdemo_state != ND_STATE_PLAYBACK ) { + if ( Newdemo_state == ND_STATE_PLAYBACK ) + { + result = std::max(newdemo_playback_one_frame(), result); + if ( Newdemo_state != ND_STATE_PLAYBACK ) + { + Assert(result == window_event_result::close); return window_event_result::close; // Go back to menu } } diff --git a/similar/main/gamecntl.cpp b/similar/main/gamecntl.cpp index 622e65901..70aafbb86 100644 --- a/similar/main/gamecntl.cpp +++ b/similar/main/gamecntl.cpp @@ -458,7 +458,7 @@ static int HandleDeathInput(const d_event &event) return 0; } -static int HandleDemoKey(int key) +static window_event_result HandleDemoKey(int key) { switch (key) { KEY_MAC(case KEY_COMMAND+KEY_1:) @@ -487,6 +487,7 @@ static int HandleDemoKey(int key) break; } newdemo_stop_playback(); + return window_event_result::close; break; case KEY_UP: Newdemo_vcr_state = ND_STATE_PLAYBACK; @@ -503,10 +504,10 @@ static int HandleDemoKey(int key) Newdemo_vcr_state = ND_STATE_ONEFRAMEFORWARD; break; case KEY_CTRLED + KEY_RIGHT: - newdemo_goto_end(0); + return newdemo_goto_end(0); break; case KEY_CTRLED + KEY_LEFT: - newdemo_goto_beginning(); + return newdemo_goto_beginning(); break; KEY_MAC(case KEY_COMMAND+KEY_P:) @@ -574,10 +575,10 @@ static int HandleDemoKey(int key) #endif default: - return 0; + return window_event_result::ignored; } - return 1; + return window_event_result::handled; } #if defined(DXX_BUILD_DESCENT_II) @@ -1893,8 +1894,9 @@ window_event_result ReadControls(const d_event &event) } else if (Newdemo_state == ND_STATE_PLAYBACK ) { - if (HandleDemoKey(key)) - return window_event_result::handled; + auto r = HandleDemoKey(key); + if (r != window_event_result::ignored) + return r; } else { diff --git a/similar/main/newdemo.cpp b/similar/main/newdemo.cpp index f287d1061..3aa2c98a1 100644 --- a/similar/main/newdemo.cpp +++ b/similar/main/newdemo.cpp @@ -3269,7 +3269,7 @@ static int newdemo_read_frame_information(int rewrite) } } -void newdemo_goto_beginning() +window_event_result newdemo_goto_beginning() { //if (nd_playback_v_framecount == 0) // return; @@ -3283,10 +3283,13 @@ void newdemo_goto_beginning() newdemo_stop_playback(); Newdemo_vcr_state = ND_STATE_PAUSED; nd_playback_v_at_eof = 0; + + // check if we stopped playback + return Newdemo_state == ND_STATE_NORMAL ? window_event_result::close : window_event_result::handled; } namespace dsx { -void newdemo_goto_end(int to_rewrite) +window_event_result newdemo_goto_end(int to_rewrite) { short frame_length=0, byte_count=0, bshort=0; sbyte level=0, bbyte=0, c=0, cloaked=0; @@ -3302,7 +3305,7 @@ void newdemo_goto_end(int to_rewrite) nm_messagebox( NULL, 1, TXT_OK, "%s\n%s\n%s", TXT_CANT_PLAYBACK, TXT_LEVEL_CANT_LOAD, TXT_DEMO_OLD_CORRUPT ); Current_mission.reset(); newdemo_stop_playback(); - return; + return window_event_result::close; } if (level != Current_level_num) LoadLevel(level,1); @@ -3336,7 +3339,7 @@ void newdemo_goto_end(int to_rewrite) } if (to_rewrite) - return; + return window_event_result::handled; PHYSFSX_fseek(infile, -12, SEEK_END); nd_read_short(&frame_length); @@ -3435,7 +3438,7 @@ void newdemo_goto_end(int to_rewrite) } if (to_rewrite) - return; + return window_event_result::handled; PHYSFSX_fseek(infile, loc, SEEK_SET); } @@ -3446,11 +3449,11 @@ void newdemo_goto_end(int to_rewrite) Newdemo_vcr_state = ND_STATE_PLAYBACK; newdemo_read_frame_information(0); // then the frame information Newdemo_vcr_state = ND_STATE_PAUSED; - return; + return window_event_result::handled; } } -static void newdemo_back_frames(int frames) +static window_event_result newdemo_back_frames(int frames) { short last_frame_length; for (int i = 0; i < frames; i++) @@ -3461,7 +3464,7 @@ static void newdemo_back_frames(int frames) if (!nd_playback_v_at_eof && newdemo_read_frame_information(0) == -1) { newdemo_stop_playback(); - return; + return window_event_result::close; } if (nd_playback_v_at_eof) nd_playback_v_at_eof = 0; @@ -3471,6 +3474,7 @@ static void newdemo_back_frames(int frames) PHYSFS_seek(infile, PHYSFS_tell(infile) + 8 - last_frame_length); } + return window_event_result::handled; } /* @@ -3483,13 +3487,13 @@ static void newdemo_back_frames(int frames) * at. */ -static void interpolate_frame(fix d_play, fix d_recorded) +static window_event_result interpolate_frame(fix d_play, fix d_recorded) { fix factor; static fix InterpolStep = fl2f(.01); if (nd_playback_v_framecount < 1) - return; + return window_event_result::ignored; factor = fixdiv(d_play, d_recorded); if (factor > F1_0) @@ -3501,7 +3505,7 @@ static void interpolate_frame(fix d_play, fix d_recorded) Newdemo_vcr_state = ND_STATE_PAUSED; if (newdemo_read_frame_information(0) == -1) { newdemo_stop_playback(); - return; + return window_event_result::close; } InterpolStep -= FrameTime; @@ -3566,21 +3570,27 @@ static void interpolate_frame(fix d_play, fix d_recorded) // with Highest_object_index and the object array (previously rendered // objects, etc....) - newdemo_back_frames(1); - newdemo_back_frames(1); + auto result = newdemo_back_frames(1); + result = std::max(newdemo_back_frames(1), result); if (newdemo_read_frame_information(0) == -1) + { newdemo_stop_playback(); + result = window_event_result::close; + } Newdemo_vcr_state = ND_STATE_PLAYBACK; std::copy(cur_objs.begin(), cur_objs.begin() + num_cur_objs, Objects.begin()); Objects.set_count(num_cur_objs); + + return result; } -void newdemo_playback_one_frame() +window_event_result newdemo_playback_one_frame() { int frames_back; static fix base_interpol_time = 0; static fix d_recorded = 0; + auto result = window_event_result::handled; range_for (auto &i, Players) { @@ -3593,7 +3603,7 @@ void newdemo_playback_one_frame() } if (Newdemo_vcr_state == ND_STATE_PAUSED) // render a frame or not - return; + return window_event_result::ignored; Control_center_destroyed = 0; Countdown_seconds_left = -1; @@ -3603,10 +3613,9 @@ void newdemo_playback_one_frame() { const int level = Current_level_num; if (nd_playback_v_framecount == 0) - return; + return window_event_result::ignored; else if ((Newdemo_vcr_state == ND_STATE_REWINDING) && (nd_playback_v_framecount < 10)) { - newdemo_goto_beginning(); - return; + return newdemo_goto_beginning(); } if (Newdemo_vcr_state == ND_STATE_REWINDING) frames_back = 10; @@ -3615,14 +3624,14 @@ void newdemo_playback_one_frame() if (nd_playback_v_at_eof) { PHYSFS_seek(infile, PHYSFS_tell(infile) + (shareware ? -2 : +11)); } - newdemo_back_frames(frames_back); + result = newdemo_back_frames(frames_back); if (level != Current_level_num) newdemo_pop_ctrlcen_triggers(); if (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD) { if (level != Current_level_num) - newdemo_back_frames(1); + result = std::max(newdemo_back_frames(1), result); Newdemo_vcr_state = ND_STATE_PAUSED; } } @@ -3636,7 +3645,10 @@ void newdemo_playback_one_frame() if (nd_playback_v_at_eof) Newdemo_vcr_state = ND_STATE_PAUSED; else + { newdemo_stop_playback(); + result = window_event_result::close; + } break; } } @@ -3649,12 +3661,18 @@ void newdemo_playback_one_frame() const int level = Current_level_num; if (newdemo_read_frame_information(0) == -1) { if (!nd_playback_v_at_eof) + { newdemo_stop_playback(); + result = window_event_result::close; + } } if (level != Current_level_num) { if (newdemo_read_frame_information(0) == -1) { if (!nd_playback_v_at_eof) + { newdemo_stop_playback(); + result = window_event_result::close; + } } } Newdemo_vcr_state = ND_STATE_PAUSED; @@ -3703,11 +3721,14 @@ void newdemo_playback_one_frame() const int level = Current_level_num; if (newdemo_read_frame_information(0) == -1) { newdemo_stop_playback(); - return; + return window_event_result::close; } if (level != Current_level_num) { if (newdemo_read_frame_information(0) == -1) + { newdemo_stop_playback(); + result = window_event_result::close; + } break; } @@ -3732,24 +3753,25 @@ void newdemo_playback_one_frame() } d_play = nd_playback_total - base_interpol_time; - interpolate_frame(d_play, d_recorded); - return; + return std::max(interpolate_frame(d_play, d_recorded), result); } else { if (newdemo_read_frame_information(0) == -1) { newdemo_stop_playback(); - return; + return window_event_result::close; } if (nd_playback_v_style == SKIP_PLAYBACK) { while (nd_playback_total > nd_recorded_total) { if (newdemo_read_frame_information(0) == -1) { newdemo_stop_playback(); - return; + return window_event_result::close; } } } } } + + return result; } void newdemo_start_recording() @@ -4083,8 +4105,12 @@ void newdemo_start_playback(const char * filename) HUD_clear_messages(); if (!Game_wind) hide_menus(); - newdemo_playback_one_frame(); // this one loads new level - newdemo_playback_one_frame(); // get all of the objects to renderb game + auto result = newdemo_playback_one_frame(); // this one loads new level + result = std::max(newdemo_playback_one_frame(), result); // get all of the objects to renderb game + + if (result == window_event_result::close) + return; // whoops, there was an error reading the first two frames! Abort! + if (!Game_wind) Game_wind = game_setup(); // create game environment } @@ -4107,9 +4133,6 @@ void newdemo_stop_playback() // Required for the editor obj_relink_all(); - - if (Game_wind) - window_close(Game_wind); // Exit game loop } } From a418f8caecde941d4d605ef511f79ae8224551e8 Mon Sep 17 00:00:00 2001 From: Chris Taylor Date: Tue, 10 Jan 2017 13:02:59 +0800 Subject: [PATCH 2/3] Remove call to window_close(Game_wind) when multiplayer level syncing fails Replace call to window_close(Game_wind) with returning window_event_result::close to game_handler. Applies to when there is a failure in net_udp_level_sync(). Closing a window within its handler is problematic - it can result in an unstable state. --- common/main/gameseq.h | 2 +- common/main/multi.h | 2 +- common/main/net_udp.h | 2 +- common/main/state.h | 9 +++++---- similar/main/gameseq.cpp | 18 ++++++++++-------- similar/main/multi.cpp | 4 +++- similar/main/net_udp.cpp | 19 ++++++------------- 7 files changed, 27 insertions(+), 29 deletions(-) diff --git a/common/main/gameseq.h b/common/main/gameseq.h index fb9359efb..222d4b6b5 100644 --- a/common/main/gameseq.h +++ b/common/main/gameseq.h @@ -60,7 +60,7 @@ namespace dsx { void StartNewGame(int start_level); // starts the next level -void StartNewLevel(int level_num); +window_event_result StartNewLevel(int level_num); } #endif diff --git a/common/main/multi.h b/common/main/multi.h index 02bd3aaf6..a31c618b9 100644 --- a/common/main/multi.h +++ b/common/main/multi.h @@ -559,7 +559,7 @@ void multi_add_lifetime_kills(int count); void multi_send_bounty( void ); void multi_consistency_error(int reset); -int multi_level_sync(void); +window_event_result multi_level_sync(); int multi_endlevel(int *secret); using multi_endlevel_poll = int(newmenu *menu,const d_event &event, const unused_newmenu_userdata_t *); multi_endlevel_poll *get_multi_endlevel_poll2(); diff --git a/common/main/net_udp.h b/common/main/net_udp.h index 2e92181d8..0a1817a3f 100644 --- a/common/main/net_udp.h +++ b/common/main/net_udp.h @@ -44,7 +44,7 @@ int net_udp_kmatrix_poll2( newmenu *menu,const d_event &event, const unused_newm void net_udp_send_endlevel_packet(); void net_udp_dump_player(const _sockaddr &dump_addr, int why); void net_udp_disconnect_player(int playernum); -int net_udp_level_sync(); +window_event_result net_udp_level_sync(); void net_udp_send_mdata_direct(const ubyte *data, int data_len, int pnum, int priority); void net_udp_send_netgame_update(); diff --git a/common/main/state.h b/common/main/state.h index 223fd642c..924d9e03a 100644 --- a/common/main/state.h +++ b/common/main/state.h @@ -34,6 +34,7 @@ COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. #ifdef __cplusplus #include #include "dsx-ns.h" +#include "fwd-window.h" extern unsigned state_game_id; extern int state_quick_item; @@ -101,11 +102,11 @@ static inline int state_restore_all(int in_game, secret_restore, std::nullptr_t, { return state_restore_all(in_game, nullptr, blind); } -void StartNewLevelSub(int level_num, int page_in_textures); +window_event_result StartNewLevelSub(int level_num, int page_in_textures); // Actually does the work to start new level -static inline void StartNewLevelSub(int level_num, int page_in_textures, secret_restore) +static inline window_event_result StartNewLevelSub(int level_num, int page_in_textures, secret_restore) { - StartNewLevelSub(level_num, page_in_textures); + return StartNewLevelSub(level_num, page_in_textures); } void init_player_stats_level(); static inline void init_player_stats_level(secret_restore) @@ -117,7 +118,7 @@ int state_restore_all_sub(const char *filename, secret_restore); void set_pos_from_return_segment(void); int state_save_all(secret_save, blind_save); int state_restore_all(int in_game, secret_restore, const char *filename_override, blind_save); -void StartNewLevelSub(int level_num, int page_in_textures, secret_restore); +window_event_result StartNewLevelSub(int level_num, int page_in_textures, secret_restore); void init_player_stats_level(secret_restore); #endif } diff --git a/similar/main/gameseq.cpp b/similar/main/gameseq.cpp index 7cdac3b2c..5a12500ad 100644 --- a/similar/main/gameseq.cpp +++ b/similar/main/gameseq.cpp @@ -1450,7 +1450,7 @@ static window_event_result AdvanceLevel(int secret_flag) } // END NMN #endif - StartNewLevel(Next_level_num); + rval = std::max(StartNewLevel(Next_level_num), rval); } return rval; @@ -1563,9 +1563,9 @@ window_event_result DoPlayerDead() //called when the player is starting a new level for normal game mode and restore state // secret_flag set if came from a secret level #if defined(DXX_BUILD_DESCENT_I) -void StartNewLevelSub(const int level_num, const int page_in_textures) +window_event_result StartNewLevelSub(const int level_num, const int page_in_textures) #elif defined(DXX_BUILD_DESCENT_II) -void StartNewLevelSub(const int level_num, const int page_in_textures, const secret_restore secret_flag) +window_event_result StartNewLevelSub(const int level_num, const int page_in_textures, const secret_restore secret_flag) #endif { if (!(Game_mode & GM_MULTI)) { @@ -1597,11 +1597,11 @@ void StartNewLevelSub(const int level_num, const int page_in_textures, const sec if (Game_mode & GM_NETWORK) { - multi_prep_level_objects(); - if(multi_level_sync()) // After calling this, Player_num is set + multi_prep_level_objects(); + if (multi_level_sync() == window_event_result::close) // After calling this, Player_num is set { songs_play_song( SONG_TITLE, 1 ); // level song already plays but we fail to start level... - return; + return window_event_result::close; } } @@ -1690,6 +1690,8 @@ void StartNewLevelSub(const int level_num, const int page_in_textures, const sec if (!Game_wind) game(); + + return window_event_result::handled; } } @@ -1799,7 +1801,7 @@ static void maybe_set_first_secret_visit(int level_num) //called when the player is starting a new level for normal game model // secret_flag if came from a secret level namespace dsx { -void StartNewLevel(int level_num) +window_event_result StartNewLevel(int level_num) { hide_menus(); @@ -1818,7 +1820,7 @@ void StartNewLevel(int level_num) ShowLevelIntro(level_num); #endif - StartNewLevelSub(level_num, 1, secret_restore::none); + return StartNewLevelSub(level_num, 1, secret_restore::none); } } diff --git a/similar/main/multi.cpp b/similar/main/multi.cpp index cdf8818a1..5908f249e 100644 --- a/similar/main/multi.cpp +++ b/similar/main/multi.cpp @@ -3445,7 +3445,7 @@ void multi_prep_level_player(void) } -int multi_level_sync(void) +window_event_result multi_level_sync(void) { switch (multi_protocol) { @@ -3458,6 +3458,8 @@ int multi_level_sync(void) Error("Protocol handling missing in multi_level_sync\n"); break; } + + return window_event_result::ignored; } namespace dsx { diff --git a/similar/main/net_udp.cpp b/similar/main/net_udp.cpp index ecdd8258b..0c687a41a 100644 --- a/similar/main/net_udp.cpp +++ b/similar/main/net_udp.cpp @@ -4330,11 +4330,8 @@ static int net_udp_start_game(void) Netgame.protocol.udp.your_index = 0; // I am Host. I need to know that y'know? For syncing later. - if(net_udp_select_players()) - { - StartNewLevel(Netgame.levelnum); - } - else + if (!net_udp_select_players() + || StartNewLevel(Netgame.levelnum) == window_event_result::close) { Game_mode = GM_GAME_OVER; return 0; // see if we want to tweak the game we setup @@ -4447,7 +4444,7 @@ menu: } /* Do required syncing after each level, before starting new one */ -int net_udp_level_sync() +window_event_result net_udp_level_sync() { int result = 0; @@ -4471,13 +4468,11 @@ int net_udp_level_sync() { get_local_player().connected = CONNECT_DISCONNECTED; net_udp_send_endlevel_packet(); - if (Game_wind) - window_close(Game_wind); show_menus(); net_udp_close(); - return -1; + return window_event_result::close; } - return(0); + return window_event_result::handled; } namespace dsx { @@ -4537,9 +4532,7 @@ int net_udp_do_join_game() net_udp_set_game_mode(Netgame.gamemode); - StartNewLevel(Netgame.levelnum); - - return 1; // look ma, we're in a game!!! + return StartNewLevel(Netgame.levelnum) == window_event_result::handled; // look ma, we're in a game!!! (If level syncing didn't fail -kreatordxx) } } From 0c5a000328058e18791aabc22d055cd27b6d2f61 Mon Sep 17 00:00:00 2001 From: Chris Taylor Date: Tue, 10 Jan 2017 14:10:39 +0800 Subject: [PATCH 3/3] Remove call to window_close(Game_wind) when a network event tells us to end the game In multi_do_frame(), replace call to window_close(Game_wind) with returning window_event_result::close whenever multi_quit_game is true. Only using this return value where multi_do_frame() is directly called by GameProcessFrame(). multi_quit_game will only be set back to 0 when a new multi game is started. Closing a window within its handler is problematic - it can result in an unstable state. --- common/main/multi.h | 2 +- similar/main/game.cpp | 10 ++-------- similar/main/multi.cpp | 11 +++-------- 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/common/main/multi.h b/common/main/multi.h index a31c618b9..c1fbb1554 100644 --- a/common/main/multi.h +++ b/common/main/multi.h @@ -502,7 +502,7 @@ void multi_do_ping_frame(); void multi_init_objects(void); void multi_do_protocol_frame(int force, int listen); -void multi_do_frame(void); +window_event_result multi_do_frame(); #ifdef dsx namespace dsx { diff --git a/similar/main/game.cpp b/similar/main/game.cpp index a8c38f897..a1d611d48 100644 --- a/similar/main/game.cpp +++ b/similar/main/game.cpp @@ -1208,7 +1208,6 @@ namespace dsx { // Event handler for the game window_event_result game_handler(window *,const d_event &event, const unused_window_userdata_t *) { - const bool was_game_wind = Game_wind != nullptr; auto result = window_event_result::ignored; switch (event.type) @@ -1275,7 +1274,7 @@ window_event_result game_handler(window *,const d_event &event, const unused_win break; case EVENT_WINDOW_CLOSE: - Game_wind = nullptr; // Don't let any of the code below close Game_wind - it's already being done! + Game_wind = nullptr; digi_stop_digi_sounds(); if ( (Newdemo_state == ND_STATE_RECORDING) || (Newdemo_state == ND_STATE_PAUSED) ) @@ -1303,11 +1302,6 @@ window_event_result game_handler(window *,const d_event &event, const unused_win break; } - // If we deleted the window ***in this call of the handler***, tell the event loop - // Will be removed when no cases of this left - if (!Game_wind && was_game_wind) - result = window_event_result::deleted; - return result; } @@ -1444,7 +1438,7 @@ window_event_result GameProcessFrame() if (Game_mode & GM_MULTI) { - multi_do_frame(); + result = std::max(multi_do_frame(), result); if (Netgame.PlayTimeAllowed && ThisLevelTime>=i2f((Netgame.PlayTimeAllowed*5*60))) multi_check_for_killgoal_winner(); } diff --git a/similar/main/multi.cpp b/similar/main/multi.cpp index 5908f249e..f99bb058c 100644 --- a/similar/main/multi.cpp +++ b/similar/main/multi.cpp @@ -983,7 +983,7 @@ void multi_do_protocol_frame(int force, int listen) } } -void multi_do_frame(void) +window_event_result multi_do_frame() { static int lasttime=0; static fix64 last_gmode_time = 0, last_inventory_time = 0, last_repo_time = 0; @@ -992,7 +992,7 @@ void multi_do_frame(void) if (!(Game_mode & GM_MULTI) || Newdemo_state == ND_STATE_PLAYBACK) { Int3(); - return; + return window_event_result::ignored; } if ((Game_mode & GM_NETWORK) && Netgame.PlayTimeAllowed && lasttime!=f2i (ThisLevelTime)) @@ -1041,12 +1041,7 @@ void multi_do_frame(void) multi_do_protocol_frame(0, 1); - if (multi_quit_game) - { - multi_quit_game = 0; - if (Game_wind) - window_close(Game_wind); - } + return multi_quit_game ? window_event_result::close : window_event_result::handled; } void _multi_send_data(const ubyte *buf, unsigned len, int priority)