// sv_main.c -- server main program
#include "quakedef.h"
+#include "libcurl.h"
void SV_VM_Init();
void SV_VM_Setup();
cvar_t sv_protocolname = {0, "sv_protocolname", "DP7", "selects network protocol to host for (values include QUAKE, QUAKEDP, NEHAHRAMOVIE, DP1 and up)"};
cvar_t sv_ratelimitlocalplayer = {0, "sv_ratelimitlocalplayer", "0", "whether to apply rate limiting to the local player in a listen server (only useful for testing)"};
cvar_t sv_maxrate = {CVAR_SAVE | CVAR_NOTIFY, "sv_maxrate", "10000", "upper limit on client rate cvar, should reflect your network connection quality"};
+cvar_t sv_allowdownloads = {0, "sv_allowdownloads", "1", "whether to allow clients to download files from the server (does not affect http downloads)"};
+cvar_t sv_allowdownloads_inarchive = {0, "sv_allowdownloads_inarchive", "0", "whether to allow downloads from archives (pak/pk3)"};
+cvar_t sv_allowdownloads_archive = {0, "sv_allowdownloads_archive", "0", "whether to allow downloads of archives (pak/pk3)"};
+
+extern cvar_t sv_random_seed;
static cvar_t sv_cullentities_pvs = {0, "sv_cullentities_pvs", "1", "fast but loose culling of hidden entities"}; // fast but loose
static 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"}; // tends to get false negatives, uses a timeout to keep entities visible a short time after becoming hidden
cvar_t sv_gameplayfix_grenadebouncedownslopes = {0, "sv_gameplayfix_grenadebouncedownslopes", "1", "prevents MOVETYPE_BOUNCE (grenades) from getting stuck when fired down a downward sloping surface"};
cvar_t sv_gameplayfix_noairborncorpse = {0, "sv_gameplayfix_noairborncorpse", "1", "causes entities (corpses) sitting ontop of moving entities (players) to fall when the moving entity (player) is no longer supporting them"};
-cvar_t sv_gameplayfix_stepdown = {0, "sv_gameplayfix_stepdown", "1", "attempts to step down stairs, not just up them (prevents the familiar thud..thud..thud.. when running down stairs and slopes)"};
+cvar_t sv_gameplayfix_stepdown = {0, "sv_gameplayfix_stepdown", "0", "attempts to step down stairs, not just up them (prevents the familiar thud..thud..thud.. when running down stairs and slopes)"};
cvar_t sv_gameplayfix_stepwhilejumping = {0, "sv_gameplayfix_stepwhilejumping", "1", "applies step-up onto a ledge even while airborn, useful if you would otherwise just-miss the floor when running across small areas with gaps (for instance running across the moving platforms in dm2, or jumping to the megahealth and red armor in dm2 rather than using the bridge)"};
cvar_t sv_gameplayfix_swiminbmodels = {0, "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_setmodelrealbox = {0, "sv_gameplayfix_setmodelrealbox", "1", "fixes a bug in Quake that made setmodel always set the entity box to ('-16 -16 -16', '16 16 16') rather than properly checking the model box, breaks some poorly coded mods"};
cvar_t sv_gameplayfix_blowupfallenzombies = {0, "sv_gameplayfix_blowupfallenzombies", "1", "causes findradius to detect SOLID_NOT entities such as zombies and corpses on the floor, allowing splash damage to apply to them"};
cvar_t sv_gameplayfix_findradiusdistancetobox = {0, "sv_gameplayfix_findradiusdistancetobox", "1", "causes findradius to check the distance to the corner of a box rather than the center of the box, makes findradius detect bmodels such as very large doors that would otherwise be unaffected by splash damage"};
+cvar_t sv_gameplayfix_qwplayerphysics = {0, "sv_gameplayfix_qwplayerphysics", "1", "changes water jumping to make it easier to get out of water, and prevents friction on landing when bunnyhopping"};
+cvar_t sv_gameplayfix_upwardvelocityclearsongroundflag = {0, "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_droptofloorstartsolid = {0, "sv_gameplayfix_droptofloorstartsolid", "1", "prevents items and monsters that start in a solid area from falling out of the level (makes droptofloor treat trace_startsolid as an acceptable outcome)"};
cvar_t sv_progs = {0, "sv_progs", "progs.dat", "selects which quakec progs.dat file to run" };
+// TODO: move these cvars here
+extern cvar_t sv_clmovement_enable;
+extern cvar_t sv_clmovement_minping;
+extern cvar_t sv_clmovement_minping_disabletime;
+extern cvar_t sv_clmovement_waitforinput;
+
server_t sv;
server_static_t svs;
extern void SV_Phys_Init (void);
extern void SV_World_Init (void);
static void SV_SaveEntFile_f(void);
+static void SV_StartDownload_f(void);
+static void SV_Download_f(void);
/*
===============
*/
void SV_Init (void)
{
+ // init the csqc progs cvars, since they are updated/used by the server code
+ // TODO: fix this since this is a quick hack to make some of [515]'s broken code run ;) [9/13/2006 Black]
+ extern cvar_t csqc_progname; //[515]: csqc crc check and right csprogs name according to progs.dat
+ extern cvar_t csqc_progcrc;
+ Cvar_RegisterVariable (&csqc_progname);
+ Cvar_RegisterVariable (&csqc_progcrc);
+
Cmd_AddCommand("sv_saveentfile", SV_SaveEntFile_f, "save map entities to .ent file (to allow external editing)");
+ Cmd_AddCommand_WithClientCommand("sv_startdownload", NULL, SV_StartDownload_f, "begins sending a file to the client (network protocol use only)");
+ Cmd_AddCommand_WithClientCommand("download", NULL, SV_Download_f, "downloads a specified file from the server");
Cvar_RegisterVariable (&sv_maxvelocity);
Cvar_RegisterVariable (&sv_gravity);
Cvar_RegisterVariable (&sv_friction);
+ Cvar_RegisterVariable (&sv_waterfriction);
Cvar_RegisterVariable (&sv_edgefriction);
Cvar_RegisterVariable (&sv_stopspeed);
Cvar_RegisterVariable (&sv_maxspeed);
Cvar_RegisterVariable (&sv_maxairspeed);
Cvar_RegisterVariable (&sv_accelerate);
+ Cvar_RegisterVariable (&sv_airaccelerate);
+ Cvar_RegisterVariable (&sv_wateraccelerate);
+ Cvar_RegisterVariable (&sv_clmovement_enable);
+ Cvar_RegisterVariable (&sv_clmovement_minping);
+ Cvar_RegisterVariable (&sv_clmovement_minping_disabletime);
+ Cvar_RegisterVariable (&sv_clmovement_waitforinput);
Cvar_RegisterVariable (&sv_idealpitchscale);
Cvar_RegisterVariable (&sv_aim);
Cvar_RegisterVariable (&sv_nostep);
Cvar_RegisterVariable (&sv_gameplayfix_setmodelrealbox);
Cvar_RegisterVariable (&sv_gameplayfix_blowupfallenzombies);
Cvar_RegisterVariable (&sv_gameplayfix_findradiusdistancetobox);
+ Cvar_RegisterVariable (&sv_gameplayfix_qwplayerphysics);
+ Cvar_RegisterVariable (&sv_gameplayfix_upwardvelocityclearsongroundflag);
+ Cvar_RegisterVariable (&sv_gameplayfix_droptofloorstartsolid);
Cvar_RegisterVariable (&sv_protocolname);
Cvar_RegisterVariable (&sv_ratelimitlocalplayer);
Cvar_RegisterVariable (&sv_maxrate);
+ Cvar_RegisterVariable (&sv_allowdownloads);
+ Cvar_RegisterVariable (&sv_allowdownloads_inarchive);
+ Cvar_RegisterVariable (&sv_allowdownloads_archive);
Cvar_RegisterVariable (&sv_progs);
SV_VM_Init();
*/
void SV_StartParticle (vec3_t org, vec3_t dir, int color, int count)
{
- int i, v;
+ int i;
if (sv.datagram.cursize > MAX_PACKETFRAGMENT-18)
return;
MSG_WriteCoord (&sv.datagram, org[1], sv.protocol);
MSG_WriteCoord (&sv.datagram, org[2], sv.protocol);
for (i=0 ; i<3 ; i++)
- {
- v = dir[i]*16;
- if (v > 127)
- v = 127;
- else if (v < -128)
- v = -128;
- MSG_WriteChar (&sv.datagram, v);
- }
+ MSG_WriteChar (&sv.datagram, (int)bound(-128, dir[i]*16, 127));
MSG_WriteByte (&sv.datagram, count);
MSG_WriteByte (&sv.datagram, color);
}
if (field_mask & SND_VOLUME)
MSG_WriteByte (&sv.datagram, volume);
if (field_mask & SND_ATTENUATION)
- MSG_WriteByte (&sv.datagram, attenuation*64);
+ MSG_WriteByte (&sv.datagram, (int)(attenuation*64));
if (field_mask & SND_LARGEENTITY)
{
MSG_WriteShort (&sv.datagram, ent);
==============================================================================
*/
-static const char *SV_InitCmd; //[515]: svprogs able to send cmd to client on connect
-extern qboolean csqc_loaded;
/*
================
SV_SendServerinfo
SZ_Clear (&client->netconnection->message);
MSG_WriteByte (&client->netconnection->message, svc_print);
- dpsnprintf (message, sizeof (message), "\002\nServer: %s build %s (progs %i crc)", gamename, buildstring, prog->filecrc);
+ dpsnprintf (message, sizeof (message), "\nServer: %s build %s (progs %i crc)", gamename, buildstring, prog->filecrc);
MSG_WriteString (&client->netconnection->message,message);
- // FIXME: LordHavoc: this does not work on dedicated servers, needs fixing.
-//[515]: init csprogs according to version of svprogs, check the crc, etc.
- if(csqc_loaded && (cls.state == ca_dedicated || PRVM_NUM_FOR_EDICT(client->edict) != 1))
+ //[515]: init csprogs according to version of svprogs, check the crc, etc.
+ if (sv.csqc_progcrc >= 0)
{
+ prvm_eval_t *val;
+ Con_DPrintf("sending csqc info to client (\"%s\" with crc %i)\n", sv.csqc_progname, sv.csqc_progcrc);
+ //[515]: init stufftext string (it is sent before svc_serverinfo)
+ val = PRVM_GETGLOBALFIELDVALUE(PRVM_ED_FindGlobalOffset("SV_InitCmd"));
MSG_WriteByte (&client->netconnection->message, svc_stufftext);
- if(SV_InitCmd)
- MSG_WriteString (&client->netconnection->message, va("csqc_progcrc %i;%s\n", csqc_progcrc.integer, SV_InitCmd));
- else
- MSG_WriteString (&client->netconnection->message, va("csqc_progcrc %i\n", csqc_progcrc.integer));
+ MSG_WriteString (&client->netconnection->message, va("csqc_progname %s\n", sv.csqc_progname));
+ MSG_WriteByte (&client->netconnection->message, svc_stufftext);
+ MSG_WriteString (&client->netconnection->message, va("csqc_progcrc %i\n", sv.csqc_progcrc));
+ if (val)
+ {
+ MSG_WriteByte (&client->netconnection->message, svc_stufftext);
+ MSG_WriteString (&client->netconnection->message, va("%s\n", PRVM_GetString(val->string)));
+ }
+ }
+
+ if (sv_allowdownloads.integer)
+ {
+ MSG_WriteByte (&client->netconnection->message, svc_stufftext);
+ MSG_WriteString (&client->netconnection->message, "cl_serverextension_download 1");
}
MSG_WriteByte (&client->netconnection->message, svc_serverinfo);
// send music
MSG_WriteByte (&client->netconnection->message, svc_cdtrack);
- MSG_WriteByte (&client->netconnection->message, prog->edicts->fields.server->sounds);
- MSG_WriteByte (&client->netconnection->message, prog->edicts->fields.server->sounds);
+ MSG_WriteByte (&client->netconnection->message, (int)prog->edicts->fields.server->sounds);
+ MSG_WriteByte (&client->netconnection->message, (int)prog->edicts->fields.server->sounds);
// set view
MSG_WriteByte (&client->netconnection->message, svc_setview);
MSG_WriteByte (&client->netconnection->message, svc_signonnum);
MSG_WriteByte (&client->netconnection->message, 1);
+ {
+ client_t *save;
+ save = host_client;
+ host_client = client;
+ Curl_SendRequirements();
+ host_client = save;
+ }
+
client->spawned = false; // need prespawn, spawn, etc
}
Con_DPrintf("Client %s connected\n", client->netconnection ? client->netconnection->address : "botclient");
- strcpy(client->name, "unconnected");
- strcpy(client->old_name, "unconnected");
+ strlcpy(client->name, "unconnected", sizeof(client->name));
+ strlcpy(client->old_name, "unconnected", sizeof(client->old_name));
client->spawned = false;
client->edict = PRVM_EDICT_NUM(clientnum+1);
if (client->netconnection)
if (effects & 32)
{
effects &= ~32;
- light[0] = 0.2;
- light[1] = 1;
- light[2] = 0.2;
+ light[0] = (int)(0.2*256);
+ light[1] = (int)(1.0*256);
+ light[2] = (int)(0.2*256);
light[3] = 200;
lightpflags |= PFLAGS_FULLDYNAMIC;
}
val = PRVM_GETEDICTFIELDVALUE(ent, eval_colormod);
if (val->vector[0] || val->vector[1] || val->vector[2])
{
- i = val->vector[0] * 32.0f;cs->colormod[0] = bound(0, i, 255);
- i = val->vector[1] * 32.0f;cs->colormod[1] = bound(0, i, 255);
- i = val->vector[2] * 32.0f;cs->colormod[2] = bound(0, i, 255);
+ i = (int)(val->vector[0] * 32.0f);cs->colormod[0] = bound(0, i, 255);
+ i = (int)(val->vector[1] * 32.0f);cs->colormod[1] = bound(0, i, 255);
+ i = (int)(val->vector[2] * 32.0f);cs->colormod[2] = bound(0, i, 255);
}
cs->modelindex = modelindex;
}
else
{
- VectorCopy(cs->origin, cullmins);
- VectorCopy(cs->origin, cullmaxs);
+ // if there is no model (or it could not be loaded), use the physics box
+ VectorAdd(cs->origin, ent->fields.server->mins, cullmins);
+ VectorAdd(cs->origin, ent->fields.server->maxs, cullmaxs);
}
if (specialvisibilityradius)
{
int items;
prvm_eval_t *val;
vec3_t punchvector;
- unsigned char viewzoom;
+ int viewzoom;
const char *s;
//
{
other = PRVM_PROG_TO_EDICT(ent->fields.server->dmg_inflictor);
MSG_WriteByte (msg, svc_damage);
- MSG_WriteByte (msg, ent->fields.server->dmg_save);
- MSG_WriteByte (msg, ent->fields.server->dmg_take);
+ MSG_WriteByte (msg, (int)ent->fields.server->dmg_save);
+ MSG_WriteByte (msg, (int)ent->fields.server->dmg_take);
for (i=0 ; i<3 ; i++)
MSG_WriteCoord (msg, other->fields.server->origin[i] + 0.5*(other->fields.server->mins[i] + other->fields.server->maxs[i]), sv.protocol);
viewzoom = 255;
if ((val = PRVM_GETEDICTFIELDVALUE(ent, eval_viewzoom)))
- viewzoom = val->_float * 255.0f;
+ viewzoom = (int)(val->_float * 255.0f);
if (viewzoom == 0)
viewzoom = 255;
}
memset(stats, 0, sizeof(int[MAX_CL_STATS]));
- stats[STAT_VIEWHEIGHT] = ent->fields.server->view_ofs[2];
+ stats[STAT_VIEWHEIGHT] = (int)ent->fields.server->view_ofs[2];
stats[STAT_ITEMS] = items;
- stats[STAT_WEAPONFRAME] = ent->fields.server->weaponframe;
- stats[STAT_ARMOR] = ent->fields.server->armorvalue;
+ stats[STAT_WEAPONFRAME] = (int)ent->fields.server->weaponframe;
+ stats[STAT_ARMOR] = (int)ent->fields.server->armorvalue;
stats[STAT_WEAPON] = client->weaponmodelindex;
- stats[STAT_HEALTH] = ent->fields.server->health;
- stats[STAT_AMMO] = ent->fields.server->currentammo;
- stats[STAT_SHELLS] = ent->fields.server->ammo_shells;
- stats[STAT_NAILS] = ent->fields.server->ammo_nails;
- stats[STAT_ROCKETS] = ent->fields.server->ammo_rockets;
- stats[STAT_CELLS] = ent->fields.server->ammo_cells;
- stats[STAT_ACTIVEWEAPON] = ent->fields.server->weapon;
+ stats[STAT_HEALTH] = (int)ent->fields.server->health;
+ stats[STAT_AMMO] = (int)ent->fields.server->currentammo;
+ stats[STAT_SHELLS] = (int)ent->fields.server->ammo_shells;
+ stats[STAT_NAILS] = (int)ent->fields.server->ammo_nails;
+ stats[STAT_ROCKETS] = (int)ent->fields.server->ammo_rockets;
+ stats[STAT_CELLS] = (int)ent->fields.server->ammo_cells;
+ stats[STAT_ACTIVEWEAPON] = (int)ent->fields.server->weapon;
stats[STAT_VIEWZOOM] = viewzoom;
// the QC bumps these itself by sending svc_'s, so we have to keep them
// zero or they'll be corrected by the engine
MSG_WriteChar (msg, stats[STAT_VIEWHEIGHT]);
if (bits & SU_IDEALPITCH)
- MSG_WriteChar (msg, ent->fields.server->idealpitch);
+ MSG_WriteChar (msg, (int)ent->fields.server->idealpitch);
for (i=0 ; i<3 ; i++)
{
if (bits & (SU_PUNCH1<<i))
{
if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE)
- MSG_WriteChar(msg, ent->fields.server->punchangle[i]);
+ MSG_WriteChar(msg, (int)ent->fields.server->punchangle[i]);
else
MSG_WriteAngle16i(msg, ent->fields.server->punchangle[i]);
}
if (bits & (SU_VELOCITY1<<i))
{
if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
- MSG_WriteChar(msg, ent->fields.server->velocity[i] * (1.0f / 16.0f));
+ MSG_WriteChar(msg, (int)(ent->fields.server->velocity[i] * (1.0f / 16.0f)));
else
MSG_WriteCoord32f(msg, ent->fields.server->velocity[i]);
}
MSG_WriteShort (msg, stats[STAT_CELLS]);
MSG_WriteShort (msg, stats[STAT_ACTIVEWEAPON]);
if (bits & SU_VIEWZOOM)
- MSG_WriteShort (msg, min(stats[STAT_VIEWZOOM], 65535));
+ MSG_WriteShort (msg, bound(0, stats[STAT_VIEWZOOM], 65535));
}
else if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
{
if (bits & SU_VIEWZOOM)
{
if (sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
- MSG_WriteByte (msg, min(stats[STAT_VIEWZOOM], 255));
+ MSG_WriteByte (msg, bound(0, stats[STAT_VIEWZOOM], 255));
else
- MSG_WriteShort (msg, min(stats[STAT_VIEWZOOM], 65535));
+ MSG_WriteShort (msg, bound(0, stats[STAT_VIEWZOOM], 65535));
}
}
}
static unsigned char sv_sendclientdatagram_buf[NET_MAXMESSAGE]; // FIXME?
void SV_SendClientDatagram (client_t *client)
{
- int rate, maxrate, maxsize, maxsize2;
+ int rate, maxrate, maxsize, maxsize2, downloadsize;
sizebuf_t msg;
int stats[MAX_CL_STATS];
else
{
// PROTOCOL_DARKPLACES5 and later support packet size limiting of updates
- maxrate = bound(NET_MINRATE, sv_maxrate.integer, NET_MAXRATE);
+ maxrate = max(NET_MINRATE, sv_maxrate.integer);
if (sv_maxrate.integer != maxrate)
Cvar_SetValueQuick(&sv_maxrate, maxrate);
// this rate limiting does not understand sys_ticrate 0
// (but no one should be running that on a server!)
rate = bound(NET_MINRATE, client->rate, maxrate);
- rate = (int)(client->rate * sys_ticrate.value);
+ rate = (int)(rate * sys_ticrate.value);
maxsize = bound(100, rate, 1400);
maxsize2 = 1400;
}
+ // while downloading, limit entity updates to half the packet
+ // (any leftover space will be used for downloading)
+ if (host_client->download_file)
+ maxsize /= 2;
+
msg.data = sv_sendclientdatagram_buf;
msg.maxsize = maxsize;
msg.cursize = 0;
{
// the player isn't totally in the game yet
// send small keepalive messages if too much time has passed
+ msg.maxsize = maxsize2;
client->keepalivetime = realtime + 5;
MSG_WriteChar (&msg, svc_nop);
}
+ msg.maxsize = maxsize2;
+
+ // if a download is active, see if there is room to fit some download data
+ // in this packet
+ downloadsize = maxsize * 2 - msg.cursize - 7;
+ if (host_client->download_file && host_client->download_started && downloadsize > 0)
+ {
+ fs_offset_t downloadstart;
+ unsigned char data[1400];
+ downloadstart = FS_Tell(host_client->download_file);
+ downloadsize = min(downloadsize, (int)sizeof(data));
+ downloadsize = FS_Read(host_client->download_file, data, downloadsize);
+ // note this sends empty messages if at the end of the file, which is
+ // necessary to keep the packet loss logic working
+ // (the last blocks may be lost and need to be re-sent, and that will
+ // only occur if the client acks the empty end messages, revealing
+ // a gap in the download progress, causing the last blocks to be
+ // sent again)
+ MSG_WriteChar (&msg, svc_downloaddata);
+ MSG_WriteLong (&msg, downloadstart);
+ MSG_WriteShort (&msg, downloadsize);
+ if (downloadsize > 0)
+ SZ_Write (&msg, data, downloadsize);
+ }
+
// send the datagram
NetConn_SendUnreliableMessage (client->netconnection, &msg, sv.protocol);
}
{
if (host_client->spawned)
SV_BroadcastPrintf("%s changed name to %s\n", host_client->old_name, host_client->name);
- strcpy(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);
MSG_WriteByte (&sv.reliable_datagram, i);
SV_CleanupEnts();
}
+void SV_StartDownload_f(void)
+{
+ if (host_client->download_file)
+ host_client->download_started = true;
+}
+
+void SV_Download_f(void)
+{
+ const char *whichpack, *whichpack2, *extension;
+
+ if (Cmd_Argc() != 2)
+ {
+ SV_ClientPrintf("usage: download <filename>\n");
+ return;
+ }
+
+ if (FS_CheckNastyPath(Cmd_Argv(1), false))
+ {
+ SV_ClientPrintf("Download rejected: nasty filename \"%s\"\n", Cmd_Argv(1));
+ return;
+ }
+
+ if (host_client->download_file)
+ {
+ // at this point we'll assume the previous download should be aborted
+ Con_DPrintf("Download of %s aborted by %s starting a new download\n", host_client->download_name, host_client->name);
+ Host_ClientCommands("\nstopdownload\n");
+
+ // close the file and reset variables
+ FS_Close(host_client->download_file);
+ host_client->download_file = NULL;
+ host_client->download_name[0] = 0;
+ host_client->download_expectedposition = 0;
+ host_client->download_started = false;
+ }
+
+ if (!sv_allowdownloads.integer)
+ {
+ SV_ClientPrintf("Downloads are disabled on this server\n");
+ Host_ClientCommands("\nstopdownload\n");
+ return;
+ }
+
+ strlcpy(host_client->download_name, Cmd_Argv(1), sizeof(host_client->download_name));
+
+ // host_client is asking to download a specified file
+ if (developer.integer >= 100)
+ Con_Printf("Download request for %s by %s\n", host_client->download_name, host_client->name);
+
+ 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);
+ Host_ClientCommands("\nstopdownload\n");
+ return;
+ }
+
+ // check if the user is trying to download part of registered Quake(r)
+ whichpack = FS_WhichPack(host_client->download_name);
+ whichpack2 = FS_WhichPack("gfx/pop.lmp");
+ if ((whichpack && whichpack2 && !strcasecmp(whichpack, whichpack2)) || FS_IsRegisteredQuakePack(host_client->download_name))
+ {
+ SV_ClientPrintf("Download rejected: file \"%s\" is part of registered Quake(r)\nYou must purchase Quake(r) from id Software or a retailer to get this file\nPlease go to http://www.idsoftware.com/games/quake/quake/index.php?game_section=buy\n", host_client->download_name);
+ Host_ClientCommands("\nstopdownload\n");
+ return;
+ }
+
+ // check if the server has forbidden archive downloads entirely
+ if (!sv_allowdownloads_inarchive.integer)
+ {
+ whichpack = FS_WhichPack(host_client->download_name);
+ if (whichpack)
+ {
+ SV_ClientPrintf("Download rejected: file \"%s\" is in an archive (\"%s\")\nYou must separately download or purchase the data archives for this game/mod to get this file\n", host_client->download_name, whichpack);
+ Host_ClientCommands("\nstopdownload\n");
+ return;
+ }
+ }
+
+ if (!sv_allowdownloads_archive.integer)
+ {
+ extension = FS_FileExtension(host_client->download_name);
+ if (!strcasecmp(extension, "pak") || !strcasecmp(extension, "pk3"))
+ {
+ 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);
+ Host_ClientCommands("\nstopdownload\n");
+ return;
+ }
+ }
+
+ host_client->download_file = FS_Open(host_client->download_name, "rb", true, false);
+ if (!host_client->download_file)
+ {
+ SV_ClientPrintf("Download rejected: server could not open the file \"%s\"\n", host_client->download_name);
+ Host_ClientCommands("\nstopdownload\n");
+ return;
+ }
+
+ if (FS_FileSize(host_client->download_file) > 1<<30)
+ {
+ SV_ClientPrintf("Download rejected: file \"%s\" is very large\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);
+
+ Host_ClientCommands("\ncl_downloadbegin %i %s\n", (int)FS_FileSize(host_client->download_file), host_client->download_name);
+
+ host_client->download_expectedposition = 0;
+ host_client->download_started = false;
+
+ // the rest of the download process is handled in SV_SendClientDatagram
+ // and other code dealing with svc_downloaddata and clc_ackdownloaddata
+ //
+ // no svc_downloaddata messages will be sent until sv_startdownload is
+ // sent by the client
+}
/*
==============================================================================
// create entity baseline
VectorCopy (svent->fields.server->origin, svent->priv.server->baseline.origin);
VectorCopy (svent->fields.server->angles, svent->priv.server->baseline.angles);
- svent->priv.server->baseline.frame = svent->fields.server->frame;
- svent->priv.server->baseline.skin = svent->fields.server->skin;
+ svent->priv.server->baseline.frame = (int)svent->fields.server->frame;
+ svent->priv.server->baseline.skin = (int)svent->fields.server->skin;
if (entnum > 0 && entnum <= svs.maxclients)
{
svent->priv.server->baseline.colormap = entnum;
else
{
svent->priv.server->baseline.colormap = 0;
- svent->priv.server->baseline.modelindex = svent->fields.server->modelindex;
+ svent->priv.server->baseline.modelindex = (int)svent->fields.server->modelindex;
}
large = false;
{
int i, j;
- svs.serverflags = prog->globals.server->serverflags;
+ svs.serverflags = (int)prog->globals.server->serverflags;
for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
{
svs.changelevel_issued = false; // now safe to issue another
+ // make the map a required file for clients
+ Curl_ClearRequirements();
+ Curl_RequireFile(modelname);
+
//
// tell all connected clients that we are going to a new level
//
// level's data which is no longer valiud
cls.signon = 0;
+ if(*sv_random_seed.string)
+ {
+ srand(sv_random_seed.integer);
+ Con_Printf("NOTE: random seed is %d; use for debugging/benchmarking only!\nUnset sv_random_seed to get real random numbers again.\n", sv_random_seed.integer);
+ }
+
SV_VM_Setup();
sv.active = true;
worldmodel->used = true;
strlcpy (sv.name, server, sizeof (sv.name));
- strcpy(sv.modelname, modelname);
+ strlcpy(sv.modelname, modelname, sizeof(sv.modelname));
sv.worldmodel = worldmodel;
sv.models[1] = sv.worldmodel;
ent->fields.server->modelindex = 1; // world model
ent->fields.server->solid = SOLID_BSP;
ent->fields.server->movetype = MOVETYPE_PUSH;
+ VectorCopy(sv.worldmodel->normalmins, ent->fields.server->mins);
+ VectorCopy(sv.worldmodel->normalmaxs, ent->fields.server->maxs);
+ VectorCopy(sv.worldmodel->normalmins, ent->fields.server->absmin);
+ VectorCopy(sv.worldmodel->normalmaxs, ent->fields.server->absmax);
if (coop.value)
prog->globals.server->coop = coop.integer;
// run two frames to allow everything to settle
for (i = 0;i < 2;i++)
{
- sv.frametime = host_frametime = 0.1;
+ sv.frametime = 0.1;
SV_Physics ();
}
int eval_Version;
int eval_customizeentityforclient;
int eval_dphitcontentsmask;
+// DRESK - Support for Entity Contents Transition Event
+int eval_contentstransition;
int gval_trace_dpstartcontents;
int gval_trace_dphitcontents;
//KrimZon - SERVER COMMANDS IN QUAKEC
mfunction_t *SV_ParseClientCommandQC;
-ddef_t *PRVM_ED_FindGlobal(const char *name);
-
void SV_VM_FindEdictFieldOffsets(void)
{
eval_gravity = PRVM_ED_FindFieldOffset("gravity");
eval_Version = PRVM_ED_FindFieldOffset("Version");
eval_customizeentityforclient = PRVM_ED_FindFieldOffset("customizeentityforclient");
eval_dphitcontentsmask = PRVM_ED_FindFieldOffset("dphitcontentsmask");
+ // DRESK - Support for Entity Contents Transition Event
+ eval_contentstransition = PRVM_ED_FindFieldOffset("contentstransition");
// LordHavoc: allowing QuakeC to override the player movement code
SV_PlayerPhysicsQC = PRVM_ED_FindFunction ("SV_PlayerPhysics");
EndFrameQC = PRVM_ED_FindFunction ("EndFrame");
//KrimZon - SERVER COMMANDS IN QUAKEC
SV_ParseClientCommandQC = PRVM_ED_FindFunction ("SV_ParseClientCommand");
-
- //[515]: init stufftext string (it is sent before svc_serverinfo)
- if(PRVM_ED_FindGlobal("SV_InitCmd") && PRVM_ED_FindGlobal("SV_InitCmd")->type & ev_string)
- SV_InitCmd = PRVM_G_STRING(PRVM_ED_FindGlobal("SV_InitCmd")->ofs);
- else
- SV_InitCmd = NULL;
-
gval_trace_dpstartcontents = PRVM_ED_FindGlobalOffset("trace_dpstartcontents");
gval_trace_dphitcontents = PRVM_ED_FindGlobalOffset("trace_dphitcontents");
gval_trace_dphitq3surfaceflags = PRVM_ED_FindGlobalOffset("trace_dphitq3surfaceflags");
{ev_string, "playerskin"},
{ev_function, "SendEntity"},
{ev_function, "customizeentityforclient"},
+ // DRESK - Support for Entity Contents Transition Event
+ {ev_function, "contentstransition"},
};
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;
+ unsigned char *csprogsdata;
+ fs_offset_t csprogsdatasize;
PRVM_Begin;
PRVM_InitProg( PRVM_SERVERPROG );
// allocate the mempools
+ // TODO: move the magic numbers/constants into #defines [9/13/2006 Black]
prog->progs_mempool = Mem_AllocPool("Server Progs", 0, NULL);
prog->builtins = vm_sv_builtins;
prog->numbuiltins = vm_sv_numbuiltins;
EntityFrameCSQC_ClearVersions();//[515]: csqc
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_progcrc = -1;
+ sv.csqc_progname[0] = 0;
+ csprogsdata = FS_LoadFile(csqc_progname.string, tempmempool, true, &csprogsdatasize);
+ if (csprogsdata)
+ {
+ strlcpy(sv.csqc_progname, csqc_progname.string, sizeof(sv.csqc_progname));
+ sv.csqc_progcrc = CRC_Block(csprogsdata, csprogsdatasize);
+ Mem_Free(csprogsdata);
+ Con_DPrintf("server detected csqc progs file \"%s\" with crc %i\n", sv.csqc_progname, sv.csqc_progcrc);
+ }
}
void SV_VM_Begin(void)