]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - sv_main.c
added blend and orientation commands for effectinfo.txt (overrides the
[xonotic/darkplaces.git] / sv_main.c
index ff02ac219b1b25106ae600fbea971d06e36dcbf8..c35976fe793ec3970db0d63cdddd23a09220bea7 100644 (file)
--- a/sv_main.c
+++ b/sv_main.c
@@ -31,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)"};
@@ -74,7 +73,7 @@ cvar_t sv_cullentities_trace_samples_extra = {0, "sv_cullentities_trace_samples_
 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"};
@@ -215,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"},
@@ -733,7 +733,19 @@ 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);
@@ -776,24 +788,15 @@ void SV_SendServerinfo (client_t *client)
 
                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);
-                       }
+                       sb.data = (void *) buf;
+                       sb.maxsize = sizeof(buf);
+                       i = 0;
+                       while(MakeDownloadPacket(sv.csqc_progname, sv.csqc_progdata, sv.csqc_progsize, sv.csqc_progcrc, i++, &sb, sv.protocol))
+                               SV_WriteDemoMessage(client, &sb, false);
                }
 
                //[515]: init stufftext string (it is sent before svc_serverinfo)
@@ -805,10 +808,12 @@ void SV_SendServerinfo (client_t *client)
                }
        }
 
-       if (sv_allowdownloads.integer)
+       //if (sv_allowdownloads.integer)
+       // always send the info that the server supports the protocol, even if downloads are forbidden
+       // only because of that, the CSQC exception can work
        {
                MSG_WriteByte (&client->netconnection->message, svc_stufftext);
-               MSG_WriteString (&client->netconnection->message, "cl_serverextension_download 1\n");
+               MSG_WriteString (&client->netconnection->message, "cl_serverextension_download 2\n");
        }
 
        // send at this time so it's guaranteed to get executed at the right time
@@ -954,12 +959,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)
@@ -1136,7 +1143,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
@@ -1192,6 +1199,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;
 }
 
@@ -1416,7 +1447,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);
@@ -1957,7 +1991,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);
@@ -2030,7 +2064,7 @@ static void SV_UpdateToReliableMessages (void)
        }
 
        for (j = 0, client = svs.clients;j < svs.maxclients;j++, client++)
-               if (client->netconnection && client->spawned)
+               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);
@@ -2087,13 +2121,58 @@ static void SV_StartDownload_f(void)
                host_client->download_started = true;
 }
 
+/*
+ * Compression extension negotiation:
+ *
+ * Server to client:
+ *   cl_serverextension_download 2
+ *
+ * Client to server:
+ *   download <filename> <list of zero or more suppported compressions in order of preference>
+ * e.g.
+ *   download maps/map1.bsp lzo deflate huffman
+ *
+ * Server to client:
+ *   cl_downloadbegin <compressed size> <filename> <compression method actually used>
+ * e.g.
+ *   cl_downloadbegin 123456 maps/map1.bsp deflate
+ *
+ * The server may choose not to compress the file by sending no compression name, like:
+ *   cl_downloadbegin 345678 maps/map1.bsp
+ *
+ * NOTE: the "download" command may only specify compression algorithms if
+ *       cl_serverextension_download is 2!
+ *       If cl_serverextension_download has a different value, the client must
+ *       assume this extension is not supported!
+ */
+
+static void Download_CheckExtensions(void)
+{
+       int i;
+       int argc = Cmd_Argc();
+
+       // first reset them all
+       host_client->download_deflate = false;
+       
+       for(i = 2; i < argc; ++i)
+       {
+               if(!strcmp(Cmd_Argv(i), "deflate"))
+               {
+                       host_client->download_deflate = true;
+                       break;
+               }
+       }
+}
+
 static void SV_Download_f(void)
 {
        const char *whichpack, *whichpack2, *extension;
+       qboolean is_csqc; // so we need to check only once
 
-       if (Cmd_Argc() != 2)
+       if (Cmd_Argc() < 2)
        {
-               SV_ClientPrintf("usage: download <filename>\n");
+               SV_ClientPrintf("usage: download <filename> {<extensions>}*\n");
+               SV_ClientPrintf("       supported extensions: deflate\n");
                return;
        }
 
@@ -2117,13 +2196,17 @@ static void SV_Download_f(void)
                host_client->download_started = false;
        }
 
-       if (!sv_allowdownloads.integer)
+       is_csqc = (sv.csqc_progname[0] && strcmp(Cmd_Argv(1), sv.csqc_progname) == 0);
+       
+       if (!sv_allowdownloads.integer && !is_csqc)
        {
                SV_ClientPrintf("Downloads are disabled on this server\n");
                Host_ClientCommands("\nstopdownload\n");
                return;
        }
 
+       Download_CheckExtensions();
+
        strlcpy(host_client->download_name, Cmd_Argv(1), sizeof(host_client->download_name));
        extension = FS_FileExtension(host_client->download_name);
 
@@ -2131,6 +2214,30 @@ static void SV_Download_f(void)
        if (developer.integer >= 100)
                Con_Printf("Download request for %s by %s\n", host_client->download_name, host_client->name);
 
