X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=sv_main.c;h=4e541af0153185e5b643431b119174e230716519;hb=7114d518a61fdde372146e27437ba6be5bf2797b;hp=aa274005779b0661c205af161f11af3e42ee38ed;hpb=030486827e095a003353a17508550f2680cd3e1e;p=xonotic%2Fdarkplaces.git diff --git a/sv_main.c b/sv_main.c index aa274005..4e541af0 100644 --- a/sv_main.c +++ b/sv_main.c @@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "quakedef.h" #include "sv_demo.h" #include "libcurl.h" +#include "csprogs.h" static void SV_SaveEntFile_f(void); static void SV_StartDownload_f(void); @@ -30,7 +31,6 @@ static void SV_VM_Setup(); void VM_CustomStats_Clear (void); void VM_SV_UpdateCustomStats (client_t *client, prvm_edict_t *ent, sizebuf_t *msg, int *stats); -void EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numstates, const entity_state_t *states); cvar_t coop = {0, "coop","0", "coop mode, 0 = no coop, 1 = coop mode, multiple players playing through the singleplayer game (coop mode also shuts off deathmatch)"}; cvar_t deathmatch = {0, "deathmatch","0", "deathmatch mode, values depend on mod but typically 0 = no deathmatch, 1 = normal deathmatch with respawning weapons, 2 = weapons stay (players can only pick up new weapons)"}; @@ -65,13 +65,15 @@ cvar_t sv_cullentities_pvs = {0, "sv_cullentities_pvs", "1", "fast but loose cul cvar_t sv_cullentities_stats = {0, "sv_cullentities_stats", "0", "displays stats on network entities culled by various methods for each client"}; cvar_t sv_cullentities_trace = {0, "sv_cullentities_trace", "0", "somewhat slow but very tight culling of hidden entities, minimizes network traffic and makes wallhack cheats useless"}; cvar_t sv_cullentities_trace_delay = {0, "sv_cullentities_trace_delay", "1", "number of seconds until the entity gets actually culled"}; +cvar_t sv_cullentities_trace_delay_players = {0, "sv_cullentities_trace_delay_players", "0.2", "number of seconds until the entity gets actually culled if it is a player entity"}; cvar_t sv_cullentities_trace_enlarge = {0, "sv_cullentities_trace_enlarge", "0", "box enlargement for entity culling"}; cvar_t sv_cullentities_trace_prediction = {0, "sv_cullentities_trace_prediction", "1", "also trace from the predicted player position"}; cvar_t sv_cullentities_trace_samples = {0, "sv_cullentities_trace_samples", "1", "number of samples to test for entity culling"}; cvar_t sv_cullentities_trace_samples_extra = {0, "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 = {0, "sv_cullentities_trace_samples_players", "8", "number of samples to test for entity culling when the entity is a player entity"}; cvar_t sv_debugmove = {CVAR_NOTIFY, "sv_debugmove", "0", "disables collision detection optimizations for debugging purposes"}; cvar_t sv_echobprint = {CVAR_SAVE, "sv_echobprint", "1", "prints gamecode bprint() calls to server console"}; -cvar_t sv_edgefriction = {0, "edgefriction", "2", "how much you slow down when nearing a ledge you might fall off"}; +cvar_t sv_edgefriction = {0, "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)"}; cvar_t sv_entpatch = {0, "sv_entpatch", "1", "enables loading of .ent files to override entities in the bsp (for example Threewave CTF server pack contains .ent patch files enabling play of CTF on id1 maps)"}; cvar_t sv_fixedframeratesingleplayer = {0, "sv_fixedframeratesingleplayer", "1", "allows you to use server-style timing system in singleplayer (don't run faster than sys_ticrate)"}; cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0", "freezes time, except for players, allowing you to walk around and take screenshots of explosions"}; @@ -212,6 +214,7 @@ prvm_required_field_t reqfields[] = {ev_entity, "nodrawtoclient"}, {ev_entity, "tag_entity"}, {ev_entity, "viewmodelforclient"}, + {ev_float, "SendFlags"}, {ev_float, "Version"}, {ev_float, "alpha"}, {ev_float, "ammo_cells1"}, @@ -337,10 +340,12 @@ void SV_Init (void) Cvar_RegisterVariable (&sv_cullentities_stats); Cvar_RegisterVariable (&sv_cullentities_trace); Cvar_RegisterVariable (&sv_cullentities_trace_delay); + Cvar_RegisterVariable (&sv_cullentities_trace_delay_players); Cvar_RegisterVariable (&sv_cullentities_trace_enlarge); Cvar_RegisterVariable (&sv_cullentities_trace_prediction); Cvar_RegisterVariable (&sv_cullentities_trace_samples); Cvar_RegisterVariable (&sv_cullentities_trace_samples_extra); + Cvar_RegisterVariable (&sv_cullentities_trace_samples_players); Cvar_RegisterVariable (&sv_debugmove); Cvar_RegisterVariable (&sv_echobprint); Cvar_RegisterVariable (&sv_edgefriction); @@ -728,13 +733,47 @@ void SV_SendServerinfo (client_t *client) } // reset csqc entity versions - memset(client->csqcentityversion, 0, sizeof(client->csqcentityversion)); + for (i = 0;i < prog->max_edicts;i++) + { + client->csqcentityscope[i] = 0; + client->csqcentitysendflags[i] = 0xFFFFFF; + client->csqcentityglobalhistory[i] = 0; + } + for (i = 0;i < NUM_CSQCENTITYDB_FRAMES;i++) + { + client->csqcentityframehistory[i].num = 0; + client->csqcentityframehistory[i].framenum = -1; + } + client->csqcnumedicts = 0; + client->csqcentityframehistory_next = 0; SZ_Clear (&client->netconnection->message); MSG_WriteByte (&client->netconnection->message, svc_print); dpsnprintf (message, sizeof (message), "\nServer: %s build %s (progs %i crc)", gamename, buildstring, prog->filecrc); MSG_WriteString (&client->netconnection->message,message); + SV_StopDemoRecording(client); // to split up demos into different files + if(sv_autodemo_perclient.integer && client->netconnection) + { + char demofile[MAX_OSPATH]; + char levelname[MAX_QPATH]; + char ipaddress[MAX_QPATH]; + size_t i; + + // start a new demo file + strlcpy(levelname, FS_FileWithoutPath(sv.worldmodel->name), sizeof(levelname)); + if (strrchr(levelname, '.')) + *(strrchr(levelname, '.')) = 0; + + LHNETADDRESS_ToString(&(client->netconnection->peeraddress), ipaddress, sizeof(ipaddress), true); + for(i = 0; ipaddress[i]; ++i) + if(!isalnum(ipaddress[i])) + ipaddress[i] = '-'; + dpsnprintf (demofile, sizeof(demofile), "%s_%s_%d_%s.dem", Sys_TimeString (sv_autodemo_perclient_nameformat.string), levelname, PRVM_NUM_FOR_EDICT(client->edict), ipaddress); + + SV_StartDemoRecording(client, demofile, -1); + } + //[515]: init csprogs according to version of svprogs, check the crc, etc. if (sv.csqc_progname[0]) { @@ -746,6 +785,29 @@ void SV_SendServerinfo (client_t *client) MSG_WriteString (&client->netconnection->message, va("csqc_progsize %i\n", sv.csqc_progsize)); MSG_WriteByte (&client->netconnection->message, svc_stufftext); MSG_WriteString (&client->netconnection->message, va("csqc_progcrc %i\n", sv.csqc_progcrc)); + + if(client->sv_demo_file != NULL) + { + void *csqcbuf; + fs_offset_t csqclen; + int csqccrc; + int i; + char buf[NET_MAXMESSAGE]; + sizebuf_t sb; + + csqcbuf = FS_LoadFile(sv.csqc_progname, tempmempool, true, &csqclen); + if(csqcbuf) + { + csqccrc = CRC_Block(csqcbuf, csqclen); + sb.data = (void *) buf; + sb.maxsize = sizeof(buf); + i = 0; + while(MakeDownloadPacket(sv.csqc_progname, csqcbuf, csqclen, csqccrc, i++, &sb, sv.protocol)) + SV_WriteDemoMessage(client, &sb, false); + Mem_Free(csqcbuf); + } + } + //[515]: init stufftext string (it is sent before svc_serverinfo) val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.SV_InitCmd); if (val) @@ -815,28 +877,6 @@ void SV_SendServerinfo (client_t *client) client->num_pings = 0; #endif client->ping = 0; - - SV_StopDemoRecording(client); // to split up demos into different files - if(sv_autodemo_perclient.integer && client->netconnection) - { - char demofile[MAX_OSPATH]; - char levelname[MAX_QPATH]; - char ipaddress[MAX_QPATH]; - size_t i; - - // start a new demo file - strlcpy(levelname, FS_FileWithoutPath(sv.worldmodel->name), sizeof(levelname)); - if (strrchr(levelname, '.')) - *(strrchr(levelname, '.')) = 0; - - LHNETADDRESS_ToString(&(client->netconnection->peeraddress), ipaddress, sizeof(ipaddress), true); - for(i = 0; ipaddress[i]; ++i) - if(!isalnum(ipaddress[i])) - ipaddress[i] = '-'; - dpsnprintf (demofile, sizeof(demofile), "%s_%s_%d_%s.dem", Sys_TimeString (sv_autodemo_perclient_nameformat.string), levelname, PRVM_NUM_FOR_EDICT(client->edict), ipaddress); - - SV_StartDemoRecording(client, demofile, -1); - } } /* @@ -926,12 +966,14 @@ crosses a waterline. static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *cs, int enumber) { int i; + unsigned int sendflags; + unsigned int version; unsigned int modelindex, effects, flags, glowsize, lightstyle, lightpflags, light[4], specialvisibilityradius; unsigned int customizeentityforclient; float f; vec3_t cullmins, cullmaxs; dp_model_t *model; - prvm_eval_t *val; + prvm_eval_t *val, *val2; // this 2 billion unit check is actually to detect NAN origins // (we really don't want to send those) @@ -1108,7 +1150,7 @@ static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *c // calculate the visible box of this entity (don't use the physics box // as that is often smaller than a model, and would not count // specialvisibilityradius) - if ((model = sv.models[modelindex])) + if ((model = sv.models[modelindex]) && (model->type != mod_null)) { float scale = cs->scale * (1.0f / 16.0f); if (cs->angles[0] || cs->angles[2]) // pitch and roll @@ -1164,6 +1206,30 @@ static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *c } } + // we need to do some csqc entity upkeep here + // get self.SendFlags and clear them + // (to let the QC know that they've been read) + val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.SendEntity); + if (val->function) + { + val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.SendFlags); + sendflags = (unsigned int)val->_float; + val->_float = 0; + // legacy self.Version system + val2 = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.Version); + if (val2->_float) + { + version = (unsigned int)val2->_float; + if (sv.csqcentityversion[enumber] != version) + sendflags = 0xFFFFFF; + sv.csqcentityversion[enumber] = version; + } + // move sendflags into the per-client sendflags + if (sendflags) + for (i = 0;i < svs.maxclients;i++) + svs.clients[i].csqcentitysendflags[enumber] |= sendflags; + } + return true; } @@ -1272,48 +1338,62 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s) // or not seen by random tracelines if (sv_cullentities_trace.integer && !isbmodel && sv.worldmodel->brush.TraceLineOfSight) { - int samples = s->specialvisibilityradius ? sv_cullentities_trace_samples_extra.integer : sv_cullentities_trace_samples.integer; + int samples = + s->number <= svs.maxclients + ? sv_cullentities_trace_samples_players.integer + : + s->specialvisibilityradius + ? sv_cullentities_trace_samples_extra.integer + : sv_cullentities_trace_samples.integer; float enlarge = sv_cullentities_trace_enlarge.value; qboolean visible = TRUE; - do + if(samples > 0) { - if(Mod_CanSeeBox_Trace(samples, enlarge, sv.worldmodel, sv.writeentitiestoclient_testeye, ed->priv.server->cullmins, ed->priv.server->cullmaxs)) - break; // directly visible from the server's view - - if(sv_cullentities_trace_prediction.integer) + do { - vec3_t predeye; - - // get player velocity - float predtime = bound(0, host_client->ping, 0.2); // / 2 - // sorry, no wallhacking by high ping please, and at 200ms - // ping a FPS is annoying to play anyway and a player is - // likely to have changed his direction - VectorMA(sv.writeentitiestoclient_testeye, predtime, host_client->edict->fields.server->velocity, predeye); - if(sv.worldmodel->brush.TraceLineOfSight(sv.worldmodel, sv.writeentitiestoclient_testeye, predeye)) // must be able to go there... - { - if(Mod_CanSeeBox_Trace(samples, enlarge, sv.worldmodel, predeye, ed->priv.server->cullmins, ed->priv.server->cullmaxs)) - break; // directly visible from the predicted view - } - else + if(Mod_CanSeeBox_Trace(samples, enlarge, sv.worldmodel, sv.writeentitiestoclient_testeye, ed->priv.server->cullmins, ed->priv.server->cullmaxs)) + break; // directly visible from the server's view + + if(sv_cullentities_trace_prediction.integer) { - //Con_DPrintf("Trying to walk into solid in a pingtime... not predicting for culling\n"); + vec3_t predeye; + + // get player velocity + float predtime = bound(0, host_client->ping, 0.2); // / 2 + // sorry, no wallhacking by high ping please, and at 200ms + // ping a FPS is annoying to play anyway and a player is + // likely to have changed his direction + VectorMA(sv.writeentitiestoclient_testeye, predtime, host_client->edict->fields.server->velocity, predeye); + if(sv.worldmodel->brush.TraceLineOfSight(sv.worldmodel, sv.writeentitiestoclient_testeye, predeye)) // must be able to go there... + { + if(Mod_CanSeeBox_Trace(samples, enlarge, sv.worldmodel, predeye, ed->priv.server->cullmins, ed->priv.server->cullmaxs)) + break; // directly visible from the predicted view + } + else + { + //Con_DPrintf("Trying to walk into solid in a pingtime... not predicting for culling\n"); + } } - } - // when we get here, we can't see the entity - visible = false; - } - while(0); - - if(visible) - svs.clients[sv.writeentitiestoclient_clientnumber].visibletime[s->number] = realtime + sv_cullentities_trace_delay.value; - else if (realtime > svs.clients[sv.writeentitiestoclient_clientnumber].visibletime[s->number]) - { - sv.writeentitiestoclient_stats_culled_trace++; - return; + // when we get here, we can't see the entity + visible = false; + } + while(0); + + if(visible) + svs.clients[sv.writeentitiestoclient_clientnumber].visibletime[s->number] = + realtime + ( + s->number <= svs.maxclients + ? sv_cullentities_trace_delay_players.value + : sv_cullentities_trace_delay.value + ); + else if (realtime > svs.clients[sv.writeentitiestoclient_clientnumber].visibletime[s->number]) + { + sv.writeentitiestoclient_stats_culled_trace++; + return; + } } } } @@ -1374,7 +1454,10 @@ void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t * if (sv_cullentities_stats.integer) Con_Printf("client \"%s\" entities: %d total, %d visible, %d culled by: %d pvs %d trace\n", client->name, sv.writeentitiestoclient_stats_totalentities, sv.writeentitiestoclient_stats_visibleentities, sv.writeentitiestoclient_stats_culled_pvs + sv.writeentitiestoclient_stats_culled_trace, sv.writeentitiestoclient_stats_culled_pvs, sv.writeentitiestoclient_stats_culled_trace); - EntityFrameCSQC_WriteFrame(msg, maxsize, numsendstates, sv.writeentitiestoclient_sendstates); + if(client->entitydatabase5) + EntityFrameCSQC_WriteFrame(msg, maxsize, numsendstates, sv.writeentitiestoclient_sendstates, client->entitydatabase5->latestframenum + 1); + else + EntityFrameCSQC_WriteFrame(msg, maxsize, numsendstates, sv.writeentitiestoclient_sendstates, 0); if (client->entitydatabase5) EntityFrame5_WriteFrame(msg, maxsize, client->entitydatabase5, numsendstates, sv.writeentitiestoclient_sendstates, client - svs.clients + 1, client->movesequence); @@ -1915,7 +1998,7 @@ static void SV_UpdateToReliableMessages (void) if (strcmp(host_client->old_name, host_client->name)) { if (host_client->spawned) - SV_BroadcastPrintf("%s changed name to %s\n", host_client->old_name, host_client->name); + SV_BroadcastPrintf("%s ^7changed name to %s\n", host_client->old_name, host_client->name); strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name)); // send notification to all clients MSG_WriteByte (&sv.reliable_datagram, svc_updatename); @@ -1988,7 +2071,7 @@ static void SV_UpdateToReliableMessages (void) } for (j = 0, client = svs.clients;j < svs.maxclients;j++, client++) - if (client->netconnection) + if (client->netconnection && (client->spawned || client->clientconnectcalled)) // also send MSG_ALL to people who are past ClientConnect, but not spawned yet SZ_Write (&client->netconnection->message, sv.reliable_datagram.data, sv.reliable_datagram.cursize); SZ_Clear (&sv.reliable_datagram); @@ -2148,7 +2231,7 @@ static void SV_Download_f(void) } } - host_client->download_file = FS_Open(host_client->download_name, "rb", true, false); + host_client->download_file = FS_OpenVirtualFile(host_client->download_name, true); if (!host_client->download_file) { SV_ClientPrintf("Download rejected: server could not open the file \"%s\"\n", host_client->download_name); @@ -2715,6 +2798,7 @@ void SV_SpawnServer (const char *server) // send serverinfo to all connected clients, and set up botclients coming back from a level change for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++) { + host_client->clientconnectcalled = false; // do NOT call ClientDisconnect if he drops before ClientConnect! if (!host_client->active) continue; if (host_client->netconnection) @@ -2807,6 +2891,9 @@ static void SV_VM_CB_InitEdict(prvm_edict_t *e) static void SV_VM_CB_FreeEdict(prvm_edict_t *ed) { + int i; + int e; + World_UnlinkEdict(ed); // unlink from world bsp ed->fields.server->model = 0; @@ -2819,6 +2906,16 @@ static void SV_VM_CB_FreeEdict(prvm_edict_t *ed) VectorClear(ed->fields.server->angles); ed->fields.server->nextthink = -1; ed->fields.server->solid = 0; + + // make sure csqc networking is aware of the removed entity + e = PRVM_NUM_FOR_EDICT(ed); + sv.csqcentityversion[e] = 0; + for (i = 0;i < svs.maxclients;i++) + { + if (svs.clients[i].csqcentityscope[e]) + svs.clients[i].csqcentityscope[e] = 1; // removed, awaiting send + svs.clients[i].csqcentitysendflags[e] = 0xFFFFFF; + } } static void SV_VM_CB_CountEdicts(void) @@ -2874,8 +2971,6 @@ static qboolean SV_VM_CB_LoadEdict(prvm_edict_t *ent) static void SV_VM_Setup(void) { extern cvar_t csqc_progname; //[515]: csqc crc check and right csprogs name according to progs.dat - extern cvar_t csqc_progcrc; - extern cvar_t csqc_progsize; size_t csprogsdatasize; PRVM_Begin; PRVM_InitProg( PRVM_SERVERPROG );