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 = (unsigned char *) 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)
}
}
- 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
// 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
void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *msg, int maxsize)
{
+ qboolean need_empty = false;
int i, numsendstates;
entity_state_t *s;
prvm_edict_t *camera;
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);
if(client->entitydatabase5)
- EntityFrameCSQC_WriteFrame(msg, maxsize, numsendstates, sv.writeentitiestoclient_sendstates, client->entitydatabase5->latestframenum + 1);
+ need_empty = 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);
+ EntityFrame5_WriteFrame(msg, maxsize, client->entitydatabase5, numsendstates, sv.writeentitiestoclient_sendstates, client - svs.clients + 1, client->movesequence, need_empty);
else if (client->entitydatabase4)
{
EntityFrame4_WriteFrame(msg, maxsize, client->entitydatabase4, numsendstates, sv.writeentitiestoclient_sendstates);
stats[STAT_CELLS] = (int)ent->fields.server->ammo_cells;
stats[STAT_ACTIVEWEAPON] = (int)ent->fields.server->weapon;
stats[STAT_VIEWZOOM] = viewzoom;
- stats[STAT_TOTALSECRETS] = prog->globals.server->total_secrets;
- stats[STAT_TOTALMONSTERS] = prog->globals.server->total_monsters;
+ stats[STAT_TOTALSECRETS] = (int)prog->globals.server->total_secrets;
+ stats[STAT_TOTALMONSTERS] = (int)prog->globals.server->total_monsters;
// the QC bumps these itself by sending svc_'s, so we have to keep them
// zero or they'll be corrected by the engine
//stats[STAT_SECRETS] = prog->globals.server->found_secrets;
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);
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;
}
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);
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);
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;
}
}
+/*
+================
+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);
+ }
+}
/*
================
static void SV_VM_Setup(void)
{
- extern cvar_t csqc_progname; //[515]: csqc crc check and right csprogs name according to progs.dat
- size_t csprogsdatasize;
PRVM_Begin;
PRVM_InitProg( PRVM_SERVERPROG );
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)