+       if(is_csqc)
+       {
+               char extensions[MAX_QPATH]; // make sure this can hold all extensions
+               extensions[0] = '\0';
+               
+               if(host_client->download_deflate)
+                       strlcat(extensions, " deflate", sizeof(extensions));
+               
+               Con_DPrintf("Downloading %s to %s\n", host_client->download_name, host_client->name);
+
+               if(host_client->download_deflate)
+                       host_client->download_file = FS_FileFromData(sv.csqc_progdata_deflated, sv.csqc_progsize_deflated, true);
+               else
+                       host_client->download_file = FS_FileFromData(sv.csqc_progdata, sv.csqc_progsize, true);
+               
+               // no, no space is needed between %s and %s :P
+               Host_ClientCommands("\ncl_downloadbegin %i %s%s\n", (int)FS_FileSize(host_client->download_file), host_client->download_name, extensions);
+
+               host_client->download_expectedposition = 0;
+               host_client->download_started = false;
+               host_client->sendsignon = true; // make sure this message is sent
+               return;
+       }
+
        if (!FS_FileExists(host_client->download_name))
        {
                SV_ClientPrintf("Download rejected: server does not have the file \"%s\"\nYou may need to separately download or purchase the data archives for this game/mod to get this file\n", host_client->download_name);
@@ -2190,7 +2297,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);
@@ -2207,8 +2314,31 @@ static void SV_Download_f(void)
                return;
        }
 
+       if (FS_FileSize(host_client->download_file) < 0)
+       {
+               SV_ClientPrintf("Download rejected: file \"%s\" is not a regular file\n", host_client->download_name);
+               Host_ClientCommands("\nstopdownload\n");
+               FS_Close(host_client->download_file);
+               host_client->download_file = NULL;
+               return;
+       }
+
        Con_DPrintf("Downloading %s to %s\n", host_client->download_name, host_client->name);
 
+       /*
+        * we can only do this if we would actually deflate on the fly
+        * which we do not (yet)!
+       {
+               char extensions[MAX_QPATH]; // make sure this can hold all extensions
+               extensions[0] = '\0';
+               
+               if(host_client->download_deflate)
+                       strlcat(extensions, " deflate", sizeof(extensions));
+
+               // no, no space is needed between %s and %s :P
+               Host_ClientCommands("\ncl_downloadbegin %i %s%s\n", (int)FS_FileSize(host_client->download_file), host_client->download_name, extensions);
+       }
+       */
        Host_ClientCommands("\ncl_downloadbegin %i %s\n", (int)FS_FileSize(host_client->download_file), host_client->download_name);
 
        host_client->download_expectedposition = 0;
@@ -2487,6 +2617,51 @@ static void SV_CreateBaseline (void)
        }
 }
 
+/*
+================
+SV_Prepare_CSQC
+
+Load csprogs.dat and comperss it so it doesn't need to be
+reloaded on request.
+================
+*/
+void SV_Prepare_CSQC(void)
+{
+       fs_offset_t progsize;
+
+       if(sv.csqc_progdata)
+       {
+               Con_DPrintf("Unloading old CSQC data.\n");
+               Mem_Free(sv.csqc_progdata);
+               if(sv.csqc_progdata_deflated)
+                       Mem_Free(sv.csqc_progdata_deflated);
+       }
+
+       sv.csqc_progdata = NULL;
+       sv.csqc_progdata_deflated = NULL;
+       
+       Con_Print("Loading csprogs.dat\n");
+
+       sv.csqc_progname[0] = 0;
+       sv.csqc_progdata = FS_LoadFile(csqc_progname.string, sv_mempool, false, &progsize);
+
+       if(progsize > 0)
+       {
+               size_t deflated_size;
+               
+               sv.csqc_progsize = (int)progsize;
+               sv.csqc_progcrc = CRC_Block(sv.csqc_progdata, progsize);
+               strlcpy(sv.csqc_progname, csqc_progname.string, sizeof(sv.csqc_progname));
+               Con_Printf("server detected csqc progs file \"%s\" with size %i and crc %i\n", sv.csqc_progname, sv.csqc_progsize, sv.csqc_progcrc);
+
+               Con_Print("Compressing csprogs.dat\n");
+               //unsigned char *FS_Deflate(const unsigned char *data, size_t size, size_t *deflated_size, int level, mempool_t *mempool);
+               sv.csqc_progdata_deflated = FS_Deflate(sv.csqc_progdata, progsize, &deflated_size, -1, sv_mempool);
+               sv.csqc_progsize_deflated = (int)deflated_size;
+               Con_Printf("Deflated: %g%%\n", 100.0 - 100.0 * (deflated_size / (float)progsize));
+               Con_DPrintf("Uncompressed: %u\nCompressed:   %u\n", (unsigned)sv.csqc_progsize, (unsigned)sv.csqc_progsize_deflated);
+       }
+}
 
 /*
 ================
@@ -2850,6 +3025,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;
@@ -2862,6 +3040,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)
@@ -2917,9 +3105,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 );
 
@@ -2993,15 +3178,7 @@ static void SV_VM_Setup(void)
 
        PRVM_End;
 
-       // see if there is a csprogs.dat installed, and if so, set the csqc_progcrc accordingly, this will be sent to connecting clients to tell them to only load a matching csprogs.dat file
-       sv.csqc_progname[0] = 0;
-       sv.csqc_progcrc = FS_CRCFile(csqc_progname.string, &csprogsdatasize);
-       sv.csqc_progsize = csprogsdatasize;
-       if (sv.csqc_progsize > 0)
-       {
-               strlcpy(sv.csqc_progname, csqc_progname.string, sizeof(sv.csqc_progname));
-               Con_DPrintf("server detected csqc progs file \"%s\" with size %i and crc %i\n", sv.csqc_progname, sv.csqc_progsize, sv.csqc_progcrc);
-       }
+       SV_Prepare_CSQC();
 }
 
 void SV_VM_Begin(void)