X-Git-Url: http://git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=sv_main.c;h=8b5cfb84d7dbd95ab4e1d742c215a0b5bd30148b;hp=a5fc047f587421c3fe72a582042a09a9f4f1ed33;hb=c573a47a538a3a90cb34ccf2e3e21a0d1b129d20;hpb=60f77488531e24be2442da573d6f69d6148ad18e diff --git a/sv_main.c b/sv_main.c index a5fc047f..8b5cfb84 100644 --- a/sv_main.c +++ b/sv_main.c @@ -50,6 +50,7 @@ cvar_t pausable = {CF_SERVER, "pausable","1", "allow players to pause or not (ot cvar_t pr_checkextension = {CF_SERVER | CF_READONLY, "pr_checkextension", "1", "indicates to QuakeC that the standard quakec extensions system is available (if 0, quakec should not attempt to use extensions)"}; cvar_t samelevel = {CF_SERVER | CF_NOTIFY, "samelevel","0", "repeats same level if level ends (due to timelimit or someone hitting an exit)"}; cvar_t skill = {CF_SERVER, "skill","1", "difficulty level of game, affects monster layouts in levels, 0 = easy, 1 = normal, 2 = hard, 3 = nightmare (same layout as hard but monsters fire twice)"}; +cvar_t campaign = {CF_SERVER, "campaign", "0", "singleplayer mode"}; cvar_t host_timescale = {CF_CLIENT | CF_SERVER, "host_timescale", "1.0", "controls game speed, 0.5 is half speed, 2 is double speed"}; cvar_t sv_accelerate = {CF_SERVER, "sv_accelerate", "10", "rate at which a player accelerates to sv_maxspeed"}; @@ -76,7 +77,7 @@ cvar_t sv_checkforpacketsduringsleep = {CF_SERVER, "sv_checkforpacketsduringslee cvar_t sv_clmovement_enable = {CF_SERVER, "sv_clmovement_enable", "1", "whether to allow clients to use cl_movement prediction, which can cause choppy movement on the server which may annoy other players"}; cvar_t sv_clmovement_minping = {CF_SERVER, "sv_clmovement_minping", "0", "if client ping is below this time in milliseconds, then their ability to use cl_movement prediction is disabled for a while (as they don't need it)"}; cvar_t sv_clmovement_minping_disabletime = {CF_SERVER, "sv_clmovement_minping_disabletime", "1000", "when client falls below minping, disable their prediction for this many milliseconds (should be at least 1000 or else their prediction may turn on/off frequently)"}; -cvar_t sv_clmovement_inputtimeout = {CF_SERVER, "sv_clmovement_inputtimeout", "0.2", "when a client does not send input for this many seconds, force them to move anyway (unlike QuakeWorld)"}; +cvar_t sv_clmovement_inputtimeout = {CF_SERVER, "sv_clmovement_inputtimeout", "0.1", "when a client does not send input for this many seconds (max 0.1), force them to move anyway (unlike QuakeWorld)"}; cvar_t sv_cullentities_nevercullbmodels = {CF_SERVER, "sv_cullentities_nevercullbmodels", "0", "if enabled the clients are always notified of moving doors and lifts and other submodels of world (warning: eats a lot of network bandwidth on some levels!)"}; cvar_t sv_cullentities_pvs = {CF_SERVER, "sv_cullentities_pvs", "1", "fast but loose culling of hidden entities"}; cvar_t sv_cullentities_stats = {CF_SERVER, "sv_cullentities_stats", "0", "displays stats on network entities culled by various methods for each client"}; @@ -92,6 +93,7 @@ cvar_t sv_cullentities_trace_entityocclusion = {CF_SERVER, "sv_cullentities_trac cvar_t sv_cullentities_trace_samples = {CF_SERVER, "sv_cullentities_trace_samples", "2", "number of samples to test for entity culling"}; cvar_t sv_cullentities_trace_samples_extra = {CF_SERVER, "sv_cullentities_trace_samples_extra", "2", "number of samples to test for entity culling when the entity affects its surroundings by e.g. dlight"}; cvar_t sv_cullentities_trace_samples_players = {CF_SERVER, "sv_cullentities_trace_samples_players", "8", "number of samples to test for entity culling when the entity is a player entity"}; +cvar_t sv_cullentities_trace_spectators = {CF_SERVER, "sv_cullentities_trace_spectators", "0", "enables trace entity culling for clients that are spectating"}; cvar_t sv_debugmove = {CF_SERVER | CF_NOTIFY, "sv_debugmove", "0", "disables collision detection optimizations for debugging purposes"}; cvar_t sv_echobprint = {CF_SERVER | CF_ARCHIVE, "sv_echobprint", "1", "prints gamecode bprint() calls to server console"}; cvar_t sv_edgefriction = {CF_SERVER, "edgefriction", "1", "how much you slow down when nearing a ledge you might fall off, multiplier of sv_friction (Quake used 2, QuakeWorld used 1 due to a bug in physics code)"}; @@ -121,7 +123,7 @@ cvar_t sv_gameplayfix_stepmultipletimes = {CF_SERVER, "sv_gameplayfix_stepmultip cvar_t sv_gameplayfix_nostepmoveonsteepslopes = {CF_SERVER, "sv_gameplayfix_nostepmoveonsteepslopes", "0", "crude fix which prevents MOVETYPE_STEP (not swimming or flying) to move on slopes whose angle is bigger than 45 degree"}; cvar_t sv_gameplayfix_swiminbmodels = {CF_SERVER, "sv_gameplayfix_swiminbmodels", "1", "causes pointcontents (used to determine if you are in a liquid) to check bmodel entities as well as the world model, so you can swim around in (possibly moving) water bmodel entities"}; cvar_t sv_gameplayfix_upwardvelocityclearsongroundflag = {CF_SERVER, "sv_gameplayfix_upwardvelocityclearsongroundflag", "1", "prevents monsters, items, and most other objects from being stuck to the floor when pushed around by damage, and other situations in mods"}; -cvar_t sv_gameplayfix_downtracesupportsongroundflag = {CF_SERVER, "sv_gameplayfix_downtracesupportsongroundflag", "1", "prevents very short moves from clearing onground (which may make the player stick to the floor at high netfps)"}; +cvar_t sv_gameplayfix_downtracesupportsongroundflag = {CF_SERVER, "sv_gameplayfix_downtracesupportsongroundflag", "1", "prevents very short moves from clearing onground (which may make the player stick to the floor at high netfps), fixes groundentity not being set when walking onto a mover with sv_gameplayfix_nogravityonground"}; cvar_t sv_gameplayfix_q1bsptracelinereportstexture = {CF_SERVER, "sv_gameplayfix_q1bsptracelinereportstexture", "1", "enables mods to get accurate trace_texture results on q1bsp by using a surface-hitting traceline implementation rather than the standard solidbsp method, q3bsp always reports texture accurately"}; cvar_t sv_gameplayfix_unstickplayers = {CF_SERVER, "sv_gameplayfix_unstickplayers", "1", "big hack to try and fix the rare case of MOVETYPE_WALK entities getting stuck in the world clipping hull."}; cvar_t sv_gameplayfix_unstickentities = {CF_SERVER, "sv_gameplayfix_unstickentities", "1", "hack to check if entities are crossing world collision hull and try to move them to the right position"}; @@ -147,8 +149,8 @@ cvar_t sv_sound_watersplash = {CF_SERVER, "sv_sound_watersplash", "misc/h2ohit1. cvar_t sv_stepheight = {CF_SERVER | CF_NOTIFY, "sv_stepheight", "18", "how high you can step up (TW_SV_STEPCONTROL extension)"}; cvar_t sv_stopspeed = {CF_SERVER | CF_NOTIFY, "sv_stopspeed","100", "how fast you come to a complete stop"}; cvar_t sv_wallfriction = {CF_SERVER | CF_NOTIFY, "sv_wallfriction", "1", "how much you slow down when sliding along a wall"}; -cvar_t sv_wateraccelerate = {CF_SERVER, "sv_wateraccelerate", "-1", "rate at which a player accelerates to sv_maxspeed while in the air, if less than 0 the sv_accelerate variable is used instead"}; -cvar_t sv_waterfriction = {CF_SERVER | CF_NOTIFY, "sv_waterfriction","-1", "how fast you slow down, if less than 0 the sv_friction variable is used instead"}; +cvar_t sv_wateraccelerate = {CF_SERVER, "sv_wateraccelerate", "-1", "rate at which a player accelerates to sv_maxspeed while in water, if less than 0 the sv_accelerate variable is used instead"}; +cvar_t sv_waterfriction = {CF_SERVER | CF_NOTIFY, "sv_waterfriction","-1", "how fast you slow down in water, if less than 0 the sv_friction variable is used instead"}; cvar_t sv_warsowbunny_airforwardaccel = {CF_SERVER, "sv_warsowbunny_airforwardaccel", "1.00001", "how fast you accelerate until you reach sv_maxspeed"}; cvar_t sv_warsowbunny_accel = {CF_SERVER, "sv_warsowbunny_accel", "0.1585", "how fast you accelerate until after reaching sv_maxspeed (it gets harder as you near sv_warsowbunny_topspeed)"}; cvar_t sv_warsowbunny_topspeed = {CF_SERVER, "sv_warsowbunny_topspeed", "925", "soft speed limit (can get faster with rjs and on ramps)"}; @@ -205,6 +207,8 @@ cvar_t halflifebsp = {CF_SERVER, "halflifebsp", "0", "indicates the current map cvar_t sv_mapformat_is_quake2 = {CF_SERVER, "sv_mapformat_is_quake2", "0", "indicates the current map is q2bsp format (useful to know because of different entity behaviors, .frame on submodels and other things)"}; cvar_t sv_mapformat_is_quake3 = {CF_SERVER, "sv_mapformat_is_quake3", "0", "indicates the current map is q2bsp format (useful to know because of different entity behaviors)"}; +cvar_t sv_writepicture_quality = {CF_SERVER | CF_ARCHIVE, "sv_writepicture_quality", "10", "WritePicture quality offset (higher means better quality, but slower)"}; + server_t sv; server_static_t svs; @@ -432,28 +436,54 @@ static void SV_AreaStats_f(cmd_state_t *cmd) World_PrintAreaStats(&sv.world, "server"); } -static qbool SV_CanSave(void) +static void SV_ServerOptions (void) { - prvm_prog_t *prog = SVVM_prog; - if(SV_IsLocalServer() == 1) - { - // singleplayer checks - if ((svs.clients[0].active && PRVM_serveredictfloat(svs.clients[0].edict, deadflag))) + int i; + + // general default + svs.maxclients = 8; + +// COMMANDLINEOPTION: Server: -dedicated [playerlimit] starts a dedicated server (with a command console), default playerlimit is 8 +// COMMANDLINEOPTION: Server: -listen [playerlimit] starts a multiplayer server with graphical client, like singleplayer but other players can connect, default playerlimit is 8 + // if no client is in the executable or -dedicated is specified on + // commandline, start a dedicated server + i = Sys_CheckParm ("-dedicated"); + if (i || !cl_available) + { + cls.state = ca_dedicated; + // check for -dedicated specifying how many players + if (i && i + 1 < sys.argc && atoi (sys.argv[i+1]) >= 1) + svs.maxclients = atoi (sys.argv[i+1]); + if (Sys_CheckParm ("-listen")) + Con_Printf ("Only one of -dedicated or -listen can be specified\n"); + // default sv_public on for dedicated servers (often hosted by serious administrators), off for listen servers (often hosted by clueless users) + Cvar_SetValue(&cvars_all, "sv_public", 1); + } + else if (cl_available) + { + // client exists and not dedicated, check if -listen is specified + cls.state = ca_disconnected; + i = Sys_CheckParm ("-listen"); + if (i) { - Con_Print("Can't savegame with a dead player\n"); - return false; + // default players unless specified + if (i + 1 < sys.argc && atoi (sys.argv[i+1]) >= 1) + svs.maxclients = atoi (sys.argv[i+1]); } - - if(host.hook.CL_Intermission && host.hook.CL_Intermission()) + else { - Con_Print("Can't save in intermission.\n"); - return false; + // default players in some games, singleplayer in most + if (gamemode != GAME_GOODVSBAD2 && !IS_NEXUIZ_DERIVED(gamemode) && gamemode != GAME_BATTLEMECH) + svs.maxclients = 1; } } - else - Con_Print(CON_WARN "Warning: saving a multiplayer game may have strange results when restored (to properly resume, all players must join in the same player slots and then the game can be reloaded).\n"); - return true; - + + svs.maxclients = svs.maxclients_next = bound(1, svs.maxclients, MAX_SCOREBOARD); + + svs.clients = (client_t *)Mem_Alloc(sv_mempool, sizeof(client_t) * svs.maxclients); + + if (svs.maxclients > 1 && !deathmatch.integer && !coop.integer) + Cvar_SetValueQuick(&deathmatch, 1); } /* @@ -496,10 +526,11 @@ void SV_Init (void) Cvar_RegisterVariable (&pr_checkextension); Cvar_RegisterVariable (&samelevel); Cvar_RegisterVariable (&skill); + Cvar_RegisterVariable (&campaign); Cvar_RegisterVariable (&host_timescale); Cvar_RegisterCallback (&host_timescale, Host_Timescale_c); - Cvar_RegisterAlias (&host_timescale, "slowmo"); - Cvar_RegisterAlias (&host_timescale, "timescale"); + Cvar_RegisterVirtual (&host_timescale, "slowmo"); + Cvar_RegisterVirtual (&host_timescale, "timescale"); Cvar_RegisterVariable (&sv_accelerate); Cvar_RegisterVariable (&sv_aim); Cvar_RegisterVariable (&sv_airaccel_qw); @@ -540,6 +571,7 @@ void SV_Init (void) Cvar_RegisterVariable (&sv_cullentities_trace_samples); Cvar_RegisterVariable (&sv_cullentities_trace_samples_extra); Cvar_RegisterVariable (&sv_cullentities_trace_samples_players); + Cvar_RegisterVariable (&sv_cullentities_trace_spectators); Cvar_RegisterVariable (&sv_debugmove); Cvar_RegisterVariable (&sv_echobprint); Cvar_RegisterVariable (&sv_edgefriction); @@ -590,7 +622,7 @@ void SV_Init (void) Cvar_RegisterVariable (&sv_protocolname); Cvar_RegisterVariable (&sv_random_seed); Cvar_RegisterVariable (&host_limitlocal); - Cvar_RegisterAlias(&host_limitlocal, "sv_ratelimitlocalplayer"); + Cvar_RegisterVirtual(&host_limitlocal, "sv_ratelimitlocalplayer"); Cvar_RegisterVariable (&sv_sound_land); Cvar_RegisterVariable (&sv_sound_watersplash); Cvar_RegisterVariable (&sv_stepheight); @@ -655,10 +687,15 @@ void SV_Init (void) Cvar_RegisterVariable (&sv_mapformat_is_quake2); Cvar_RegisterVariable (&sv_mapformat_is_quake3); + Cvar_RegisterVariable (&sv_writepicture_quality); + SV_InitOperatorCommands(); - host.hook.SV_CanSave = SV_CanSave; + host.hook.SV_Shutdown = SV_Shutdown; sv_mempool = Mem_AllocPool("server", 0, NULL); + + SV_ServerOptions(); + Cvar_Callback(&sv_netport); } static void SV_SaveEntFile_f(cmd_state_t *cmd) @@ -858,11 +895,6 @@ void SV_SendServerinfo (client_t *client) client->movesequence = 0; client->movement_highestsequence_seen = 0; memset(&client->movement_count, 0, sizeof(client->movement_count)); -#ifdef NUM_PING_TIMES - for (i = 0;i < NUM_PING_TIMES;i++) - client->ping_times[i] = 0; - client->num_pings = 0; -#endif client->ping = 0; // allow the client some time to send his keepalives, even if map loading took ages @@ -956,14 +988,31 @@ void SV_ConnectClient (int clientnum, netconn_t *netconnection) SV_DropClient Called when the player is getting totally kicked off the host -if (crash = true), don't bother sending signofs +if (leaving = true), don't bother sending signofs ===================== */ -void SV_DropClient(qbool crash) +void SV_DropClient(qbool leaving, const char *fmt, ... ) { prvm_prog_t *prog = SVVM_prog; int i; - Con_Printf("Client \"%s\" dropped\n", host_client->name); + + va_list argptr; + char reason[512] = ""; + + Con_Printf("Client \"%s\" dropped", host_client->name); + + if(fmt) + { + va_start(argptr, fmt); + dpvsnprintf(reason, sizeof(reason), fmt, argptr); + va_end(argptr); + + Con_Printf(" (%s)\n", reason); + } + else + { + Con_Printf(" \n"); + } SV_StopDemoRecording(host_client); @@ -973,15 +1022,22 @@ void SV_DropClient(qbool crash) if (host_client->netconnection) { // tell the client to be gone - if (!crash) + if (!leaving) { // LadyHavoc: no opportunity for resending, so use unreliable 3 times - unsigned char bufdata[8]; + unsigned char bufdata[520]; // Disconnect reason string can be 512 characters sizebuf_t buf; memset(&buf, 0, sizeof(buf)); buf.data = bufdata; buf.maxsize = sizeof(bufdata); MSG_WriteByte(&buf, svc_disconnect); + if(fmt) + { + if(sv.protocol == PROTOCOL_DARKPLACES8) + MSG_WriteString(&buf, reason); + else + SV_ClientPrintf("%s\n", reason); + } NetConn_SendUnreliableMessage(host_client->netconnection, &buf, sv.protocol, 10000, 0, false); NetConn_SendUnreliableMessage(host_client->netconnection, &buf, sv.protocol, 10000, 0, false); NetConn_SendUnreliableMessage(host_client->netconnection, &buf, sv.protocol, 10000, 0, false); @@ -1009,6 +1065,10 @@ void SV_DropClient(qbool crash) NetConn_Close(host_client->netconnection); host_client->netconnection = NULL; } + if(fmt) + SV_BroadcastPrintf("\003^3%s left the game (%s)\n", host_client->name, reason); + else + SV_BroadcastPrintf("\003^3%s left the game\n", host_client->name); // if a download is active, close it if (host_client->download_file) @@ -1246,7 +1306,7 @@ static void SV_Download_f(cmd_state_t *cmd) if (!sv_allowdownloads_archive.integer) { - if (!strcasecmp(extension, "pak") || !strcasecmp(extension, "pk3")) + if (!strcasecmp(extension, "pak") || !strcasecmp(extension, "pk3") || !strcasecmp(extension, "dpk")) { SV_ClientPrintf("Download rejected: file \"%s\" is an archive\nYou must separately download or purchase the data archives for this game/mod to get this file\n", host_client->download_name); SV_ClientCommands("\nstopdownload\n"); @@ -1325,7 +1385,7 @@ SV_ModelIndex */ int SV_ModelIndex(const char *s, int precachemode) { - int i, limit = ((sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3) ? 256 : MAX_MODELS); + int i, limit = ((sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE) ? 256 : MAX_MODELS); char filename[MAX_QPATH]; if (!s || !*s) return 0; @@ -1388,7 +1448,7 @@ SV_SoundIndex */ int SV_SoundIndex(const char *s, int precachemode) { - int i, limit = ((sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3) ? 256 : MAX_SOUNDS); + int i, limit = ((sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP) ? 256 : MAX_SOUNDS); char filename[MAX_QPATH]; if (!s || !*s) return 0; @@ -1528,7 +1588,7 @@ model_t *SV_GetModelFromEdict(prvm_edict_t *ed) { prvm_prog_t *prog = SVVM_prog; int modelindex; - if (!ed || ed->priv.server->free) + if (!ed || ed->free) return NULL; modelindex = (int)PRVM_serveredictfloat(ed, modelindex); return (modelindex > 0 && modelindex < MAX_MODELS) ? sv.models[modelindex] : NULL; @@ -1555,7 +1615,7 @@ static void SV_CreateBaseline (void) // LadyHavoc: always clear state values, whether the entity is in use or not svent->priv.server->baseline = defaultstate; - if (svent->priv.server->free) + if (svent->free) continue; if (entnum > svs.maxclients && !PRVM_serveredictfloat(svent, modelindex)) continue; @@ -1744,15 +1804,6 @@ void SV_SpawnServer (const char *map) if(sv.active) { - client_t *client; - for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++) - { - if (client->netconnection) - { - MSG_WriteByte(&client->netconnection->message, svc_stufftext); - MSG_WriteString(&client->netconnection->message, "reconnect\n"); - } - } World_End(&sv.world); if(PRVM_serverfunction(SV_Shutdown)) { @@ -1795,14 +1846,37 @@ void SV_SpawnServer (const char *map) // // tell all connected clients that we are going to a new level // - if (!sv.active) + if (sv.active) + { + client_t *client; + for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++) + { + if (client->netconnection) + { + MSG_WriteByte(&client->netconnection->message, svc_stufftext); + MSG_WriteString(&client->netconnection->message, "reconnect\n"); + } + } + } + else + { + // open server port NetConn_OpenServerPorts(true); + } // // make cvars consistant // + if (coop.integer) + { Cvar_SetValueQuick(&deathmatch, 0); + Cvar_SetValueQuick(&campaign, 0); + } + else if(!deathmatch.integer) + Cvar_SetValueQuick(&campaign, 1); + else + Cvar_SetValueQuick(&campaign, 0); // LadyHavoc: it can be useful to have skills outside the range 0-3... //current_skill = bound(0, (int)(skill.value + 0.5), 3); //Cvar_SetValue ("skill", (float)current_skill); @@ -1903,7 +1977,7 @@ void SV_SpawnServer (const char *map) // AK possible hack since num_edicts is still 0 ent = PRVM_EDICT_NUM(0); memset (ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t)); - ent->priv.server->free = false; + ent->free = false; PRVM_serveredictstring(ent, model) = PRVM_SetEngineString(prog, sv.worldname); PRVM_serveredictfloat(ent, modelindex) = 1; // world model PRVM_serveredictfloat(ent, solid) = SOLID_BSP; @@ -2025,10 +2099,12 @@ void SV_Shutdown(void) prvm_prog_t *prog = SVVM_prog; int i; - Con_DPrintf("SV_Shutdown\n"); + SV_LockThreadMutex(); if (!sv.active) - return; + goto end; + + Con_DPrintf("SV_Shutdown\n"); NetConn_Heartbeat(2); NetConn_Heartbeat(2); @@ -2047,7 +2123,7 @@ void SV_Shutdown(void) } for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++) if (host_client->active) - SV_DropClient(false); // server shutdown + SV_DropClient(false, "Server shutting down"); // server shutdown NetConn_CloseServerPorts(); @@ -2057,8 +2133,8 @@ void SV_Shutdown(void) // memset(&sv, 0, sizeof(sv)); memset(svs.clients, 0, svs.maxclients*sizeof(client_t)); - - cl.islocalgame = false; +end: + SV_UnlockThreadMutex(); } ///////////////////////////////////////////////////// @@ -2077,7 +2153,7 @@ static void SVVM_end_increase_edicts(prvm_prog_t *prog) // link every entity except world for (i = 1, ent = prog->edicts;i < prog->num_edicts;i++, ent++) - if (!ent->priv.server->free && !VectorCompare(PRVM_serveredictvector(ent, absmin), PRVM_serveredictvector(ent, absmax))) + if (!ent->free && !VectorCompare(PRVM_serveredictvector(ent, absmin), PRVM_serveredictvector(ent, absmax))) SV_LinkEdict(ent); } @@ -2153,9 +2229,10 @@ static void SVVM_free_edict(prvm_prog_t *prog, prvm_edict_t *ed) PRVM_serveredictfloat(ed, solid) = 0; VM_RemoveEdictSkeleton(prog, ed); +#ifdef USEODE World_Physics_RemoveFromEntity(&sv.world, ed); World_Physics_RemoveJointFromEntity(&sv.world, ed); - +#endif // make sure csqc networking is aware of the removed entity e = PRVM_NUM_FOR_EDICT(ed); sv.csqcentityversion[e] = 0; @@ -2173,7 +2250,7 @@ static void SVVM_count_edicts(prvm_prog_t *prog) for (i=0 ; inum_edicts ; i++) { ent = PRVM_EDICT_NUM(i); - if (ent->priv.server->free) + if (ent->free) continue; active++; if (PRVM_serveredictfloat(ent, solid)) @@ -2216,7 +2293,7 @@ static qbool SVVM_load_edict(prvm_prog_t *prog, prvm_edict_t *ent) static void SV_VM_Setup(void) { prvm_prog_t *prog = SVVM_prog; - PRVM_Prog_Init(prog, &cmd_server); + PRVM_Prog_Init(prog, cmd_local); // allocate the mempools // TODO: move the magic numbers/constants into #defines [9/13/2006 Black] @@ -2409,13 +2486,20 @@ static void SV_CheckTimeouts(void) // never timeout loopback connections for (i = (host_isclient.integer ? 1 : 0), host_client = &svs.clients[i]; i < svs.maxclients; i++, host_client++) - { if (host_client->netconnection && host.realtime > host_client->netconnection->timeout) - { - Con_Printf("Client \"%s\" connection timed out\n", host_client->name); - SV_DropClient(false); - } - } + SV_DropClient(false, "Timed out"); +} + +/* +================== +SV_TimeReport + +Returns a time report string, for example for +================== +*/ +const char *SV_TimingReport(char *buf, size_t buflen) +{ + return va(buf, buflen, "%.1f%% CPU, %.2f%% lost, offset avg %.1fms, max %.1fms, sdev %.1fms", svs.perf_cpuload * 100, svs.perf_lost * 100, svs.perf_offset_avg * 1000, svs.perf_offset_max * 1000, svs.perf_offset_sdev * 1000); } extern cvar_t host_maxwait; @@ -2451,7 +2535,7 @@ double SV_Frame(double time) } if(svs.perf_lost > 0 && developer_extra.integer && playing) // only complain if anyone is looking - Con_DPrintf("Server can't keep up: %s\n", Host_TimingReport(vabuf, sizeof(vabuf))); + Con_DPrintf("Server can't keep up: %s\n", SV_TimingReport(vabuf, sizeof(vabuf))); } if(svs.perf_acc_realtime > 5 || sv.time < 10) @@ -2647,7 +2731,7 @@ static int SV_ThreadFunc(void *voiddata) } if(svs.perf_lost > 0 && developer_extra.integer) if(playing) - Con_DPrintf("Server can't keep up: %s\n", Host_TimingReport(vabuf, sizeof(vabuf))); + Con_DPrintf("Server can't keep up: %s\n", SV_TimingReport(vabuf, sizeof(vabuf))); svs.perf_acc_realtime = svs.perf_acc_sleeptime = svs.perf_acc_lost = svs.perf_acc_offset = svs.perf_acc_offset_squared = svs.perf_acc_offset_max = svs.perf_acc_offset_samples = 0; }