#include "quakedef.h"#include "q1_palette.h" /* * A server can always be started, even if the system started out as a client * to a remote system. * * A client can NOT be started if the system started as a dedicated server. * * Memory is cleared / released when a server or client begins, not when they end. */bool dedicated; bool host_initialized; // true if into command execution double host_frametime;double host_time;double realtime; // without any filtering or boundingu64int host_framecount; client_t *host_client; // current client jmp_buf host_abortserver; const byte *host_basepal;const byte *host_colormap; cvar_t sys_ticrate = {"sys_ticrate", "0.05"};cvar_t fraglimit = {"fraglimit", "0", false, true};cvar_t timelimit = {"timelimit", "0", false, true};cvar_t teamplay = {"teamplay", "0", false, true};cvar_t skill = {"skill", "1"}; // 0 - 3cvar_t deathmatch = {"deathmatch", "0"}; // 0, 1, or 2cvar_t coop = {"coop", "0"}; // 0 or 1cvar_t pausable = {"pausable", "1"}; static double oldrealtime; // last frame runstatic void *host_hunklevel; static cvar_t host_framerate = {"host_framerate", "0"}; // set for slow motionstatic cvar_t host_speeds = {"host_speeds", "0"}; // set for running timesstatic cvar_t serverprofile = {"serverprofile", "0"};static cvar_t samelevel = {"samelevel", "0"};static cvar_t noexit = {"noexit", "0", false, true};static cvar_t temp1 = {"temp1", "0"}; voidHost_EndGame(char *fmt, ...){ va_list arg; char s[1024]; va_start(arg, fmt); vsnprint(s, sizeof(s), fmt, arg); va_end(arg); Con_DPrintf("Host_EndGame: %s\n", s); if(sv.active) Host_ShutdownServer(false); if(cls.state == ca_dedicated) fatal("Host_EndGame: %s\n", s); // dedicated servers exit if(!CL_NextDemo()) CL_Disconnect(); longjmp(host_abortserver, 1);} /* This shuts down both the client and server */_Noreturn voidHost_Error(char *fmt, ...){ static bool inerror = false; va_list arg; char s[1024]; va_start(arg, fmt); vsnprint(s, sizeof(s), fmt, arg); va_end(arg); if(inerror) { assert(nil); fatal("Host_Error: recursively entered: %s", s); } inerror = true; SCR_EndLoadingPlaque(); // reenable screen updates Con_Printf("Host_Error: %s\n", s); if(sv.active) Host_ShutdownServer(false); if(cls.state == ca_dedicated) fatal("Host_Error: %s\n", s); // dedicated servers exit CL_Disconnect(); cls.demonum = -1; inerror = false; longjmp(host_abortserver, 1);} static voidHost_FindMaxClients(void){ svs.maxclients = 1; cls.state = ca_disconnected; if(dedicated) { cls.state = ca_dedicated; svs.maxclients = MAX_SCOREBOARD; } if(svs.maxclients < 1) svs.maxclients = 8; else if(svs.maxclients > MAX_SCOREBOARD) svs.maxclients = MAX_SCOREBOARD; svs.maxclientslimit = svs.maxclients; if(svs.maxclientslimit < 4) svs.maxclientslimit = 4; svs.clients = Hunk_Alloc(svs.maxclientslimit * sizeof(*svs.clients)); if(svs.maxclients > 1) setcvarv("deathmatch", 1.0); else setcvarv("deathmatch", 0.0);} static voidHost_InitLocal(void){ Host_InitCommands(); Cvar_RegisterVariable(&host_framerate); Cvar_RegisterVariable(&host_speeds); Cvar_RegisterVariable(&sys_ticrate); Cvar_RegisterVariable(&serverprofile); Cvar_RegisterVariable(&fraglimit); Cvar_RegisterVariable(&timelimit); Cvar_RegisterVariable(&teamplay); Cvar_RegisterVariable(&samelevel); Cvar_RegisterVariable(&noexit); Cvar_RegisterVariable(&skill); Cvar_RegisterVariable(&deathmatch); Cvar_RegisterVariable(&coop); Cvar_RegisterVariable(&pausable); Cvar_RegisterVariable(&temp1); Host_FindMaxClients(); host_time = 1.0; // so a think at time 0 won't get called} /* Sends text across to be displayed. FIXME: make this just a stuffed echo? */voidSV_ClientPrintf(const char *fmt, ...){ va_list arg; char s[1024]; va_start(arg, fmt); vsnprint(s, sizeof(s), fmt, arg); va_end(arg); MSG_WriteByte(&host_client->message, svc_print); MSG_WriteString(&host_client->message, s);} /* Sends text to all active clients */voidSV_BroadcastPrintf(const char *fmt, ...){ va_list arg; char s[1024]; int i; va_start(arg, fmt); vsnprint(s, sizeof(s), fmt, arg); va_end(arg); for(i = 0; i < svs.maxclients; i++) { if(svs.clients[i].active && svs.clients[i].spawned) { MSG_WriteByte(&svs.clients[i].message, svc_print); MSG_WriteString(&svs.clients[i].message, s); } }} /* Send text over to the client to be executed */voidHost_ClientCommands(char *fmt, ...){ va_list arg; char s[1024]; va_start(arg, fmt); vsnprint(s, sizeof(s), fmt, arg); va_end(arg); MSG_WriteByte(&host_client->message, svc_stufftext); MSG_WriteString(&host_client->message, s);} /* * Called when the player is getting totally kicked off the host * if (crash = true), don't bother sending signofs */voidSV_DropClient(bool crash){ int saveSelf, i; client_t *client; if(!crash) { // send any final messages (don't check for errors) if(NET_CanSendMessage(host_client->netconnection)) { MSG_WriteByte(&host_client->message, svc_disconnect); NET_SendMessage(host_client->netconnection, &host_client->message); } if(host_client->edict && host_client->spawned) { // call the prog function for removing a client // this will set the body to a dead frame, among other things saveSelf = sv.pr->global_struct->self; sv.pr->global_struct->self = EDICT_TO_PROG(sv.pr, host_client->edict); PR_ExecuteProgram(sv.pr, sv.pr->global_struct->ClientDisconnect); sv.pr->global_struct->self = saveSelf; } Con_DPrintf("client %s removed\n", host_client->name); } // break the net connection NET_Close(host_client->netconnection); host_client->netconnection = nil; // free the client (the body stays around) host_client->active = false; host_client->name[0] = 0; host_client->old_frags = Q_MININT; net_activeconnections--; // send notification to all clients for(i = 0, client = svs.clients; i < svs.maxclients; i++, client++) { if(!client->active) continue; MSG_WriteByte(&client->message, svc_updatename); MSG_WriteByte(&client->message, host_client - svs.clients); MSG_WriteString(&client->message, ""); MSG_WriteByte(&client->message, svc_updatefrags); MSG_WriteByte(&client->message, host_client - svs.clients); MSG_WriteShort(&client->message, 0); MSG_WriteByte(&client->message, svc_updatecolors); MSG_WriteByte(&client->message, host_client - svs.clients); MSG_WriteByte(&client->message, 0); }} /* This only happens at the end of a game, not between levels */voidHost_ShutdownServer(bool crash){ int i, count; sizebuf_t buf; char message[4]; double start; if(!sv.active) return; UDP_Listen(0); sv.active = false; // stop all client sounds immediately if(cls.state == ca_connected) CL_Disconnect(); // flush any pending messages - like the score!!! start = dtime(); do { count = 0; for(i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++) { if(host_client->active && host_client->message.cursize) { if(NET_CanSendMessage(host_client->netconnection)) { NET_SendMessage(host_client->netconnection, &host_client->message); SZ_Clear(&host_client->message); } else { NET_GetMessage(host_client->netconnection); count++; } } } if((dtime() - start) > 3.0f) break; } while(count); // make sure all the clients know we're disconnecting buf.data = (byte *)message; buf.maxsize = 4; buf.cursize = 0; MSG_WriteByte(&buf, svc_disconnect); count = NET_SendToAll(&buf, 5); if(count) Con_Printf("Host_ShutdownServer: NET_SendToAll failed for %d clients\n", count); for(i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++) { if(host_client->active) SV_DropClient(crash); } // clear structures memset(&sv, 0, sizeof(sv)); memset(svs.clients, 0, svs.maxclientslimit * sizeof(*svs.clients));} /* This clears all the memory used by both the client and server, but does not reinitialize * anything. */voidHost_ClearMemory(void){ Con_DPrintf("Clearing memory\n"); D_FlushCaches(); Mod_ClearAll(); R_ResetSkyBox(); if(host_hunklevel) Hunk_FreeToMark(host_hunklevel); cls.signon = 0; memset(&sv, 0, sizeof(sv)); memset(&cl, 0, sizeof(cl));} voidHost_ServerFrame(void){ // run the world state sv.pr->global_struct->frametime = host_frametime; // set the time and clear the general datagram SV_ClearDatagram(); // check for new clients SV_CheckForNewClients(); // read client messages SV_RunClients(); // move things around and think // always pause in single player if in console or menus if(!sv.paused && (svs.maxclients > 1 || key_dest == key_game)) SV_Physics(sv.pr); // send all messages to the clients SV_SendClientMessages();} static boolboundfps(double t){ realtime += t; if(!cls.timedemo && realtime - oldrealtime < 1.0 / Fpsmax) return true; host_frametime = realtime - oldrealtime; oldrealtime = realtime; if(host_framerate.value > 0) host_frametime = host_framerate.value; else if(host_frametime > 1.0 / Fpsmin) host_frametime = 1.0 / Fpsmin; return false;} static void_Host_Frame(double time){ static double time1 = 0; static double time2 = 0; static double time3 = 0; int pass1, pass2, pass3; if(setjmp(host_abortserver)) return; // something bad happened, or the server disconnected // keep the random time dependent rand(); // update audio if(cls.signon == SIGNONS) { stepsnd(&r_refdef.view); CL_DecayLights(); } else { static const view_t originview = {0}; stepsnd(&originview); } // get new key events Sys_SendKeyEvents(); // allow mice or other external controllers to add commands IN_Commands(); // process console commands Cbuf_Execute(); // if running the server locally, make intentions now if(sv.active) CL_SendCmd(); stepcd(); // check for commands typed to the host conscmd(); if(boundfps(time)) { // even if fps-bound, at least do something // fetch results from server if(cls.state == ca_connected) CL_ReadFromServer(false); SCR_UpdateScreen(false); return; } if(sv.active) Host_ServerFrame(); // if running the server remotely, send intentions now after // the incoming messages have been read if(!sv.active) CL_SendCmd(); host_time += host_frametime; // fetch results from server if(cls.state == ca_connected) { cl.oldtime = cl.time; cl.time += host_frametime; CL_ReadFromServer(cl_shownet.value > 0); } // update video if(host_speeds.value) time1 = dtime(); SCR_UpdateScreen(false); if(host_speeds.value) time2 = dtime(); if(host_speeds.value) { pass1 = (time1 - time3) * 1000; time3 = dtime(); pass2 = (time2 - time1) * 1000; pass3 = (time3 - time2) * 1000; Con_Printf( "%3d tot %3d server %3d gfx %3d snd\n", pass1 + pass2 + pass3, pass1, pass2, pass3 ); } host_framecount++;} voidHost_Frame(double time){ static double timetotal; static int timecount; double time1, time2; int i, c, m; if(!serverprofile.value) { _Host_Frame(time); return; } time1 = dtime(); _Host_Frame(time); time2 = dtime(); timetotal += time2 - time1; timecount++; if(timecount < 1000) return; m = timetotal * 1000 / timecount; timecount = 0; timetotal = 0; c = 0; for(i = 0; i < svs.maxclients; i++) { if(svs.clients[i].active) c++; } Con_Printf("serverprofile: %2d clients %2d msec\n", c, m);} voidHost_Init(int argc, char **argv, char **paths){ int i; Memory_Init(); Cbuf_Init();Lines 1–500 of 579. Load more
#include "quakedef.h" #include "q1_palette.h" /* * A server can always be started, even if the system started out as a client * to a remote system. * * A client can NOT be started if the system started as a dedicated server. * * Memory is cleared / released when a server or client begins, not when they end. */ bool dedicated; bool host_initialized; // true if into command execution double host_frametime; double host_time; double realtime; // without any filtering or bounding u64int host_framecount; client_t *host_client; // current client jmp_buf host_abortserver; const byte *host_basepal; const byte *host_colormap; cvar_t sys_ticrate = {"sys_ticrate", "0.05"}; cvar_t fraglimit = {"fraglimit", "0", false, true}; cvar_t timelimit = {"timelimit", "0", false, true}; cvar_t teamplay = {"teamplay", "0", false, true}; cvar_t skill = {"skill", "1"}; // 0 - 3 cvar_t deathmatch = {"deathmatch", "0"}; // 0, 1, or 2 cvar_t coop = {"coop", "0"}; // 0 or 1 cvar_t pausable = {"pausable", "1"}; static double oldrealtime; // last frame run static void *host_hunklevel; static cvar_t host_framerate = {"host_framerate", "0"}; // set for slow motion static cvar_t host_speeds = {"host_speeds", "0"}; // set for running times static cvar_t serverprofile = {"serverprofile", "0"}; static cvar_t samelevel = {"samelevel", "0"}; static cvar_t noexit = {"noexit", "0", false, true}; static cvar_t temp1 = {"temp1", "0"}; void Host_EndGame(char *fmt, ...) { va_list arg; char s[1024]; va_start(arg, fmt); vsnprint(s, sizeof(s), fmt, arg); va_end(arg); Con_DPrintf("Host_EndGame: %s\n", s); if(sv.active) Host_ShutdownServer(false); if(cls.state == ca_dedicated) fatal("Host_EndGame: %s\n", s); // dedicated servers exit if(!CL_NextDemo()) CL_Disconnect(); longjmp(host_abortserver, 1); } /* This shuts down both the client and server */ _Noreturn void Host_Error(char *fmt, ...) { static bool inerror = false; va_list arg; char s[1024]; va_start(arg, fmt); vsnprint(s, sizeof(s), fmt, arg); va_end(arg); if(inerror) { assert(nil); fatal("Host_Error: recursively entered: %s", s); } inerror = true; SCR_EndLoadingPlaque(); // reenable screen updates Con_Printf("Host_Error: %s\n", s); if(sv.active) Host_ShutdownServer(false); if(cls.state == ca_dedicated) fatal("Host_Error: %s\n", s); // dedicated servers exit CL_Disconnect(); cls.demonum = -1; inerror = false; longjmp(host_abortserver, 1); } static void Host_FindMaxClients(void) { svs.maxclients = 1; cls.state = ca_disconnected; if(dedicated) { cls.state = ca_dedicated; svs.maxclients = MAX_SCOREBOARD; } if(svs.maxclients < 1) svs.maxclients = 8; else if(svs.maxclients > MAX_SCOREBOARD) svs.maxclients = MAX_SCOREBOARD; svs.maxclientslimit = svs.maxclients; if(svs.maxclientslimit < 4) svs.maxclientslimit = 4; svs.clients = Hunk_Alloc(svs.maxclientslimit * sizeof(*svs.clients)); if(svs.maxclients > 1) setcvarv("deathmatch", 1.0); else setcvarv("deathmatch", 0.0); } static void Host_InitLocal(void) { Host_InitCommands(); Cvar_RegisterVariable(&host_framerate); Cvar_RegisterVariable(&host_speeds); Cvar_RegisterVariable(&sys_ticrate); Cvar_RegisterVariable(&serverprofile); Cvar_RegisterVariable(&fraglimit); Cvar_RegisterVariable(&timelimit); Cvar_RegisterVariable(&teamplay); Cvar_RegisterVariable(&samelevel); Cvar_RegisterVariable(&noexit); Cvar_RegisterVariable(&skill); Cvar_RegisterVariable(&deathmatch); Cvar_RegisterVariable(&coop); Cvar_RegisterVariable(&pausable); Cvar_RegisterVariable(&temp1); Host_FindMaxClients(); host_time = 1.0; // so a think at time 0 won't get called } /* Sends text across to be displayed. FIXME: make this just a stuffed echo? */ void SV_ClientPrintf(const char *fmt, ...) { va_list arg; char s[1024]; va_start(arg, fmt); vsnprint(s, sizeof(s), fmt, arg); va_end(arg); MSG_WriteByte(&host_client->message, svc_print); MSG_WriteString(&host_client->message, s); } /* Sends text to all active clients */ void SV_BroadcastPrintf(const char *fmt, ...) { va_list arg; char s[1024]; int i; va_start(arg, fmt); vsnprint(s, sizeof(s), fmt, arg); va_end(arg); for(i = 0; i < svs.maxclients; i++) { if(svs.clients[i].active && svs.clients[i].spawned) { MSG_WriteByte(&svs.clients[i].message, svc_print); MSG_WriteString(&svs.clients[i].message, s); } } } /* Send text over to the client to be executed */ void Host_ClientCommands(char *fmt, ...) { va_list arg; char s[1024]; va_start(arg, fmt); vsnprint(s, sizeof(s), fmt, arg); va_end(arg); MSG_WriteByte(&host_client->message, svc_stufftext); MSG_WriteString(&host_client->message, s); } /* * Called when the player is getting totally kicked off the host * if (crash = true), don't bother sending signofs */ void SV_DropClient(bool crash) { int saveSelf, i; client_t *client; if(!crash) { // send any final messages (don't check for errors) if(NET_CanSendMessage(host_client->netconnection)) { MSG_WriteByte(&host_client->message, svc_disconnect); NET_SendMessage(host_client->netconnection, &host_client->message); } if(host_client->edict && host_client->spawned) { // call the prog function for removing a client // this will set the body to a dead frame, among other things saveSelf = sv.pr->global_struct->self; sv.pr->global_struct->self = EDICT_TO_PROG(sv.pr, host_client->edict); PR_ExecuteProgram(sv.pr, sv.pr->global_struct->ClientDisconnect); sv.pr->global_struct->self = saveSelf; } Con_DPrintf("client %s removed\n", host_client->name); } // break the net connection NET_Close(host_client->netconnection); host_client->netconnection = nil; // free the client (the body stays around) host_client->active = false; host_client->name[0] = 0; host_client->old_frags = Q_MININT; net_activeconnections--; // send notification to all clients for(i = 0, client = svs.clients; i < svs.maxclients; i++, client++) { if(!client->active) continue; MSG_WriteByte(&client->message, svc_updatename); MSG_WriteByte(&client->message, host_client - svs.clients); MSG_WriteString(&client->message, ""); MSG_WriteByte(&client->message, svc_updatefrags); MSG_WriteByte(&client->message, host_client - svs.clients); MSG_WriteShort(&client->message, 0); MSG_WriteByte(&client->message, svc_updatecolors); MSG_WriteByte(&client->message, host_client - svs.clients); MSG_WriteByte(&client->message, 0); } } /* This only happens at the end of a game, not between levels */ void Host_ShutdownServer(bool crash) { int i, count; sizebuf_t buf; char message[4]; double start; if(!sv.active) return; UDP_Listen(0); sv.active = false; // stop all client sounds immediately if(cls.state == ca_connected) CL_Disconnect(); // flush any pending messages - like the score!!! start = dtime(); do { count = 0; for(i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++) { if(host_client->active && host_client->message.cursize) { if(NET_CanSendMessage(host_client->netconnection)) { NET_SendMessage(host_client->netconnection, &host_client->message); SZ_Clear(&host_client->message); } else { NET_GetMessage(host_client->netconnection); count++; } } } if((dtime() - start) > 3.0f) break; } while(count); // make sure all the clients know we're disconnecting buf.data = (byte *)message; buf.maxsize = 4; buf.cursize = 0; MSG_WriteByte(&buf, svc_disconnect); count = NET_SendToAll(&buf, 5); if(count) Con_Printf("Host_ShutdownServer: NET_SendToAll failed for %d clients\n", count); for(i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++) { if(host_client->active) SV_DropClient(crash); } // clear structures memset(&sv, 0, sizeof(sv)); memset(svs.clients, 0, svs.maxclientslimit * sizeof(*svs.clients)); } /* This clears all the memory used by both the client and server, but does not reinitialize * anything. */ void Host_ClearMemory(void) { Con_DPrintf("Clearing memory\n"); D_FlushCaches(); Mod_ClearAll(); R_ResetSkyBox(); if(host_hunklevel) Hunk_FreeToMark(host_hunklevel); cls.signon = 0; memset(&sv, 0, sizeof(sv)); memset(&cl, 0, sizeof(cl)); } void Host_ServerFrame(void) { // run the world state sv.pr->global_struct->frametime = host_frametime; // set the time and clear the general datagram SV_ClearDatagram(); // check for new clients SV_CheckForNewClients(); // read client messages SV_RunClients(); // move things around and think // always pause in single player if in console or menus if(!sv.paused && (svs.maxclients > 1 || key_dest == key_game)) SV_Physics(sv.pr); // send all messages to the clients SV_SendClientMessages(); } static bool boundfps(double t) { realtime += t; if(!cls.timedemo && realtime - oldrealtime < 1.0 / Fpsmax) return true; host_frametime = realtime - oldrealtime; oldrealtime = realtime; if(host_framerate.value > 0) host_frametime = host_framerate.value; else if(host_frametime > 1.0 / Fpsmin) host_frametime = 1.0 / Fpsmin; return false; } static void _Host_Frame(double time) { static double time1 = 0; static double time2 = 0; static double time3 = 0; int pass1, pass2, pass3; if(setjmp(host_abortserver)) return; // something bad happened, or the server disconnected // keep the random time dependent rand(); // update audio if(cls.signon == SIGNONS) { stepsnd(&r_refdef.view); CL_DecayLights(); } else { static const view_t originview = {0}; stepsnd(&originview); } // get new key events Sys_SendKeyEvents(); // allow mice or other external controllers to add commands IN_Commands(); // process console commands Cbuf_Execute(); // if running the server locally, make intentions now if(sv.active) CL_SendCmd(); stepcd(); // check for commands typed to the host conscmd(); if(boundfps(time)) { // even if fps-bound, at least do something // fetch results from server if(cls.state == ca_connected) CL_ReadFromServer(false); SCR_UpdateScreen(false); return; } if(sv.active) Host_ServerFrame(); // if running the server remotely, send intentions now after // the incoming messages have been read if(!sv.active) CL_SendCmd(); host_time += host_frametime; // fetch results from server if(cls.state == ca_connected) { cl.oldtime = cl.time; cl.time += host_frametime; CL_ReadFromServer(cl_shownet.value > 0); } // update video if(host_speeds.value) time1 = dtime(); SCR_UpdateScreen(false); if(host_speeds.value) time2 = dtime(); if(host_speeds.value) { pass1 = (time1 - time3) * 1000; time3 = dtime(); pass2 = (time2 - time1) * 1000; pass3 = (time3 - time2) * 1000; Con_Printf( "%3d tot %3d server %3d gfx %3d snd\n", pass1 + pass2 + pass3, pass1, pass2, pass3 ); } host_framecount++; } void Host_Frame(double time) { static double timetotal; static int timecount; double time1, time2; int i, c, m; if(!serverprofile.value) { _Host_Frame(time); return; } time1 = dtime(); _Host_Frame(time); time2 = dtime(); timetotal += time2 - time1; timecount++; if(timecount < 1000) return; m = timetotal * 1000 / timecount; timecount = 0; timetotal = 0; c = 0; for(i = 0; i < svs.maxclients; i++) { if(svs.clients[i].active) c++; } Con_Printf("serverprofile: %2d clients %2d msec\n", c, m); } void Host_Init(int argc, char **argv, char **paths) { int i; Memory_Init(); Cbuf_Init();