/** * @brief Sets the @c cls.state to @c ca_disconnected and informs the server * @sa CL_Drop * @note Goes from a connected state to disconnected state * Sends a disconnect message to the server * This is also called on @c Com_Error, so it shouldn't cause any errors */ void CL_Disconnect (void) { if (cls.state < ca_connecting) return; Com_Printf("Disconnecting...\n"); /* send a disconnect message to the server */ if (!Com_ServerState()) { dbuffer msg; NET_WriteByte(&msg, clc_stringcmd); NET_WriteString(&msg, NET_STATE_DISCONNECT "\n"); NET_WriteMsg(cls.netStream, msg); /* make sure, that this is send */ NET_Wait(0); } NET_StreamFinished(cls.netStream); cls.netStream = nullptr; CL_ClearState(); S_Stop(); R_ShutdownModels(false); R_FreeWorldImages(); CL_SetClientState(ca_disconnected); CL_ClearBattlescapeEvents(); GAME_EndBattlescape(); }
/** * @note Both client and server can use this, and it will * do the appropriate things. */ void Com_Error (int code, const char* fmt, ...) { va_list argptr; static char msg[MAXPRINTMSG]; static bool recursive = false; if (recursive) Sys_Error("recursive error after: %s", msg); recursive = true; va_start(argptr, fmt); Q_vsnprintf(msg, sizeof(msg), fmt, argptr); va_end(argptr); switch (code) { case ERR_DISCONNECT: Com_Printf("%s\n", msg); CL_Drop(); recursive = false; Com_Drop(); case ERR_DROP: Com_Printf("********************\n"); Com_Printf("ERROR: %s\n", msg); Com_Printf("********************\n"); Sys_Backtrace(); SV_Shutdown("Server crashed.", false); CL_Drop(); recursive = false; Com_Drop(); default: Com_Printf("%s\n", msg); SV_Shutdown("Server fatal crashed", false); /* send an receive net messages a last time */ NET_Wait(0); FS_CloseFile(&logfile); if (pipefile.f != nullptr) { FS_CloseFile(&pipefile); FS_RemoveFile(va("%s/%s", FS_Gamedir(), pipefile.name)); } CL_Shutdown(); Qcommon_Shutdown(); Sys_Error("Shutdown"); } }
/** * Both client and server can use this, and it will * do the appropriate things. */ void Com_Quit (void) { #ifdef DEDICATED_ONLY Com_WriteConfigToFile("dedconfig.cfg"); #else Com_WriteConfigToFile("config.cfg"); #endif SV_Shutdown("Server quit.", false); SV_Clear(); CL_Shutdown(); /* send an receive net messages a last time */ NET_Wait(0); FS_CloseFile(&logfile); if (pipefile.f != nullptr) { FS_CloseFile(&pipefile); FS_RemoveFile(va("%s/%s", FS_Gamedir(), pipefile.name)); } Sys_Quit(); }
/** * @brief Used by SV_Shutdown to send a final message to all * connected clients before the server goes down. * @sa SV_Shutdown */ static void SV_FinalMessage (const char *message, bool reconnect) { client_t *cl; dbuffer msg(2 + strlen(message)); if (reconnect) NET_WriteByte(&msg, svc_reconnect); else NET_WriteByte(&msg, svc_disconnect); NET_WriteString(&msg, message); cl = NULL; while ((cl = SV_GetNextClient(cl)) != NULL) if (cl->state >= cs_connected) { NET_WriteConstMsg(cl->stream, msg); NET_StreamFinished(cl->stream); cl->stream = NULL; } /* make sure, that this is send */ NET_Wait(0); }
/** * @brief This is the function that is called directly from main() * @sa main * @sa Qcommon_Init * @sa Qcommon_Shutdown * @sa SV_Frame * @sa CL_Frame */ void Qcommon_Frame (void) { try { /* If the next event is due... */ ScheduleEventPtr event = Dequeue_Event(Sys_Milliseconds()); if (event) { /* Dispatch the event */ event->func(event->when, event->data); } } catch (comRestart_t const& restart) { SV_Shutdown("Restart.", false); CL_Shutdown(); Qcommon_Shutdown(); CL_FilterEventQueue(&Event_FilterAll); if (restart.gamedir != nullptr) { const char* restartArgv[] = {"", "+set", "fs_gamedir", restart.gamedir}; Qcommon_Init(4, const_cast<char** >(restartArgv)); } else { Qcommon_Init(0, nullptr); } } catch (comDrop_t const&) { return; } /* Now we spend time_to_next milliseconds working on whatever * IO is ready (but always try at least once, to make sure IO * doesn't stall) */ int time_to_next; do { time_to_next = !eventQueue.empty() ? (eventQueue.begin()->get()->when - Sys_Milliseconds()) : 1000; if (time_to_next < 0) time_to_next = 0; NET_Wait(time_to_next); } while (time_to_next > 0); }