/** * @brief Callback for subsystem failures. Depending on the severity, we may try to * recover, or we may shut the entire engine down and exit. */ static void Error(err_t err, const char *msg) { if (quetoo.debug_mask & DEBUG_BREAKPOINT) { SDL_TriggerBreakpoint(); } Print(va("^1%s\n", msg)); if (err == ERROR_DROP && !jmp_set) { err = ERROR_FATAL; } switch (err) { case ERROR_DROP: Sv_ShutdownServer(msg); Cl_Disconnect(); quetoo.recursive_error = false; longjmp(env, err); break; case ERROR_FATAL: default: Sys_Backtrace(); Shutdown(msg); exit(err); break; } }
/* * @brief Entry point for spawning a new server or changing maps / demos. Brings any * connected clients along for the ride by broadcasting a reconnect before * clearing state. Special effort is made to ensure that a locally connected * client sees the reconnect message immediately. */ void Sv_InitServer(const char *server, sv_state_t state) { #ifdef BUILD_CLIENT extern void Cl_Disconnect(void); #endif char path[MAX_QPATH]; Com_Debug("Sv_InitServer: %s (%d)\n", server, state); Cbuf_CopyToDefer(); // ensure that the requested map or demo exists if (state == SV_ACTIVE_DEMO) g_snprintf(path, sizeof(path), "demos/%s.dem", server); else g_snprintf(path, sizeof(path), "maps/%s.bsp", server); if (!Fs_Exists(path)) { Com_Print("Couldn't open %s\n", path); return; } // inform any connected clients to reconnect to us Sv_ShutdownMessage("Server restarting...\n", true); #ifdef BUILD_CLIENT // disconnect any local client, they'll immediately reconnect Cl_Disconnect(); #endif // clear the sv_server_t structure Sv_ClearState(); Com_Print("Server initialization...\n"); // initialize the clients, loading the game module if we need it Sv_InitClients(); // load the map or demo and related media Sv_LoadMedia(server, state); sv.state = state; Sb_Init(&sv.multicast, sv.multicast_buffer, sizeof(sv.multicast_buffer)); Com_Print("Server initialized\n"); Com_InitSubsystem(Q2W_SERVER); svs.initialized = true; }
/* * @brief Re-send a connect message if the last one has timed out. */ static void Cl_CheckForResend(void) { // if the local server is running and we aren't then connect if (Com_WasInit(QUETOO_SERVER) && g_strcmp0(cls.server_name, "localhost")) { if (cls.state > CL_DISCONNECTED) { Cl_Disconnect(); } g_strlcpy(cls.server_name, "localhost", sizeof(cls.server_name)); cls.state = CL_CONNECTING; cls.connect_time = 0; } // re-send if we haven't received a reply yet if (cls.state != CL_CONNECTING) return; // don't flood connection packets if (cls.connect_time && (quetoo.time - cls.connect_time < 1000)) return; net_addr_t addr; if (!Net_StringToNetaddr(cls.server_name, &addr)) { Com_Print("Bad server address\n"); cls.state = CL_DISCONNECTED; return; } if (addr.port == 0) addr.port = htons(PORT_SERVER); cls.connect_time = quetoo.time; // for retransmit requests const char *s = Net_NetaddrToString(&addr); if (g_strcmp0(cls.server_name, s)) { Com_Print("Connecting to %s (%s)...\n", cls.server_name, s); } else { Com_Print("Connecting to %s...\n", cls.server_name); } Netchan_OutOfBandPrint(NS_UDP_CLIENT, &addr, "get_challenge\n"); }
/* * @brief */ static void Cl_Connect_f(void) { if (Cmd_Argc() != 2) { Com_Print("Usage: %s <address>\n", Cmd_Argv(0)); return; } if (Com_WasInit(QUETOO_SERVER)) { // if running a local server, kill it Sv_ShutdownServer("Server quit\n"); } Cl_Disconnect(); strncpy(cls.server_name, Cmd_Argv(1), sizeof(cls.server_name)); cls.server_name[sizeof(cls.server_name) - 1] = '\0'; cls.state = CL_CONNECTING; cls.connect_time = 0; // fire immediately }