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;
World_PrintAreaStats(&sv.world, "server");
}
-static qbool SV_CanSave(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)))
- {
- Con_Print("Can't savegame with a dead player\n");
- return false;
- }
-
- if(host.hook.CL_Intermission && host.hook.CL_Intermission())
- {
- Con_Print("Can't save in intermission.\n");
- return false;
- }
- }
- 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;
-
-}
-
static void SV_ServerOptions (void)
{
int i;
Cvar_RegisterVariable (&skill);
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);
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);
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)
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);
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);
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)
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");
{
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;
// 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;
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))
{
//
// 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
// 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;
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);
}
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();
//
memset(&sv, 0, sizeof(sv));
memset(svs.clients, 0, svs.maxclients*sizeof(client_t));
-
- cl.islocalgame = false;
+end:
+ SV_UnlockThreadMutex();
}
/////////////////////////////////////////////////////
// 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);
}
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;
for (i=0 ; i<prog->num_edicts ; i++)
{
ent = PRVM_EDICT_NUM(i);
- if (ent->priv.server->free)
+ if (ent->free)
continue;
active++;
if (PRVM_serveredictfloat(ent, solid))
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]
// 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;
}
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)
}
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;
}